ARC __bridge修饰符揭秘

Ale*_*dro 31 objective-c automatic-ref-counting

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

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

Ale*_*dro 60

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

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

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

     CFArrayRef arrayRef = (CFArrayRef) myArray;        
Run Code Online (Sandbox Code Playgroud)

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

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

在此输入图像描述

那到底是什么意思啊!?!

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

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

__桥

__bridge_retained (由CFBridgingRetain函数合作)

__bridge_transfer (由CFBridgingRelease函数合作)

所以我们将从__bridge开始.它是什么?__bridge只是另一种说法:"嘿编译器,只要给我一个我的darn casted对象!".编译器很乐意这样做并返回给你一个你喜欢的铸造对象!

但是,因为你想要一个"自由"的类似对象,仍然负责释放最初分配的对象的内存.在这种情况下,如果我这样做:

    NSArray* myArray = [NSArray alloc]init];
    CFArrayRef arrayRef = (__bridge CFArrayRef) myArray;
Run Code Online (Sandbox Code Playgroud)

我仍然负责释放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;
Run Code Online (Sandbox Code Playgroud)

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

现在让我们转到__bridge_retained及其犯罪伙伴CFBridgingRetain().这个__bridge修饰符明确地适用于将NS对象的所有权转移 到CF OBJECT(因此我们将期望手动CFRelease(无论如何),因为它是CF类型对象)

意思是,如果我再次使用旧方案,但这次使用__bridge_retained:

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

 CFArrayRef arrayRef = (__bridge_retained) myArray;
Run Code Online (Sandbox Code Playgroud)

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

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

    CFTypeRef CFBridgingRetain(id x);
Run Code Online (Sandbox Code Playgroud)

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

最后,但绝不是最少,__ 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;
Run Code Online (Sandbox Code Playgroud)

那么我们刚写的这个令人敬畏的小程序到底做了什么呢?

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

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

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

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

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

你知道它变凉了吗?这个__bridge修饰符是犯罪中令人敬畏的伙伴:CFBridgingRelease()

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

   id CFBridgingRelease(CFTypeRef x);
Run Code Online (Sandbox Code Playgroud)

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

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

所有被召唤的东西:

ñ EW

一个 lloc

[R Etain的

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
Run Code Online (Sandbox Code Playgroud)

由于保留发布匹配,可以使学习__bridge的过程变得更容易

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

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

      you = [[NSArray alloc]init];
Run Code Online (Sandbox Code Playgroud)

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

但不幸的是,你所在的节目并不是很"正常".程序员决定使用一些CoreFoundation对象!的Bleh!

他写了这行代码:

    CFArrayRef other = (__bridge_retained CFArrayRef) you;
Run Code Online (Sandbox Code Playgroud)

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

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

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

   CFArrayRef ref = (__bridge CFArrayRef) you;
Run Code Online (Sandbox Code Playgroud)

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

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

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

    you = (__bridge_transfer NSArray*)arrayRef;
Run Code Online (Sandbox Code Playgroud)

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

适用相同的内存管理规则.由于您接管了"拥有"洗手间,您必须手动关闭灯.而且因为你是一个NS类型的对象,ARC会为你做到这一点......再次:)不是ARC这么美!

我知道这可能看起来有点令人生畏和困惑,但是只要按照自己的方式进行操作,再次阅读它,你会发现这个ARC机制有多么令人难以置信!

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

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

  • 不要使用`__bridge_retained`强制转换,而应考虑使用`CFBridgingRetain`宏.同样,考虑使用`CFBridgingRelease`而不是`__bridge_transfer`.为什么?两个原因.首先,宏名称使得更清楚哪一个增加保留计数以及哪一个减少保留计数.其次,它与现有约定相匹配,即保留必须由发布平衡.`CFBridgingRetain`可以平衡`CFRelease`,`CFRetain`(或``Create ...`或`... Copy ...`)可以通过`CFBridgingRelease`来平衡. (2认同)
  • 我认为你可以改进这个答案,特别是因为你花了这么多精力来写这个.把3个桥的情况分开,在一个大胆的标题下,这样它们很容易被注意到.解释CFBridgingRetain和Rob指出的其他变种..好工作.. (2认同)