如何将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
相关产品推荐
相关产品推荐

