You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 07:51:24