ARC __bridge修饰符揭秘

时间:2013-01-08 03:53:36

标签: objective-c automatic-ref-counting

我的一位朋友最近问我关于在ARC下变得活跃的新桥修改器。他问我是否知道在特定时间使用哪些,以及不同的__bridge修饰符之间的区别是什么。他告诉我,“那么它们是如何工作的,我什么时候使用它们,我如何使用它们,以及它们如何在”引擎盖下“工作?”

注意:这应该是一个“分享你的知识”类型的问题,我自己回答了这个问题,但我不确定我是否正确设置了这个问题。

1 个答案:

答案 0 :(得分:55)

因为我了解它们是什么以及它们最近是如何运作的,所以我希望与其他任何希望了解ARC下__bridge修饰符的人分享,因为使用了免费桥接,这可能会造成混乱。用一个简单的演员来完成。

过去,如果您想将NSArray对象强制转换为CFArrayRef用于任何目的,可以使用这样的简单转换来完成:

     NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

     CFArrayRef arrayRef = (CFArrayRef) myArray;        

在这种情况下,也许你有一个颜色的NSArray,并需要将其转换为CFArrayRef,以便与CoreGraphics一起使用它来绘制渐变。

但是使用ARC,这将不再适用于您,您将收到此错误:

enter image description here

这到底意味着什么!?!

事实证明,当你做这种演员表时,ARC并不喜欢它,甚至会给你一些"修复它"解决方案,它们似乎都有相同的 __ bridge 关键字,所以让我们对它们做好准备!

在ARC下,我们有3个主要的__bridge修饰符:

<强> __桥

__ bridge_retained (由 CFBridgingRetain 功能合作)

__ bridge_transfer (由 CFBridgingRelease 功能合作)

所以我们以__bridge开头。它是什么? __bridge只是另一种说法:&#34;嘿编译器,只要给我一个我的darn铸造对象!&#34;。编译器很乐意这样做,并返回给你一个你喜欢的铸造对象!

然而,因为你想要一个自由的&#34;像这样的类似对象,仍然负责释放最初分配的对象的内存。在这种情况下,如果我这样做:

    NSArray* myArray = [NSArray alloc]init];
    CFArrayRef arrayRef = (__bridge CFArrayRef) myArray;

我仍然负责释放 myArray内存,因为它是最初分配的对象。记住,__ bridge只是告诉编译器执行演员!而且因为我在ARC下编译,我没有明确地在myArray对象上调用[-release],ARC会为我做这些!

注意,__ bridge修饰符可以双向工作! (因此,免费桥接)你可以像以下一样轻松地将CF对象强制转换为NS对象(即免费桥接!):

CFArrayRef arrayRef; // allocate this arrayRef and give it a value later on
//... amazing code.....
NSArray* myArray = (__bridge NSArray*)arrayRef;

但由于CF对象是最初分配的对象,我必须调用CFRelease(无论如何);

现在让我们转到 __ bridge_retained 及其犯罪伙伴 CFBridgingRetain()。这个__bridge修饰符很明显地用于转移NS对象到CF OBJECT 的所有权(因此我们预计会因为它是CF类型对象而手动CFRelease(无论如何))

意思是,如果我再次做旧场景,但这次是__bridge_retained:

 NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

 CFArrayRef arrayRef = (__bridge_retained) myArray;

arrayRef对象现在具有显式对myArray指针所拥有的内存的所有权。因为现在CF类型拥有所有权,我必须使用CFRelease(无论如何)自己发布它;

那么 CFBridgingRetain()函数在所有这些混乱中扮演什么角色?它与我们刚刚谈到的演员扮演的角色完全相同!让我们来看看CFBridgingRetain的函数原型:

    CFTypeRef CFBridgingRetain(id x);

我们可以看到,它几乎只是将整个(__bridge_retained)概念简化为一个函数!我们在&#34;输入&#34;之后回收CF对象。一个NS类型的对象!基!是的,我知道这太棒了!一次坐着太酷了!是的,它还执行记忆&#34;所有权&#34;转移..多么棒!

最后,但绝不是最重要的, __ bridge_transfer 和全能的 CFBridgingRelease()

__ bridge_transfer几乎与__bridge_retained相反。 __bridge_transfer修饰符将CF对象类型的所有权转移到NS对象类型。

