Cocoa中Block隐式持有self警告:两种解决方式的差异
关于Block中self引用的两种写法差异分析
你编写的这段Cocoa拖拽处理代码:
NSArray<Class> *acceptableClasses = @[[DesktopEntity class]]; __block NSInteger insertIdx = row; [info enumerateDraggingItemsWithOptions:0 forView:_tableView classes:acceptableClasses searchOptions:nil usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) { DesktopEntity *entity = draggingItem.item; [_tableContents insertObject:entity atIndex: insertIdx]; [_tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:insertIdx] withAnimation:NSTableViewAnimationEffectGap]; draggingItem.draggingFrame = [_tableView frameOfCellAtColumn:0 row: insertIdx]; ++insertIdx; }];
编译时触发了警告:
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
你通过显式添加self关键字消除了警告:
[self.tableContents insertObject:entity atIndex: insertIdx]; [self.tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:insertIdx] withAnimation:NSTableViewAnimationEffectGap]; draggingItem.draggingFrame = [self.tableView frameOfCellAtColumn:0 row: insertIdx]; ++insertIdx;
下面解释两种写法的差异,以及你的第一种方法是否正确:
两种写法的核心差异
1. 显式使用self.xxx
- Block会强引用self:当你通过
self访问属性或实例变量时,Block会自动对self进行强引用(retain)。 - 适用场景:当Block是同步执行,或者Block的生命周期与self完全一致,不会造成循环引用时使用。比如你这里的
enumerateDraggingItemsWithOptions:usingBlock:是同步执行的方法,Block执行完成后就会被销毁,不会长期持有self,不存在循环引用风险。 - 优势:写法简洁,代码可读性高,不需要额外的变量定义。
2. 使用__weak typeof(self) weakSelf = self;
- 核心目的是避免循环引用:当Block会被self持有(比如赋值给self的属性),同时Block又持有self时,会形成循环引用,导致两者都无法被释放。这种写法通过弱引用self,打破循环。
- 进阶用法:有时候会在Block内部再将weakSelf转为强引用(
__strong typeof(weakSelf) strongSelf = weakSelf;),防止Block执行过程中self被意外释放,保证代码执行的完整性。 - 适用场景:异步执行的Block(比如网络请求回调、延时执行的Block),或者Block会被长期保存、可能被self持有的场景。
你的第一种方法是否有误?
你的第一种方法完全没有错误。
编译器的警告只是提醒你:Block正在隐式持有self,需要你明确确认这是有意行为。你通过显式写self.xxx,相当于告诉编译器“我知道Block在持有self,这是我想要的”,所以消除警告的方式是正确的。再加上你使用的是同步执行的Block,不会造成循环引用,所以这种写法是安全且合理的。
内容的提问来源于stack exchange,提问作者IRP_HANDLER
相关产品推荐
相关产品推荐

