浅谈Unmanaged

最近学习Swift内存相关的东西,接触到了Unmanaged这个类型,在网上看了很多文档,个人感觉讲的比较晦涩,所以写下这篇笔记方便自己理解和总结。

Unmanaged解决的是在Swift中调用C函数时,C函数返回CoreFoundation类型的对象内存管理相关的问题,它的出现属于历史原因而作为临时的过渡方案。

在ARC下,所有的OC对象和从OC方法返回的CoreFoundation类型的对象都能由编译器自动管理内存。而通过C语言返回的CoreFoundation类型的对象仍然需要CFRetain、CFRelease来手动管理引用计数,或者桥接到OC对象上。

苹果通过在C函数命名中使用Create\Copy、Get字眼,让调用者明白返回的对象是否被调用者持有。比如我们调用包含Create\Copy的C函数返回的对象,需要我们对其使用CFRelease函数进行手动释放。

CFAttributedStringRef attrStrRef = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"Hello", NULL);
CFRelease(attrStrRef);//需要手动释放

而调用包含Get的C函数返回的对象则不需要手动释放,如果我们需要持有这个返回对象,则需要对其调用CFRetain函数。

Swift只支持ARC,所以没有retain、release这些方法,在Swift和OC混编的时候,编译器也能很好的帮我们自动管理内存,除了上面说的C函数返回CoreFundation对象。

举个例子,我们使用苹果的命名规范声明一个C函数:

//声明
CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2);

//实现
CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2) {
    CFMutableStringRef resultString = CFStringCreateMutableCopy(NULL, 0, s1);
    CFStringAppend(resultString, s2);
    return resultString;
}

然后在Swift中进行调用:


图1

可以看到桥接到Swift中的方法的返回值是一个Unmanaged!类型,为什么会返回这个类型?因为在OC中编译器都不能帮我们自动管理C函数返回的CoreFoundation对象,更别说Swift桥接调用C函数了。所以编译器这时候不知道如何管理这个C函数返回对象的引用计数,在翻译成Swift代码时将其包装成了Unmanaged类型的对象。这个类型现在只关注如下两个方法。

    //都是取值,区别在于
    //这个方法在取值时不会改变引用值的引用计数
    public func takeUnretainedValue() -> Instance
    //这个方法返回引用值并对引用值进行一次release(引用计数减1)
    public func takeRetainedValue() -> Instance

这其实就是将对象的内存管理交给调用者自己处理,相当于在Swift中变相提供了手动release操作。通过调用需要我们手动释放内存的函数而获得的对象时,使用takeRetainedValue进行取值,否则使用takeUnretainedValue,也就是苹果在CoreFoundation中使用Create/Get命名方式的那些函数。

当然我们自己在写这种C函数时要避免在Swift中被包装成Unmanaged类型,可以使用CF_IMPLICIT_BRIDGING_ENABLEDCF_IMPLICIT_BRIDGING_DISABLED这两个宏将函数声明包裹起来,这是告诉clang编译器:不需要为Swift桥接审查和处理这些函数的CoreFoundation类型(即Annotated APIs,向编译器注明该方法可自动管理)

CF_IMPLICIT_BRIDGING_ENABLED

CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2);

CF_IMPLICIT_BRIDGING_DISABLED

再回到Swift调用可以看到返回值不在是Unmanaged类型,而是C函数一样直接返回CFString


图2

现在大部分CoreFoundation的方法都已经面向Swift进行了优化,返回值没有被Unmanaged包装,但还有部分API没有被注明可自动管理,比如AddressBook。

查看AddressBook的方法声明,可以看到很多方法的返回值类型是Unmanaged类型


图3

可能由于苹果来不及对这些API进行处理吧,所以正如文初所说,Umagnage是因为历史原因(Swift 的API桥接OC的API,OC底层又是C的实现)和作为过渡方案的产物。随着Swift的完善,可能会越来越少甚至不会再见到这样的API。自己写的C函数要尽量避免这样的情况。

参考:https://nshipster.cn/unmanaged/

你可能感兴趣的:(浅谈Unmanaged)