Adium

Changeset 24151

Show
Ignore:
Timestamp:
07/01/2008 05:09:23 PM (5 months ago)
Author:
evands
Message:

Backported [23524] to adium-1.2:

  • Avoid the possibility of modifying the set of list object observers being enumerated by copying/autoreleasing it before enumeration.
  • Keep track of observers removed while observers are being notified, and avoid messaging them subsequently. Fixes #9425, where I wrote:

The problem occurs when a list object observer is unregistered and deallocates in response to the status update being posted. In this specific case, AIContactMenu is a list object observer, responds to an update by informing its delegate, which happens to be an AIAccountSelectorView. AIAccountSelectorView destroys and then recreates its AIAccountMenu... and then the enumeration of the observers continues, eventually trying to message the old AIAccountMenu, which has now been deallocated. *boom*.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/adium-1.2/Source/AIContactController.h

    r21622 r24151  
    4949        //Status and Attribute updates 
    5050    NSMutableSet                        *contactObservers; 
     51        NSMutableSet                    *removedContactObservers; 
    5152    NSTimer                                     *delayedUpdateTimer; 
    5253    int                                         delayedStatusChanges; 
     
    5758        int                                             delayedUpdateRequests; 
    5859        BOOL                                    updatesAreDelayed; 
    59          
     60        BOOL                                    informingObservers; 
     61 
    6062        //Sorting 
    6163    NSMutableArray                      *sortControllerArray; 
  • branches/adium-1.2/Source/AIContactController.m

    r23243 r24151  
    14461446#endif   
    14471447    [contactObservers removeObject:[NSValue valueWithNonretainedObject:inObserver]]; 
     1448         
     1449        /* If we're in the middle of informing observers, we need to note this now-removed observer 
     1450         * so that we don't attempt to message it during this iteration. 
     1451         */ 
     1452        if (informingObservers) { 
     1453                if (!removedContactObservers) removedContactObservers = [[NSMutableSet alloc] init]; 
     1454                [removedContactObservers addObject:[NSValue valueWithNonretainedObject:inObserver]]; 
     1455        }        
    14481456} 
    14491457 
     
    15191527        NSValue                 *observerValue; 
    15201528         
     1529        informingObservers = YES; 
     1530 
    15211531        //Let our observers know 
    1522         enumerator = [contactObservers objectEnumerator]; 
     1532        enumerator = [[[contactObservers copy] autorelease] objectEnumerator]; 
    15231533        while ((observerValue = [enumerator nextObject])) { 
    1524                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    15251534                id <AIListObjectObserver>       observer; 
    15261535                NSSet                                           *newKeys; 
     1536 
     1537                /* Skip any observer which has been removed while we were iterating over observers, 
     1538                 * as we don't retain observers and therefore risk messaging a released object. 
     1539                 */ 
     1540                if (removedContactObservers && [removedContactObservers containsObject:observerValue]) 
     1541                        continue; 
    15271542 
    15281543                observer = [observerValue nonretainedObjectValue]; 
     
    15371552                        [attrChange unionSet:newKeys]; 
    15381553                } 
    1539                 [pool release]; 
    15401554        } 
    15411555 
     
    15451559                                                                                        userInfo:(modifiedKeys ? [NSDictionary dictionaryWithObject:modifiedKeys 
    15461560                                                                                                                                                                                                 forKey:@"Keys"] : nil)]; 
    1547  
     1561        informingObservers = NO; 
     1562         
     1563        //If we removed any observers while informing them, we don't need that information any more 
     1564        if (removedContactObservers) { 
     1565                [removedContactObservers release]; removedContactObservers = nil; 
     1566        } 
     1567         
    15481568        return [attrChange autorelease]; 
    15491569} 
     
    15521572- (void)_updateAllAttributesOfObject:(AIListObject *)inObject 
    15531573{ 
    1554         NSEnumerator    *enumerator = [contactObservers objectEnumerator]; 
     1574        NSEnumerator    *enumerator = [[[contactObservers copy] autorelease] objectEnumerator]; 
    15551575        NSValue                 *observerValue; 
    1556  
    1557         while ((observerValue = [enumerator nextObject])) {              
     1576         
     1577        informingObservers = YES; 
     1578         
     1579        while ((observerValue = [enumerator nextObject])) { 
     1580                /* Skip any observer which has been removed while we were iterating over observers, 
     1581                 * as we don't retain observers and therefore risk messaging a released object. 
     1582                 */ 
     1583                if (removedContactObservers && [removedContactObservers containsObject:observerValue]) 
     1584                        continue; 
     1585 
    15581586                id <AIListObjectObserver> observer = [observerValue nonretainedObjectValue]; 
    1559  
     1587                 
    15601588                [observer updateListObject:inObject keys:nil silent:YES]; 
     1589        } 
     1590 
     1591        informingObservers = NO; 
     1592         
     1593        //If we removed any observers while informing them, we don't need that information any more 
     1594        if (removedContactObservers) { 
     1595                [removedContactObservers release]; removedContactObservers = nil; 
    15611596        } 
    15621597}