因此,让我们参考一下在整个过程中使用的示例来剖析它:

 NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects

 CFArrayRef arrayRef = (__bridge_retained) myArray;

 // at this point, arrayRef holds the ownership
 // Let's add this new line to change things up a bit:

 NSArray* otherArray = (__bridge_transfer NSArray*)arrayRef;

那么我们刚写的这个很棒的小程序到底做了什么呢?

第1步:我们分配了一个NSArray

第2步:我们将数组的拥有权传递给了arrayRef对象

//在继续第3步之前,让我们理解此时arrayRef是所有者

步骤3:我们将由ArrayRef拥有的USED所有权重新转移回NSArray *

因为在这一点上,otherArray指针是所有者,此时它似乎有点自然在我们完成时说[otherArray release],对吧?那么这就是ARC开始的地方,并将为您释放该数组!

你知道它变凉了吗?这个__bridge修改者在犯罪方面非常棒的合作伙伴:        CFBridgingRelease()

让它更酷! CFBridgingRelease有这个函数原型:

   id CFBridgingRelease(CFTypeRef x);

我们看到,这与我们使用__bridge_transfer进行转换时发生的情况完全相同。此功能还将所有权转移到NS对象!这太棒了!

使用CFBridgingXXX函数最初可能会更有意义,因为许多Objective-c程序员仍然具有NARC规则的概念:

所有被调用的东西:

N ew

A lloc

R 获得

C opy

必须进行平衡 - 释放调用

这样做:

     NSArray* myArray = [[NSArray alloc]init]; 
                                       // there's the A of NARC! 
                                       //(cleaned by ARC)
     CFArrayRef arrayRef = CFBridgingRetain(myArray); // there's the R of NARC!!
     //NSArray* other = CFBridgingRelease(arrayRef); // cleaned up by ARC

由于保留发布相匹配

,可以让学习__bridge的过程变得更容易

如果所有这些仍然令人困惑,请以这种方式考虑:您是指向任何NS对象类型的指针。为了保持一致性,让我们说你是一个NSArray指针,现在它并没有指向任何东西。所以你可以想象你,作为零指针,你站在浴室里关灯。 (灯关闭表示你没有指向任何东西)。

然后,在代码中,您的程序员决定将您分配给新的NSArray。他/她这样说:

      you = [[NSArray alloc]init];

突然间,你站在浴室里的灯已亮了!你指向一个物体!现在在正常的程序执行中,当您使用该对象完成后,您将释放它。因此,在这种情况下,当您使用浴室时,请关闭灯。

但遗憾的是,您所处的计划并非如此正常。#34;程序员决定使用一些CoreFoundation对象!的Bleh!

他写了这行代码:

    CFArrayRef other = (__bridge_retained CFArrayRef) you;

所以现在发生的是,另一个人在你离开的相同时间走进浴室。出于礼貌,你不能关掉灯,因为那里有另一个人使用洗手间并负责在他/她离开时关灯

在这种情况下,由于洗手间的新所有者是CF对象,程序员必须手动释放它。

但如果他/她写这个怎么办:

   CFArrayRef ref = (__bridge CFArrayRef) you;

这里发生的事情就是,另一个人甚至没有问过就像你一样闯进了同一个洗手间!太粗鲁了!最重要的是,他希望你也能跟上他!因此,当你两个完成时,你是一位绅士/女士会关灯。

但是,由于你是一个NS类型的对象,ARC会为你清理它:)

最后,如果程序员写这个:

    you = (__bridge_transfer NSArray*)arrayRef;

这里发生的事情与第一种情况完全相反。而不是你在某人进入的同时离开洗手间你是那个在另一个人离开时进入的人

适用相同的内存管理规则。自从你接管了#34;拥有&#34;在洗手间,你必须手动关灯。而且因为你是一个NS类型的对象,ARC会为你做这件事......再次:)不是ARC这样的美丽!

我知道这起初可能看起来有些令人生畏和困惑,但是只要按照它去做,再读一遍,你就会发现这个ARC机制有多么令人难以置信!

感谢大家阅读!希望这有帮助:)

感谢@rob mayoff和@ Krishnabhadra  所有额外的帮助和建议!