【问题标题】:__weak seemingly retains the object__weak 貌似保留了对象
【发布时间】:2013-10-04 21:09:30
【问题描述】:

如果对象被释放,Objective-C 的弱属性应该指向 nil,但在这种情况下,弱属性似乎会保留对象。考虑案例:

@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 循环遍历所有添加到缓存中的对象并调用对应的块(见下文)

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

现在 saveReference 退出并且 SillyObject 应该被释放,但它没有并且弱引用不是 nil。

缓存的相关实现细节:

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:nil 则它会这样做)。

调用 saveReference 后,我运行 callSillyObjectBlocks 并且不应调用添加的对象的相应块,但会使用对象的值调用它。 输出:

100

【问题讨论】:

  • 请注意,读取弱变量可能会保留并自动释放对象。您可能需要添加一个自动释放池来清理这些引用并允许对象死亡。

标签: ios objective-c objective-c-blocks


【解决方案1】:

当你有一个从块外引用变量的块时,你也需要将它声明为weak。否则你会得到一个强有力的参考。所以应该是:

-(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]];
}

【讨论】:

  • 谢谢,虽然在可用性方面这很糟糕:(如果我正在使用缓存并想将 self 添加到具有相应块的对象缓存中,我不想与 self 交换在块的每一行都使用weakSelf。但目前我想不出解决方法。
  • 您可能需要考虑缓存的设计。按照目前的情况,您为每个缓存实例添加两个引用:一个直接引用(实例变量 source)和一个间接引用(通过 callback 实例变量和关联的块)。还可以想象所有缓存的实例都实现了一个通用的 CacheCallback 协议。然后你可以摆脱 callback 实例变量和所有相关的问题。
  • 你是对的,但是使用块比每次都让类符合协议更方便。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2017-08-26
相关资源
最近更新 更多