Reputation: 934
Objective-C weak properties should point to nil if the object gets deallocated, but in this case weak properties seem to retain the object. Consider the case:
@interface SillyObject : NSObject
@property (nonatomic, assign) NSInteger val;
-(void)printVal;
@end
@implementation SillyObject
-(void)printVal
{
NSLog(@"%d", self.val);
}
@end
-(void)saveReference
{
SillyObject* s = [SillyObject new];
s.val = 100;
[[ObjectCache sharedInstance] addWeakRef:s callback:^(NSString* junk) {
[s printVal];
}];
}
callSillyObjectBlocks loops over all the objects added to the cache and calls the correspondending blocks (see below)
-(void)callDeadObject
{
[self saveReference];
[[ObjectCache sharedInstance] callSillyObjectBlocks];
}
Now saveReference exits and the SillyObject should be deallocated, but it doesn't and the weak reference is not nil.
The relevant implementation details of the cache:
typedef void (^Callback)(NSString* junk);
@interface CacheSlot : NSObject
@property (nonatomic, copy) Callback callback;
@property (nonatomic, weak) id source;
// More irrelevant properties.
-(instancetype)initWithRef:(__weak id)obj callback:(Callback)cb;
@end
@implementation CacheSlot
@synthesize callback, source;
-(instancetype)initWithRef:(__weak id)obj callback:(Callback)cb
{
self = [super init];
if(self)
{
self.callback = cb;
self.source = obj;
}
return self;
}
@end
@interface ObjectCache()
// This array contains CacheSlot objects
@property (nonatomic, strong) NSMutableArray* savedObjects;
@end
// Implementation.
-(void)addWeakRef:(id)obj callback:(Callback)block
{
__weak id src = obj;
[self.savedObjects addObject:[[CacheSlot alloc] initWithRef:src callback:block]];
}
-(void)callSillyObjectBlocks
{
for(CacheSlot* slot in self.savedObjects)
{
if(slot.source)
{
slot.callback(@"Whatever!");
}
else
{
// Remove the slot from cache
}
}
}
Calling saveReference initially should create a temporary object which gets deallocated as soon as the function exits (which it does if I call addWeakRef:nil instead).
After calling saveReference I run callSillyObjectBlocks and the added object's corresponding block should not be called, but it gets called with the object's value. Output:
100
Upvotes: 1
Views: 136
Reputation: 78825
Whenever you have a block that references a variable from outside the block, you need to declare it as weak as well. Otherwise you get a strong reference. So it should be:
-(void)saveReference
{
SillyObject* s = [SillyObject new];
s.val = 100;
SillyObject * __weak weakSilly = s;
[[ObjectCache sharedInstance] addWeakRef:s callback:^(NSString* junk) {
[weakSilly printVal];
}];
}
At the same time you could simplify your code by removing __weak
from a few other locations as they affect method arguments only that will no retain an instance for a relevant time:
-(instancetype)initWithRef:(id)obj callback:(Callback)cb {
...
}
-(void)addWeakRef:(id)obj callback:(Callback)block
{
[self.savedObjects addObject:[[CacheSlot alloc] initWithRef:obj callback:block]];
}
Upvotes: 3