2013-10-04 3 views
1

Слабые свойства Objective-C должны указывать на нуль, если объект освобождается, но в этом случае слабые свойства, похоже, сохраняют объект. Рассмотрим случай:__weak, похоже, сохраняет объект

@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 перебирает все объекты добавляются в кэш и вызывает correspondending блоков (см ниже)

-(void)callDeadObject 
{ 
    [self saveReference]; 
    [[ObjectCache sharedInstance] callSillyObjectBlocks]; 
} 

Теперь saveReference выходы и SillyObject должны быть высвобождены, но это Безразлично 't и слабая ссылка не равна нулю.

Соответствующие детали реализации кэша:

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 
    } 
    } 
} 

Вызывающие saveReference первоначально должен создать временный объект, который будет освобождён как только функция выходов (что он делает, если я позвоню addWeakRef: ноль вместо).

После вызова saveReference я запускаю callSillyObjectBlocks, и соответствующий блок добавленного объекта не следует вызывать, но он вызывается с его значением. Выход:

100 
+0

Обратите внимание, что чтение слабой переменной может сохранять и автообновлять объект. Возможно, вам придется добавить пул авторесурсов, чтобы очистить эти ссылки и позволить объекту умереть. –

ответ

3

Всякий раз, когда у вас есть блок, который ссылается на переменную вне блока, вы должны объявить его как слабой а. В противном случае вы получите сильную ссылку. Так оно и должно быть:

-(void)saveReference 
{ 
    SillyObject* s = [SillyObject new]; 
    s.val = 100; 
    SillyObject * __weak weakSilly = s; 

    [[ObjectCache sharedInstance] addWeakRef:s callback:^(NSString* junk) { 
    [weakSilly printVal]; 
    }]; 
} 

В то же время, вы можете упростить ваш код, удаляя __weak от нескольких других мест, так как они влияют на аргументы метода только то, что будет не сохраняющим экземпляр для соответствующего времени:

-(instancetype)initWithRef:(id)obj callback:(Callback)cb { 
    ... 
} 

-(void)addWeakRef:(id)obj callback:(Callback)block 
{ 
    [self.savedObjects addObject:[[CacheSlot alloc] initWithRef:obj callback:block]]; 
} 
+0

Спасибо, хотя в удобстве использования это ужасно :(Если я использую кеш и хочу добавить себя в кэш объектов с соответствующим блоком, я бы не хотел обмениваться «я» со слабым Self в каждой строке блока Но на данный момент я не могу придумать обходное решение для этого. – SMK

+0

Возможно, вам стоит подумать о дизайне вашего кеша. В настоящее время вы добавляете две ссылки для каждого кэшированного экземпляра: одна прямая ссылка (переменная экземпляра * источник *) и одну косвенную ссылку (через переменную экземпляра * callback * и связанный с ней блок). Можно также представить, что весь кешированный экземпляр реализует общий протокол * CacheCallback *. Тогда вы можете избавиться от переменной экземпляра * callback * и все связанные проблемы. – Codo

+0

Вы правы, однако использование блоков было бы более удобным, чем приведение класса в соответствие с протоколом каждый раз. – SMK

Смежные вопросы