AIProxyListObject no longer retains the ESObjectWithProperties which retains it; instead, it maintains a zeroing weak reference. This simplifies the memory management significantly.
authorEvan Schoenberg
Sun, 21 Aug 2011 16:34:04 -0500
changeset 3617 0447e5ab4152
parent 3616 c71cfeb0aa13
child 3618 c3f1acd5bbe7
AIProxyListObject no longer retains the ESObjectWithProperties which retains it; instead, it maintains a zeroing weak reference. This simplifies the memory management significantly.
* Don't try to aggressively release AIProxyListObjects when a group is collapsed. Instead, clear out its display cache. At that point, it's a very small object, and it can live until the AIListObject is deallocated.
* Although messaging the AIProxyListObject results in a call through to -[AIPorxyListObject listObject], it's clearer to use this directly. (Future changes should probably remove its 'mirroring' capability entirely; it's just a set up for confusion and mis-casting).
* I believe that between this and kbotc's changes, this fixes #14294. I never could reproduce the crash in the first place, though. Please test and report back.
(transplanted from 8de0a4e32e422217fd6aa24004c56ade6693b3fb)
Frameworks/Adium Framework/Source/AIAbstractListController.m
Frameworks/Adium Framework/Source/AIListCell.h
Frameworks/Adium Framework/Source/AIListCell.m
Frameworks/Adium Framework/Source/AIListContactCell.m
Frameworks/Adium Framework/Source/AIListContactGroupChatCell.m
Frameworks/Adium Framework/Source/AIListGroup.m
Frameworks/Adium Framework/Source/AIListGroupBubbleToFitCell.m
Frameworks/Adium Framework/Source/AIListGroupCell.m
Frameworks/Adium Framework/Source/AIListOutlineView.m
Frameworks/Adium Framework/Source/AIProxyListObject.h
Frameworks/Adium Framework/Source/AIProxyListObject.m
Frameworks/Adium Framework/Source/ESObjectWithProperties.h
Frameworks/Adium Framework/Source/ESObjectWithProperties.m
--- a/Frameworks/Adium Framework/Source/AIAbstractListController.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIAbstractListController.m	Sun Aug 21 16:34:04 2011 -0500
@@ -609,7 +609,7 @@
 	AIProxyListObject *proxyListObject;
 
 	if (item) {
-		AIListObject<AIContainingObject> *listObject = item.listObject;
+		id<AIContainingObject> listObject = (id<AIContainingObject>)(item.listObject);
 		proxyListObject = [AIProxyListObject proxyListObjectForListObject:[listObject visibleObjectAtIndex:index]
 															 inListObject:listObject];
 
@@ -628,7 +628,7 @@
 	NSInteger children;
 
 	if (item) {
-		AIListObject<AIContainingObject> *listObject = item.listObject;
+		id<AIContainingObject> listObject = (id<AIContainingObject>)(item.listObject);
 
 		children = listObject.visibleCount;
 	} else if (hideRoot)
@@ -685,16 +685,15 @@
 - (void)outlineView:(NSOutlineView *)outlineView setExpandState:(BOOL)state ofItem:(AIProxyListObject *)item
 {
 	/* XXX Should note the combination of item and item's parent for expansion tracking */
-	id<AIContainingObject> containingObject = item.listObject;
-    [containingObject setExpanded:state];
+    id<AIContainingObject> listObject = (id<AIContainingObject>)(item.listObject);
+    [listObject setExpanded:state];
 
 	if (!state) {
 		/* If the item is collapsed, clear cached data which was being used while it was displayed */
-		for (AIListObject *listObject in (containingObject.visibleContainedObjects)) {
-			[AIUserIcons flushCacheForObject:listObject];
-			
-			[listObject removeProxyObject:[AIProxyListObject existingProxyListObjectForListObject:listObject
-																					 inListObject:containingObject]];
+		for (AIListObject *containedListObject in (listObject.visibleContainedObjects)) {
+			[AIUserIcons flushCacheForObject:containedListObject];
+            [[AIProxyListObject existingProxyListObjectForListObject:containedListObject
+                                                        inListObject:listObject] flushCache];
 		}
 	}
 }
@@ -742,9 +741,9 @@
     [self hideTooltip];
 	
     //Return the context menu
-	AIListObject	*listObject = ((AIProxyListObject *)outlineView.firstSelectedItem).listObject;
+	AIListObject *listObject = ((AIProxyListObject *)outlineView.firstSelectedItem).listObject;
 
-	return [self contextualMenuForListObject:listObject];
+	return ([listObject isKindOfClass:[AIListObject class]] ? [self contextualMenuForListObject:listObject] : nil);
 }
 
 - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(AIProxyListObject *)item
--- a/Frameworks/Adium Framework/Source/AIListCell.h	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListCell.h	Sun Aug 21 16:34:04 2011 -0500
@@ -14,7 +14,9 @@
  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-@class AIListObject, AIListOutlineView, AIAdium, AIProxyListObject;
+#import "AIProxyListObject.h"
+
+@class AIListObject, AIListOutlineView, AIAdium;
 
 #define DROP_HIGHLIGHT_WIDTH_MARGIN 5.0
 #define DROP_HIGHLIGHT_HEIGHT_MARGIN 1.0
@@ -22,7 +24,6 @@
 @interface AIListCell : NSCell {
 	AIListOutlineView	*controlView;
     AIProxyListObject	*proxyObject;
-    AIListObject		*listObject;
     BOOL				isGroup;
 	
 	NSTextAlignment		textAlignment;
--- a/Frameworks/Adium Framework/Source/AIListCell.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListCell.m	Sun Aug 21 16:34:04 2011 -0500
@@ -72,7 +72,6 @@
 	AIListCell *newCell = [super copyWithZone:zone];
 
 	newCell->proxyObject = nil;
-	newCell->listObject = nil;
 	[newCell setProxyListObject:proxyObject];
 
 	[newCell->font retain];
@@ -90,7 +89,7 @@
 	
 	[font release];
 	
-	[listObject release];
+	[proxyObject release];
 	[labelAttributes release];
 
 	[super dealloc];
@@ -102,12 +101,9 @@
 	if (proxyObject != inProxyObject) {
 		[proxyObject release];
 		proxyObject = [inProxyObject retain];
-		
-		[listObject release];
-		listObject = [proxyObject.listObject retain];
 	}
 
-	isGroup = [listObject isKindOfClass:[AIListGroup class]];
+	isGroup = [[proxyObject listObject] isKindOfClass:[AIListGroup class]];
 }
 
 @synthesize isGroup, controlView;
@@ -174,7 +170,7 @@
 }
 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)inControlView
 {	
-	if (listObject) {
+	if ([proxyObject listObject]) {
 		//Cell spacing
 		cellFrame.origin.y += [self topSpacing];
 		cellFrame.size.height -= [self bottomSpacing] + [self topSpacing];
@@ -332,10 +328,10 @@
 - (NSString *)labelString
 {
 	NSString *label =  ([self shouldShowAlias] ? 
-						[listObject longDisplayName] :
-						(listObject.formattedUID ? listObject.formattedUID : [listObject longDisplayName]));
+						[[proxyObject listObject] longDisplayName] :
+						([proxyObject listObject].formattedUID ? [proxyObject listObject].formattedUID : [[proxyObject listObject] longDisplayName]));
 	if (!label) {
-		AILog(@"Couldn't get a labelString for contact %@", listObject);
+		AILog(@"Couldn't get a labelString for contact %@", [proxyObject listObject]);
 		return @"";
 	}
 	return label;
@@ -415,21 +411,21 @@
 		value = NSAccessibilityRowRole;
 		
 	} else if([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
-		if ([listObject isKindOfClass:[AIListGroup class]]) {
-			value = [NSString stringWithFormat:AILocalizedString(@"contact group %@", "%@ will be the name of a group in the contact list"), [listObject longDisplayName]];
+		if ([[proxyObject listObject] isKindOfClass:[AIListGroup class]]) {
+			value = [NSString stringWithFormat:AILocalizedString(@"contact group %@", "%@ will be the name of a group in the contact list"), [[proxyObject listObject] longDisplayName]];
 
-		} else if ([listObject isKindOfClass:[AIListBookmark class]]) {			
-			value = [NSString stringWithFormat:AILocalizedString(@"group chat bookmark %@", "%@ will be the name of a bookmark"), [listObject longDisplayName]];			
+		} else if ([[proxyObject listObject] isKindOfClass:[AIListBookmark class]]) {			
+			value = [NSString stringWithFormat:AILocalizedString(@"group chat bookmark %@", "%@ will be the name of a bookmark"), [[proxyObject listObject] longDisplayName]];
 
 		} else {
 			NSString *name, *statusDescription, *statusMessage;
 			
-			name = [listObject longDisplayName];
-			statusDescription = [adium.statusController localizedDescriptionForStatusName:(listObject.statusName ?
-																					  listObject.statusName :
-																					  [adium.statusController defaultStatusNameForType:listObject.statusType])
-																		  statusType:listObject.statusType];
-			statusMessage = [listObject statusMessageString];
+			name = [[proxyObject listObject] longDisplayName];
+			statusDescription = [adium.statusController localizedDescriptionForStatusName:([proxyObject listObject].statusName ?
+																						   [proxyObject listObject].statusName :
+																						   [adium.statusController defaultStatusNameForType:[proxyObject listObject].statusType])
+																			   statusType:[proxyObject listObject].statusType];
+			statusMessage = [[proxyObject listObject] statusMessageString];
 			
 			value = [[name mutableCopy] autorelease];
 			if (statusDescription) [value appendFormat:@"; %@", statusDescription];
--- a/Frameworks/Adium Framework/Source/AIListContactCell.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListContactCell.m	Sun Aug 21 16:34:04 2011 -0500
@@ -21,6 +21,7 @@
 #import <AIUtilities/AIStringAdditions.h>
 #import <Adium/AIServiceIcons.h>
 #import <Adium/AIUserIcons.h>
+#import "AIProxyListObject.h"
 
 #define NAME_STATUS_PAD			6
 
@@ -110,6 +111,8 @@
 {
 	int		width = [super cellWidth];
 	
+	AIListObject *listObject = [proxyObject listObject];
+
 	//Name
 	width += ceil(self.displayNameSize.width);
 	
@@ -206,6 +209,8 @@
 //Cache is flushed when alignment, color, or font is changed
 - (NSDictionary *)statusAttributes
 {
+    AIListObject    *listObject = [proxyObject listObject];
+
 	if (!_statusAttributes) {
 		NSMutableParagraphStyle	*paragraphStyle = [NSMutableParagraphStyle styleWithAlignment:NSLeftTextAlignment
 																				lineBreakMode:NSLineBreakByTruncatingTail];
@@ -358,6 +363,8 @@
 //Draw content of our cell
 - (void)drawContentWithFrame:(NSRect)rect
 {
+    AIListObject    *listObject = [proxyObject listObject];
+
 	//Far Left
 	if (statusIconPosition == LIST_POSITION_FAR_LEFT) rect = [self drawStatusIconInRect:rect position:IMAGE_POSITION_LEFT];
 	if (serviceIconPosition == LIST_POSITION_FAR_LEFT) rect = [self drawServiceIconInRect:rect position:IMAGE_POSITION_LEFT];
@@ -457,7 +464,8 @@
 //User Icon
 - (NSRect)drawUserIconInRect:(NSRect)inRect position:(IMAGE_POSITION)position
 {
-	NSRect	rect = inRect;
+    AIListObject    *listObject = [proxyObject listObject];
+	NSRect          rect = inRect;
 	if (userIconVisible) {
 		NSImageInterpolation savedInterpolation = [[NSGraphicsContext currentContext] imageInterpolation];
 		NSImage *image;
@@ -637,6 +645,7 @@
 
 - (BOOL)shouldShowAlias
 {
+    AIListObject *listObject = [proxyObject listObject];
 	// If we use aliases...
 	if (useAliasesAsRequested) {
 		// If we use aliases on non-parents OR this is a parent...
@@ -651,6 +660,7 @@
 //Contact label color
 - (NSColor *)labelColor
 {
+	AIListObject *listObject = [proxyObject listObject];
 	BOOL	isEvent = [listObject boolValueForProperty:@"Is Event"];
 	
 	if ((isEvent && backgroundColorIsEvents) || (!isEvent && backgroundColorIsStatus)) {
@@ -668,6 +678,7 @@
 //Contact text color
 - (NSColor *)textColor
 {
+    AIListObject *listObject = [proxyObject listObject];
 	NSColor	*theTextColor;
 	if (shouldUseContactTextColors && (theTextColor = [listObject valueForProperty:@"Text Color"])) {
 		return theTextColor;
@@ -683,23 +694,27 @@
 //Contact user image - AIUserIcons should already have been informed of our desired size by setUserIconSize: above.
 - (NSImage *)userIconImage
 {
+	AIListObject *listObject = [proxyObject listObject];
 	return [AIUserIcons listUserIconForContact:(AIListContact *)listObject size:userIconSize];
 }
 
 //Contact state or status image
 - (NSImage *)statusImage
 {
+	AIListObject *listObject = [proxyObject listObject];
 	return [listObject statusIcon];
 }
 
 //Contact service image
 - (NSImage *)serviceImage
 {
+	AIListObject *listObject = [proxyObject listObject];
 	return [AIServiceIcons serviceIconForObject:listObject type:AIServiceIconList direction:AIIconFlipped];
 }
 
 - (float)imageOpacityForDrawing
 {
+	AIListObject *listObject = [proxyObject listObject];
 	NSNumber *imageOpacityNumber = [listObject numberValueForProperty:@"Image Opacity"];
 	return (imageOpacityNumber ? [imageOpacityNumber floatValue] : 0.0);
 }
--- a/Frameworks/Adium Framework/Source/AIListContactGroupChatCell.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListContactGroupChatCell.m	Sun Aug 21 16:34:04 2011 -0500
@@ -15,6 +15,7 @@
 
 - (NSString *)labelString
 {
+	AIListObject *listObject = [proxyObject listObject];
 	NSString *label;
 	
 	if (chat && [chat displayNameForContact:listObject]) {
@@ -28,12 +29,14 @@
 
 - (NSImage *)statusImage
 {
+    AIListObject    *listObject = [proxyObject listObject];
 	return [[AIGroupChatStatusIcons sharedIcons] imageForFlag:[chat flagsForContact:listObject]];
 }
 
 - (NSImage *)serviceImage
 {
 	// We can't use [listObject statusIcon] because it will show unknown for strangers.
+    AIListObject    *listObject = [proxyObject listObject];
 	return [AIStatusIcons statusIconForListObject:listObject
 											 type:AIStatusIconList
 										direction:AIIconFlipped];
@@ -41,6 +44,7 @@
 
 - (NSColor *)textColor
 {
+    AIListObject    *listObject = [proxyObject listObject];
 	return [[AIGroupChatStatusIcons sharedIcons] colorForFlag:[chat flagsForContact:listObject]];
 }
 
--- a/Frameworks/Adium Framework/Source/AIListGroup.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListGroup.m	Sun Aug 21 16:34:04 2011 -0500
@@ -121,7 +121,7 @@
 		/* Should be able to remove the proxy object here, but it seemed to cause a crash previously (before fixes
 		 * made to the contactObserverManager. Reenable after 1.4.
 		 */
-		//[obj removeProxyObject:[AIProxyListObject existingProxyListObjectForListObject:obj inListObject:self]];
+		[[AIProxyListObject existingProxyListObjectForListObject:obj inListObject:self] flushCache];
 		[AIUserIcons flushCacheForObject:obj];
 	}
 
@@ -160,10 +160,7 @@
 			modifiedProperties = [NSSet setWithObjects:@"VisibleObjectCount", nil];
 			
 			if (!shouldBeVisible) {
-				/* Should be able to remove the proxy object here, but it seemed to cause a crash previously (before fixes
-				 * made to the contactObserverManager. Reenable after 1.4.
-				 */
-				//[inObject removeProxyObject:[AIProxyListObject existingProxyListObjectForListObject:inObject inListObject:self]];
+				[[AIProxyListObject existingProxyListObjectForListObject:inObject inListObject:self] flushCache];
 				[AIUserIcons flushCacheForObject:inObject];
 			}
 		}
--- a/Frameworks/Adium Framework/Source/AIListGroupBubbleToFitCell.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListGroupBubbleToFitCell.m	Sun Aug 21 16:34:04 2011 -0500
@@ -27,6 +27,7 @@
 - (NSAttributedString *)displayName
 {
 	NSString *countText;
+	AIListObject *listObject = [proxyObject listObject];
 
 	if ([listObject boolValueForProperty:@"Show Count"] &&
 		(countText = [listObject valueForProperty:@"Count Text"])) {
@@ -45,6 +46,7 @@
  */
 - (int)cellWidth
 {
+	AIListObject *listObject = [proxyObject listObject];
 	int width = [super cellWidth];
 
 	if ([listObject boolValueForProperty:@"Show Count"] && 
--- a/Frameworks/Adium Framework/Source/AIListGroupCell.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListGroupCell.m	Sun Aug 21 16:34:04 2011 -0500
@@ -135,7 +135,8 @@
 }
 - (int)cellWidth
 {
-	unsigned			width = [super cellWidth] + [self flippyIndent] + GROUP_COUNT_PADDING;
+	AIListObject    *listObject = [proxyObject listObject];
+	unsigned		width = [super cellWidth] + [self flippyIndent] + GROUP_COUNT_PADDING;
 	
 	//Get the size of our display name
 	width += ceil(self.displayNameSize.width) + 1;
@@ -173,6 +174,8 @@
 //Draw content of our cell
 - (void)drawContentWithFrame:(NSRect)rect
 {
+    AIListObject *listObject = [proxyObject listObject];
+    
 	//Draw flippy triangle (disclosure triangle)
 	[[self flippyColor] set];
 	
@@ -205,6 +208,8 @@
 
 - (NSRect)drawGroupCountWithFrame:(NSRect)inRect
 {
+	AIListObject *listObject = [proxyObject listObject];
+
 	if ([listObject valueForProperty:@"Count Text"]) {
 		NSAttributedString	*groupCount = [[NSAttributedString alloc] initWithString:[listObject valueForProperty:@"Count Text"]
 																		  attributes:[self labelAttributes]];
--- a/Frameworks/Adium Framework/Source/AIListOutlineView.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIListOutlineView.m	Sun Aug 21 16:34:04 2011 -0500
@@ -221,7 +221,7 @@
 	for (unsigned i = 0; i <numberOfRows ; i++) {
 		AIProxyListObject *item = [self itemAtRow:i];
 		if ([item isKindOfClass:[AIListContact class]]) {
-			return item.listObject;
+			return (AIListContact *)item.listObject;
 		}
 	}
 
--- a/Frameworks/Adium Framework/Source/AIProxyListObject.h	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIProxyListObject.h	Sun Aug 21 16:34:04 2011 -0500
@@ -6,12 +6,12 @@
 //  Copyright 2009 Adium X / Saltatory Software. All rights reserved.
 //
 
-@class ESObjectWithProperties;
+@class ESObjectWithProperties, MAZeroingWeakRef;
 @protocol AIContainingObject;
 
 @interface AIProxyListObject : NSObject {
-	id listObject;
-	id <AIContainingObject> containingObject;
+	MAZeroingWeakRef *weakRef_listObject;
+    MAZeroingWeakRef *weakRef_containingObject;
 	NSString *key;
 	NSString *cachedDisplayNameString;
 	NSAttributedString *cachedDisplayName;
@@ -23,18 +23,15 @@
 @property (nonatomic, retain) NSAttributedString *cachedDisplayName;
 @property (nonatomic) NSSize cachedDisplayNameSize;
 
-/*! 
- * @brief Return the listObject represented by this AIProxyListObject
- *
- * This is a retain loop, as listObject also retains its AIProxyListObjects.
- * It is therefore imperative that, when an AIListObject is no longer tracked by an account,
- * +[AIProxyListObject releaseProxyObject:] is called. This must not wait until -[AIListContact dealloc] or it would
- * never be called.
- */
-@property (nonatomic, retain) id listObject; 
-@property (nonatomic, assign) id <AIContainingObject> containingObject;
 @property (nonatomic, retain) NSString *key;
 
+- (AIListObject *)listObject;
+- (void)setListObject:(AIListObject *)inListObject;
+
+- (ESObjectWithProperties <AIContainingObject> *)containingObject;
+- (void)setContainingObject:(ESObjectWithProperties <AIContainingObject> *)containingObject;
+
+
 + (AIProxyListObject *)proxyListObjectForListObject:(ESObjectWithProperties *)inListObject
 									   inListObject:(ESObjectWithProperties<AIContainingObject> *)containingObject;
 
@@ -42,10 +39,13 @@
 											   inListObject:(ESObjectWithProperties <AIContainingObject>*)inContainingObject;
 
 /*!
- * @biref Called by ESObjectWithProperties to release its proxy object.
- *
- * This method resolves the retain count noted in documentation for -[AIPorxyListObject listObject]; it must be called.
+ * @brief Called when an AIListObject is done with an AIProxyListObject to remove it from the global dictionary
  */
 + (void)releaseProxyObject:(AIProxyListObject *)proxyObject;
 
+/*!
+ * @brief Clear out cached display information; should be called when the AIProxyListObject may be used later
+ */
+- (void)flushCache;
+
 @end
--- a/Frameworks/Adium Framework/Source/AIProxyListObject.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/AIProxyListObject.m	Sun Aug 21 16:34:04 2011 -0500
@@ -9,6 +9,7 @@
 #import "AIProxyListObject.h"
 #import <Adium/ESObjectWithProperties.h>
 #import <Adium/AIListObject.h>
+#import <Adium/MAZeroingWeakRef.h>
 
 @interface NSObject (PublicAPIMissingFromHeadersAndDocsButInTheReleaseNotesGoshDarnit)
 - (id)forwardingTargetForSelector:(SEL)aSelector;
@@ -16,7 +17,7 @@
 
 @implementation AIProxyListObject
 
-@synthesize listObject, containingObject, key, cachedDisplayName, cachedDisplayNameString, cachedLabelAttributes, cachedDisplayNameSize;
+@synthesize key, cachedDisplayName, cachedDisplayNameString, cachedLabelAttributes, cachedDisplayNameSize;
 
 static NSMutableDictionary *proxyDict;
 
@@ -26,7 +27,7 @@
 		proxyDict = [[NSMutableDictionary alloc] init];
 }
 
-+ (AIProxyListObject *)existingProxyListObjectForListObject:(ESObjectWithProperties *)inListObject
++ (AIProxyListObject *)existingProxyListObjectForListObject:(AIListObject *)inListObject
 											   inListObject:(ESObjectWithProperties <AIContainingObject>*)inContainingObject
 {
 	NSString *key = (inContainingObject ? 
@@ -36,7 +37,7 @@
 	return [proxyDict objectForKey:key];
 }
 
-+ (AIProxyListObject *)proxyListObjectForListObject:(ESObjectWithProperties *)inListObject
++ (AIProxyListObject *)proxyListObjectForListObject:(AIListObject *)inListObject
 									   inListObject:(ESObjectWithProperties <AIContainingObject>*)inContainingObject
 {
 	AIProxyListObject *proxy;
@@ -45,18 +46,14 @@
 					 inListObject.internalObjectID);
 
 	proxy = [proxyDict objectForKey:key];
-	
+
 	if (proxy && proxy.listObject != inListObject) {
-		// If the old list object is for some reason invalid (released in contact controller, but not fully released)
-		// we end up with an old list object as our proxied object. Correct this by getting rid of the old one.
-#ifdef DEBUG_BUILD
+        /* This is a cataclysmic memory management failure and should NEVER happen. I leave the logging in for now. -evands 8/7/11 */
 		NSLog(@"Re-used AIProxyListObject (this should not happen.). Key %@ for inListObject %@ -> %p.listObject=%@", key,inListObject,proxy,proxy.listObject);
-#endif
-		[proxy.listObject removeProxyObject:proxy];
 		[self releaseProxyObject:proxy];
 		proxy = nil;
 	}
-	
+
 	if (!proxy) {
 		proxy = [[AIProxyListObject alloc] init];
 		proxy.listObject = inListObject;
@@ -73,7 +70,7 @@
 }
 
 /*!
- * @brief Release a proxy object
+ * @brief Called when an AIListObject is done with an AIProxyListObject to remove it from the global dictionary
  *
  * This should be called only by AIListObject when it deallocates, for each of its proxy objects
  */
@@ -84,49 +81,79 @@
 	[proxyDict removeObjectForKey:proxyObject.key];
 }
 
+- (void)flushCache
+{
+	self.cachedDisplayName = nil;
+	self.cachedDisplayNameString = nil;
+	self.cachedLabelAttributes = nil;
+}
+
 - (void)dealloc
 {
 	AILogWithSignature(@"%@", self);
-	self.listObject = nil;
 	self.key = nil;
-	self.cachedDisplayName = nil;
-	self.cachedDisplayNameString = nil;
-	self.cachedLabelAttributes = nil;
+    [weakRef_listObject release];
+    [weakRef_containingObject release];
+
+    [self flushCache];
 	
 	[super dealloc];
 }
 
+- (AIListObject *)listObject
+{
+    return [weakRef_listObject target];
+}
+
+- (void)setListObject:(AIListObject *)inListObject
+{
+    [weakRef_listObject release];
+    weakRef_listObject = [[MAZeroingWeakRef alloc] initWithTarget: inListObject];
+}
+
+- (ESObjectWithProperties<AIContainingObject> *)containingObject
+{
+    return (ESObjectWithProperties<AIContainingObject> *)[weakRef_listObject target];
+}
+
+- (void)setContainingObject:(ESObjectWithProperties<AIContainingObject> *)inContainingObject
+{
+    [weakRef_containingObject release];
+    weakRef_containingObject = [[MAZeroingWeakRef alloc] initWithTarget:(id)inContainingObject];
+}
+
+
 /* Pretend to be our listObject. I suspect being an NSProxy subclass could do this more cleanly, but my initial attempt
  * failed and this works fine.
  */
 - (Class)class
 {
-	return [listObject class];
+	return [[self listObject] class];
 }
 
 - (BOOL)isKindOfClass:(Class)class
 {
-	return [listObject isKindOfClass:class];
+	return [[self listObject] isKindOfClass:class];
 }
 
 - (BOOL)isMemberOfClass:(Class)class
 {
-	return [listObject isMemberOfClass:class];
+	return [[self listObject] isMemberOfClass:class];
 }
 
 - (BOOL)isEqual:(id)inObject
 {
-	return [listObject isEqual:inObject];
+	return [[self listObject] isEqual:inObject];
 }
 
-- (id)forwardingTargetForSelector:(SEL)aSelector;
+- (id)forwardingTargetForSelector:(SEL)aSelector
 {
-	return listObject;
+	return [self listObject];
 }
 
 - (NSString *)description
 {
-	return [NSString stringWithFormat:@"<AIProxyListObject %p -> %@>", self, listObject];
+	return [NSString stringWithFormat:@"<AIProxyListObject %p -> %@>", self, [self listObject]];
 }
 
 @end
--- a/Frameworks/Adium Framework/Source/ESObjectWithProperties.h	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/ESObjectWithProperties.h	Sun Aug 21 16:34:04 2011 -0500
@@ -67,7 +67,6 @@
 
 - (NSSet *)proxyObjects;
 - (void)noteProxyObject:(id)proxyObject;
-- (void)removeProxyObject:(id)proxyObject;
 - (void)clearProxyObjects;
 
 @end
--- a/Frameworks/Adium Framework/Source/ESObjectWithProperties.m	Sun Aug 21 16:19:20 2011 -0500
+++ b/Frameworks/Adium Framework/Source/ESObjectWithProperties.m	Sun Aug 21 16:34:04 2011 -0500
@@ -328,15 +328,4 @@
 	[proxyObjects addObject:proxyObject];
 }
 
-/*!
- * @brief Notify that a proxy object has been removed for this object
- */
-- (void)removeProxyObject:(id)proxyObject
-{
-	if (proxyObject) {
-		[AIProxyListObject releaseProxyObject:proxyObject];
-		[proxyObjects removeObject:proxyObject];
-	}
-}
-
 @end