我正在阅读Big Nerd Ranch iOS教科书,第19章,我不清楚为什么他们在那里重新分配指针以避免强大的参考周期。基本上他们有一个代码块actionBlock
,它强烈引用了类BNRItemCell
,并且引用在块的生命周期之后仍然存在。 BNRItemCell
通过@property
:@property (strong, nonatomic) void (^actionBlock)(void)
引用该块。显然,这会产生强大的参考周期。为避免这种情况,他们在块外定义__weak BNRItemCell *weakCell = cell
,然后在块内定义BNRItemCell *strongCell = weakCell
。他们说strongCell
应该在块执行时持续存在,并且当块完成时它将被销毁。我不理解块中的重新分配BNRItemCell *strongCell = weakCell
,以及指针是__weak
的重点是什么。如果,我们只是BNRItemCell *strongCell = cell
,cell
是强烈引用该类的,那么它应该没问题吗?当块执行完毕后,strongCell
仍然会被销毁。
我试图想象一下发生了什么,他们在做什么对我来说没有意义。为了弄清楚,
之间有什么区别__weak someClass *weakPointer = strongPointer_1;
someObject.actionBlock = ^{
someClass *strongPointer_2 = weakPoiter;
// Here we are using strongPointer_2
}
和
someObject.actionBlock = ^{
someClass *strongPointer_2 = strongPointer_1;
// Here we are using strongPointer_2
}
?
我无法区分,所以我猜,我对幕后发生的事情并不清楚。
完整代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get a new or recycled cell
BNRItemCell *cell =
[tableView dequeueReusableCellWithIdentifier:@"BNRItemCell"
forIndexPath:indexPath];
// Set the text on the cell with the description of the item
// that is the nth index of items, where n = row this cell
// will appear in on the tableview
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *item = items[indexPath.row];
// Configure the cell with the BNRItem
cell.nameLabel.text = item.itemName;
cell.serialNumberLabel.text = item.serialNumber;
cell.valueLabel.text = [NSString stringWithFormat:@"$%d", item.valueInDollars];
cell.thumbnailView.image = item.thumbnail;
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
NSLog(@"Going to show image for %@", item);
BNRItemCell *strongCell = weakCell;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
NSString *itemKey = item.itemKey;
// if there is no image, we don't need to display anything
UIImage *img = [[BNRImageStore sharedStore] imageForKey:itemKey];
if (!img) {
return;
}
BNRImageViewController *ivc = [[BNRImageViewController alloc] init];
ivc.image = img;
ivc.modalPresentationStyle = UIModalPresentationPopover;
ivc.preferredContentSize = CGSizeMake(380, 300);
CGRect frame = [self.view convertRect:strongCell.thumbnailView.bounds
toView:self.view];
// frame.origin.y -= 150;
UIPopoverPresentationController *popoverController = ivc.popoverPresentationController;
popoverController.permittedArrowDirections = UIPopoverArrowDirectionUp;
popoverController.sourceView = cell.thumbnailView;
popoverController.sourceRect = frame;
[self.navigationController presentViewController:ivc animated:YES completion:nil];
}
};
return cell;
}
答案 0 :(得分:1)
如果我正确理解这一点,看起来他们正试图阻止保留周期。
__weak BNRItemCell *weakCell = cell;
cell.actionBlock = ^{
BNRItemCell *strongCell = weakCell; // weakCell is captured here at block declaration time
}
阻止捕获并保留变量值,因此如果它捕获了强BNRItemCell
(对自身的引用),则其强保留actionBlock
将创建保留周期。通过将BNRItemCell
捕获为弱,如果表视图决定丢弃该单元格,则您的单元格将被正确释放。
<小时/> 如果它对您有所帮助,请考虑
BNRItemCell
存在多少强引用。当单元格在屏幕上时,表格视图保留强引用(+1)。通过在actionBlock
中捕获强引用(+1),您的引用计数最多为2.
只要引用计数为1或更多,对象就会保留在内存中。因此,即使UITableView
释放其对单元格的引用,您仍然可以在其actionBlock
中获得引用,这将使其引用计数为正。由于您从未设置actionBlock = nil
(您不应该这样做),actionBlock
将永远不会被释放,因此BNRItemCell
也永远不会发布。
请注意,weakCell
是块创建时唯一捕获的值,strongCell
甚至不会存在,直到实际调用块为止。
TL; DR:该单元格将通过其actionBlock
保持对自身的强引用,这将创建一个保留周期。
答案 1 :(得分:1)
了解为什么&#34;正常&#34;非常重要。在块中捕获self
或其他引用的情况会导致保留周期。
如果您有一个实例变量,其中包含对块的强引用,则该块将不会被解除分区,直到该引用为nil
。如果该块捕获对引用该块的实例的强引用,则该实例不会被dealloc
编辑,直到该引用为nil
。如果实例和块同时包含这些引用,那么除非明确nil
引用另一个引用,否则您将拥有经典循环。
这里的关键是块对类实例的引用保存在捕获数据中。只要该块存在,该数据就会存在。
现在我们可以看到为什么捕获弱指针会有所帮助。如果问题是捕获数据,而不是块本身中的引用,则弱指针会很好地处理我们的循环。现在,类实例可以独立于块dealloc
编辑,因为该块没有强引用。
现在的问题是,为什么在块体内有一个强大的参考?这只是为了确保在块执行时实例不会dealloc
。这并不是主要线程上运行的东西的真正问题,但该块可能正在调度到后台线程。
为什么没有(重新)创建保留周期?嗯,确实如此,但只是暂时的。块内的强引用捕获弱指针,可以nil
。强引用是一个块范围的变量,当块退出时将被清除。 ARC确保在从块返回之前这样的引用是release
d,从而停止循环。