CF_RETURNS_RETAINED可用于桥接至Swift的C函数指针返回值吗?
Swift 与 Core Foundation 函数指针桥接内存管理方案
普通C函数桥接规则
若定义返回CFType类型的C函数,Swift可自动桥接其内存管理语义,若无法确定语义则会返回Unmanaged类型:
- 未加注解的Objective-C函数定义:
CFStringRef CreateStringByAddingTwoStrings(CFStringRef s1, CFStringRef s2);
桥接后Swift代码为:
public func CreateStringByAddingTwoStrings(_ s1: CFString!, _ s2: CFString!) -> Unmanaged<CFString>!
- 添加
CF_RETURNS_RETAINED注解后:
CFStringRef CreateStringByAddingTwoStrings(CFStringRef s1, CFStringRef s2) CF_RETURNS_RETAINED;
桥接后Swift代码为:
public func CreateStringByAddingTwoStrings(_ s1: CFString!, _ s2: CFString!) -> CFString!
- 也可使用
CF_IMPLICIT_BRIDGING_ENABLED宏包裹函数声明实现同等效果:
CF_IMPLICIT_BRIDGING_ENABLED CFStringRef CreateStringByAddingTwoStrings(CFStringRef s1, CFStringRef s2); CF_IMPLICIT_BRIDGING_DISABLED
函数指针桥接的问题
上述注解和宏无法直接作用于函数指针类型的声明,例如以下函数指针定义:
typedef CFStringRef (*CreateStringByAddingTwoStrings)(CFStringRef, CFStringRef);
默认桥接后Swift代码为:
public typealias CreateStringByAddingTwoStrings = @convention(c) (CFString?, CFString?) -> Unmanaged<CFString>?
即便给上述typedef添加CF_RETURNS_RETAINED注解或者用CF_IMPLICIT_BRIDGING_ENABLED宏包裹,也不会改变桥接结果。
可行解决方案
目前有两种通用的落地方式处理该问题:
- Swift侧手动处理内存
如果无法修改C侧声明,可在Swift侧调用函数指针后,根据实际内存规则手动调用takeRetainedValue()(对应返回+1引用计数的对象)或takeUnretainedValue()(对应返回+0引用计数的对象)获取自动管理的CF对象:
let createFunc: CreateStringByAddingTwoStrings = 获取函数指针实现 let result = createFunc(str1, str2)?.takeRetainedValue()
- C侧新增包装函数
如果可以修改C侧代码,最优方案是额外定义一层普通C函数包裹函数指针调用,给外层函数添加CF_RETURNS_RETAINED注解即可让Swift正确桥接:
// 原有函数指针定义 typedef CFStringRef (*CreateStringByAddingTwoStrings)(CFStringRef, CFStringRef); CreateStringByAddingTwoStrings createFuncImpl; // 新增包装函数 CFStringRef WrappedCreateString(CFStringRef s1, CFStringRef s2) CF_RETURNS_RETAINED { return createFuncImpl(s1, s2); }
内容的提问来源于stack exchange,提问作者vanvoorden
相关产品推荐
相关产品推荐

