Big Nerd Ranch,第19章,通过重新分配指针避免强大的参考周期

时间:2015-11-19 00:50:23

标签: ios objective-c pointers

我正在阅读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 = cellcell是强烈引用该类的,那么它应该没问题吗?当块执行完毕后,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;
}

2 个答案:

答案 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,从而停止循环。