Objective-C中带completion block的递归函数Release环境报错修复
递归completion block函数Release环境异常修复
核心问题根因
Release和Debug环境表现不一致,本质是原代码同时存在异步时序逻辑错误和block生命周期管理缺失两个问题:
- Debug模式关闭编译器优化、对象内存回收延迟,会暂时掩盖这两个问题;Release模式开启激进优化、对象生命周期严格按引用计数管理,问题会直接暴露。
- 原代码用同步的
__block标记位判断异步回调的执行状态,当前函数执行到末尾判断!hasAlreadyCompleted时,递归调用的子节点异步回调根本还没执行,标记位永远为false,会提前触发completion(nil),后续子节点回调执行时就可能访问已经被释放的对象,触发崩溃。 - 遍历Form类型子节点时,即使子节点拿到了有效图片数据,也没有透传给当前层的completion,找到结果也无法向上层返回。
- 递归调用block时没有做引用管理,Release模式下block捕获的
completion、局部变量被提前回收,触发野指针。
修复方案
你查到的weak-strong写法适用于block内部持有自身的递归场景,你当前是方法递归,除了必要的引用规避循环引用,首先要修正异步时序的逻辑错误,修复后代码如下:
- (void)getBitmapFromXObject:(OCPdfXObject *)xobj completion:(void(^)(NSData * _Nullable data))completion { // 兜底保证completion全局仅调用一次,避免重复回调 __block BOOL hasCalledCompletion = NO; void (^safeCompletion)(NSData * _Nullable) = ^(NSData * _Nullable data) { // 统一回调到主线程,避免线程安全问题,可按需替换为业务需要的队列 dispatch_async(dispatch_get_main_queue(), ^{ if (hasCalledCompletion) return; hasCalledCompletion = YES; completion(data); }); }; // 处理Form类型XObject:遍历子节点查找符合要求的图片 if (xobj.type == OCPdfXObjectType_Form) { OCPdfXObjectForm *formXobj = (OCPdfXObjectForm *)xobj; OCPdfDictionary *res = formXobj.resources.xObject; if (res != nil) { dispatch_group_t childGroup = dispatch_group_create(); __block NSData *validImageData = nil; for (CBKeyValuePair *key in res) { OCPdfXObject *childXObj = [OCPdfXObject createFromObject:key.value]; if (!childXObj) continue; dispatch_group_enter(childGroup); // weak引用self避免循环引用 __weak typeof(self) weakSelf = self; [weakSelf getBitmapFromXObject:childXObj completion:^(NSData * _Nullable data) { if (data != nil && !validImageData) { validImageData = data; } dispatch_group_leave(childGroup); }]; } // 所有子节点遍历完成后再回调结果 dispatch_group_notify(childGroup, dispatch_get_main_queue(), ^{ safeCompletion(validImageData); }); return; } } // 处理Image类型XObject if (xobj.type == OCPdfXObjectType_Image) { OCPdfImage *imageXobj = (OCPdfImage *)xobj; if (imageXobj.colorSpace != OCPdfColorSpace_DeviceRGB) { safeCompletion(nil); return; } [self getBitmapFromImage:imageXobj withCompletion:^(NSData * _Nullable data) { safeCompletion(data); }]; return; } // 其他无有效数据的场景兜底回调 safeCompletion(nil); }
关键说明
- 异步遍历子节点时用
dispatch_group管理所有子回调的时序,等所有子节点执行完成后再统一返回结果,彻底解决原代码同步判断异步状态的逻辑错误。 - 新增
safeCompletion统一管控回调入口,保证任何分支下completion只会被调用一次,避免重复回调导致的野指针、上层逻辑异常。 - 递归调用时对
self使用弱引用,规避block持有self、self持有block导致的循环引用,符合内存管理要求,Release环境下不会出现对象提前释放的问题。 - 如果你的业务要求找到第一个符合要求的图片就终止遍历,可以在拿到
validImageData后加标记跳过后续子节点的处理,减少不必要的计算。
内容的提问来源于stack exchange,提问作者Bellots
相关产品推荐
相关产品推荐

