Block对象可以使用其封闭作用域(enclosing scope)内的所有变量。对声明了某个Block对象的方法,该方法的作用域就是这个Block对象的封闭作用域。因此,这个Block对象可以访问该方法的所有局部变量、传入该方法的实参以及所属对象的实例变量。在上述代码中,BNRItem对象(item)和BNRItemCell对象(cell)都是actionBlock封闭作用域中的捕获变量。
如果捕获变量是Objective-C对象,那么Block对象对捕获变量具有强引用。如果捕获变量也对Block对象具有强引用,就会导致强引用循环。之前在actionBlock中创建rect时,Xcode会提示警告信息:“Capturing 'cell' strongly in this block is likely to lead to a strong reference cycle(在Block对象中捕获‘cell’很可能导致强引用循环)”——BNRItemCell对actionBlock具有强引用,actionBlock对BNRItemCell也具有强引用,如图19-14所示。
图19-14 cell和Block对象之间存在强引用循环
解决问题的方法是:将actionBlock对cell的引用改为弱引用。
在BNRItemsViewController.m中修改actionBlock中的代码,使其对cell具有弱引用,代码如下:
__weak *weakCell = cell;
cell.actionBlock = ^{
NSLog(@“Going to show the image for %@”, item);
BNRItemCell *strongCell = weakCell;
if ([UIDevice currentDevice] userInterfaceIdiom == UIUserInterfaceIdiomPad) {
NSString *itemKey = item.itemKey;
// 如果BNRItem对象没有图片,就直接返回
UIImage *img = [[BNRImageStore sharedStore] imageForKey:imageKey];
if (!img)
return;
}
// 根据UITableView对象的坐标系获取UIImageView对象的位置和大小
// 注意:这里也许会出现警告信息,下面很快就会讨论到
CGRect rect = [self.viewconvertRect:cell.thumbnailView.bounds
fromView:cell.thumbnailView];
CGRect rect = [self.viewconvertRect:strongCell.thumbnailView.bounds
fromView:strongCell.thumbnailView];
// 创建BNRImageViewController对象并为image属性赋值
BNRImageViewController *ivc = [[BNRImageViewController alloc] init];
ivc.image = img;
// 根据UIImageView对象的位置和大小
// 显示一个大小为600x600点的UIPopoverController对象
self.imagePopover = [[UIPopoverController alloc]
initWithContentViewController:ivc];
self.imagePopover.delegate = self;
self.imagePopover.PopoverContentSize = CGSizeMake(600, 600)];
[self.imagePopover presentPopoverFromRect:rect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
};
在Block对象执行过程中,必须保证Block对象始终可以访问cell。因此,以上代码在actionBlock内部创建了strongCell,以保持对cell的强引用。这与Block对象对捕获变量的强引用不同,strongCell只是在Block对象执行过程中对cell保持强引用。
构建并运行应用,运行结果与之前相同,但是现在应用不会由于强引用循环导致内存泄露。