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

如何将ObjC Block作为CGDataProviderCreateWithData的releaseData回调?

解决CGDataProviderCreateWithData回调类型不匹配的问题

这个问题我之前也碰到过,核心原因是你混淆了Objective-C Block和C风格函数指针的类型,而且Block的参数签名也和CGDataProviderReleaseDataCallback的要求不匹配,咱们一步步来解决:

先搞懂错误根源

报错提示里明确说了:你传递的Block类型和CGDataProviderReleaseDataCallback不兼容。先看一下这个回调的官方定义:

typedef void (*CGDataProviderReleaseDataCallback)(void * _Nullable info, const void * _Nonnull data, unsigned long size);

这是一个C风格的函数指针,而你直接传了一个Objective-C Block,两者是完全不同的类型,不能直接赋值。另外你的Block参数签名也有问题:第二个参数const void少了指针符号*,而且size_t虽然在多数平台和unsigned long等价,但回调签名必须严格匹配定义。

解决方案一:使用C函数作为回调

这是最直接的方式,因为CGDataProviderCreateWithData本来就设计为接受C函数指针:

// 1. 定义符合要求的C回调函数
void customReleaseData(void *info, const void *data, unsigned long size) {
    // 这里处理资源释放逻辑
    // 如果info是Objective-C对象,需要用__bridge_transfer来转移所有权
    if (info) {
        id obj = (__bridge_transfer id)info;
        // 这里可以对obj做释放相关操作,比如如果是NSData的话不需要额外释放,因为已经转移了所有权
    }
    // 假设data是通过malloc分配的,这里用free释放
    free((void *)data);
}

// 2. 创建CGDataProvider时传入这个C函数
- (void)createDataProvider {
    // 模拟分配数据
    size_t dataSize = 1024;
    void *rawData = malloc(dataSize);
    if (!rawData) {
        return;
    }
    // 填充数据(示例)
    memset(rawData, 0, dataSize);
    
    // 创建DataProvider,info参数可以传需要在回调中处理的对象(如果不需要传NULL)
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rawData, dataSize, customReleaseData);
    
    // 使用provider做后续操作,比如创建CGImage等
    // ...
    
    // 记得用完释放provider
    if (provider) {
        CGDataProviderRelease(provider);
    }
}

解决方案二:用Block封装(如果一定要用Block)

如果想在回调里使用Block的特性(比如捕获外部变量),可以把Block作为info参数传递,然后用一个C函数作为中间层来调用Block:

// 1. 定义中间C函数,用来调用传入的Block
void blockReleaseWrapper(void *info, const void *data, unsigned long size) {
    // 将info转换为Block类型
    void (^releaseBlock)(void *, const void *, unsigned long) = (__bridge void (^)(void *, const void *, unsigned long))info;
    // 调用Block
    releaseBlock(info, data, size);
    // 释放Block(因为之前用CFBridgingRetain保留了)
    CFRelease((__bridge CFTypeRef)releaseBlock);
}

// 2. 使用Block作为回调逻辑
- (void)createDataProviderWithBlock {
    size_t dataSize = 1024;
    void *rawData = malloc(dataSize);
    if (!rawData) {
        return;
    }
    memset(rawData, 0, dataSize);
    
    // 定义释放逻辑的Block,参数签名严格匹配回调要求
    void (^releaseBlock)(void *, const void *, unsigned long) = ^(void *info, const void *data, unsigned long size) {
        free((void *)data);
        // 这里可以访问外部变量,比如self(注意循环引用,必要时用weakSelf)
        NSLog(@"Data released, size: %lu", size);
    };
    
    // 保留Block,因为CGDataProvider会持有info参数,需要确保Block在回调前不被释放
    CFTypeRef retainedBlock = CFBridgingRetain(releaseBlock);
    
    // 创建DataProvider,info传保留后的Block
    CGDataProviderRef provider = CGDataProviderCreateWithData(retainedBlock, rawData, dataSize, blockReleaseWrapper);
    
    // 使用provider...
    
    if (provider) {
        CGDataProviderRelease(provider);
    }
}

关键注意点

  • 内存管理:如果info参数传递的是Objective-C对象或Block,一定要正确处理所有权,避免内存泄漏或野指针。
  • 签名严格匹配:回调的参数类型、数量、顺序必须和CGDataProviderReleaseDataCallback完全一致,哪怕是size_t和unsigned long这种看似等价的类型也不能混用。

内容的提问来源于stack exchange,提问作者Alexander Abakumov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:09:32