ARC __bridge修饰符已神秘化 [英] ARC __bridge modifiers demystified

查看:63
本文介绍了ARC __bridge修饰符已神秘化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,我的一个朋友问我有关在ARC下活跃的新桥修改器的问题.他问我是否知道在特定时间使用哪些,以及不同的__bridge修饰符之间的区别是什么.他问我,那么它们如何工作,何时使用它们,如何使用它们,以及它们如何在幕后"工作?"

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

解决方案

由于我最近了解了它们的功能以及它们的工作方式,因此我想与其他任何希望了解ARC下的__bridge修饰符的人分享一下.由于过去免费电话桥接是通过简单的转换来实现的,所以这引起了混乱.

过去,如果您想出于任何目的将NSArray对象强制转换为CFArrayRef,则可以通过以下简单的强制转换来实现:

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

     CFArrayRef arrayRef = (CFArrayRef) myArray;        

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

但是对于ARC,这将不再对您有效,并且您会收到此错误:

这到底是什么意思!?!

事实证明,当您进行这种类型的转换时,ARC并不喜欢它,甚至还会为您提供一些修复"解决方案,这些解决方案似乎都具有相同的 __ bridge 关键字在他们里面,所以让我们开始吧!

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

__ bridge

__ bridge_retained (与 CFBridgingRetain 函数配合使用)

__ bridge_transfer (与 CFBridgingRelease 函数配合使用)

因此,我们将从__bridge开始.它是什么? __bridge只是另一种说法:嘿,编译器,给我我该死的对象!".编译器很乐意这样做,并向您返回您喜欢的强制转换对象!

但是,由于您想要这样的自由"转换对象,因此 YOU 仍然负责释放最初分配的对象的内存.在这种情况下,如果我要这样做:

    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(whatever);

现在让我们进入 __ bridge_retained 及其犯罪伙伴 CFBridgingRetain(). 这个__bridge修饰符专门用于将NS对象的所有权转移到CF对象(因此,由于它是CF类型的对象,因此我们希望手动对其进行CFRelease(无论如何))

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

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

 CFArrayRef arrayRef = (__bridge_retained) myArray;

arrayRef对象现在对过去由myArray指针拥有的内存具有显式所有权.因为现在CF类型拥有所有权,所以我必须使用CFRelease(whatever);自己释放它.

CFBridgingRetain()函数在所有这些混乱中起什么作用?它扮演的角色与我们刚才谈到的演员阵容完全相同!让我们看一下CFBridgingRetain的函数原型:

    CFTypeRef CFBridgingRetain(id x);

我们可以看到,它几乎只是将整个(__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;

那么,我们刚刚编写的这个很棒的小程序到底有什么用?

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

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

///在继续执行第3步之前,让我们了解一下arrayRef是所有者

第3步:我们将原先要由arrayRef拥有的所有权重新转移回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

由于 retain release

相匹配,因此可以简化__bridge演员表的学习过程

如果这一切仍然令人困惑,请这样考虑:您是任何NS对象类型的指针.为了保持一致性,我们假设您是一个NSArray指针,但现在没有指向任何对象.因此,您可以想象一下,作为零指针,您站在关着灯的浴室里. (指示灯熄灭表示您没有指向任何物体.)

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

      you = [[NSArray alloc]init];

突然,您所站在的浴室里的灯打开了!您要指向一个对象!现在,在正常的程序执行中,使用完对象后,就可以释放它.因此,在这种情况下,当您用完浴室后,就关掉灯了.

但是,不幸的是,您所使用的程序不是很正常".程序员决定使用一些CoreFoundation对象!哎呀!

他编写了以下代码行:

    CFArrayRef other = (__bridge_retained CFArrayRef) you;

所以现在发生的是,另一个人在您离开的同一时间走进卫生间.出于礼貌,您不会关闭电灯,因为还有另一个人在使用洗手间,并且有责任在他/她离开时关闭电灯

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

但是如果他/她要写这个怎么办?

   CFArrayRef ref = (__bridge CFArrayRef) you;

这里发生的是,另一个人甚至和你一样都闯进了与你同一个洗手间!真没礼貌!最重要的是,他希望您也能跟着他进行清理!因此,作为绅士/女士,当您结束时,请关掉灯.

但是,由于您是NS类型的对象,因此ARC会为您清理它:)

最后,如果程序员编写此代码怎么办:

    you = (__bridge_transfer NSArray*)arrayRef;

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

适用相同的内存管理规则.由于您接管了拥有"洗手间,因此您必须手动关闭灯.而且因为您是NS类型的对象,所以ARC会再次为您做... :) ARC并非如此美丽!

我知道一开始这似乎有些令人生畏和令人困惑,但是只要按照自己的方式进行操作,再读一遍,您就会发现这种ARC机制的作用是多么不可思议!

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

感谢@rob mayoff和@Krishnabhadra的支持 所有其他帮助和建议!

I was asked recently by one of my friends about the new bridge modifiers that became active under ARC. He asked me if I knew which ones to use in specific times and what the difference between the different __bridge modifiers were. He asked me,"so how do they work, when do I use them, how do I use them , and how do they work "under the hood" "?

Note: This was supposed to be a "Share your knowledge" type of question, where I answered the question myself, but I'm not sure I set it up properly.

解决方案

Because I learned what they were and how they operated just recently, I want to share with anyone else who wishes to learn about the __bridge modifiers under ARC which can be a source of confusion due to the fact that toll-free bridging used to be accomplished with a simple cast.

It used to be that if you wanted to, say, cast your NSArray object to a CFArrayRef for any purpose, it could be done with a simple cast like so:

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

     CFArrayRef arrayRef = (CFArrayRef) myArray;        

In this case, maybe you had an NSArray of colors and needed to cast it to CFArrayRef for using it with CoreGraphics to draw a gradient.

However with ARC, this will no longer work for you and you will receive this error:

Well what the heck does this mean!?!

Well it turns out, ARC doesn't like it when you do this kind of cast and will even give you a few "Fix it" solutions, which all seem to have that same __bridge keyword in them, so let's get right to them!

Under ARC we have 3 main __bridge modifiers:

__bridge

__bridge_retained ( which is partnered by the CFBridgingRetain function)

__bridge_transfer (which is partnered by the CFBridgingRelease function)

So we'll begin with __bridge. What is it? __bridge is just another way of saying: "Hey compiler, just give me my darn casted object!". The compiler will be happy to do so and return to you a casted object of your liking!

However, because you wanted a "freely" casted object like that, YOU are still responsible of releasing the memory for the originally allocated object. In this case, if I were to do this:

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

I am still responsible for releasing the myArray memory because it was the originally allocated object. Remember, __bridge just tells the compiler to perform the cast!! And because I am compiling under ARC, I don't explicitly have to call [-release] on the myArray object, ARC will do it for me!

Note, that the __bridge modifier works both ways! (Hence, toll-free bridging) and you can just as easily cast a CF object to an NS object the same way ( that is toll-free bridgeable!) like so:

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

But since the CF object would be the originally allocated object, I must call CFRelease(whatever);

Now let's move on to __bridge_retained and its partner in crime CFBridgingRetain() . This __bridge modifier is geared EXPLICITLY towards transferring the ownership of an NS object TO A CF OBJECT (So we'll be expecting to manually CFRelease(whatever) this due to it being a CF type object)

Meaning, if I were to do my old scenario again, but this time with __bridge_retained:

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

 CFArrayRef arrayRef = (__bridge_retained) myArray;

the arrayRef object now has explicit ownership of the memory that used to be owned by the myArray pointer. Because now the CF type has ownership, I must release it myself using CFRelease(whatever);

So what role does the CFBridgingRetain() function play in all this chaos? It plays the same exact role as doing the cast we just talked about! Let's take a look at the function prototype for CFBridgingRetain:

    CFTypeRef CFBridgingRetain(id x);

We can see, it pretty much just simplifies the whole (__bridge_retained) notion into one function! We're gettting back a CF object after "inputting" an NS type object! Radical! Yes, I know this is awesome! Too much coolness to take in one sitting! And yes, it also performs the memory "ownership" transfer.. how awesome!

And last, but by no means least, __bridge_transfer and the almighty CFBridgingRelease() !

__bridge_transfer works almost like the opposite of __bridge_retained. The __bridge_transfer modifier transfers the ownership of a CF object type to an NS object type.

So let's refer to the example that's been used throughout this to dissect it:

 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;

So what does this awesome little program that we just wrote exactly do?

Step 1: We allocated an NSArray

Step 2: We passed the ownsership of the array to the arrayRef object

// Before we continue to step 3, let's understand at this point arrayRef is the owner

Step 3: We re-transfer the ownership that USED to be owned by arrayRef back to an NSArray*

Because at this point, the otherArray pointer is the owner, it would seem sort of natural at this point to say [otherArray release] when we're done, right? Well this is where ARC kicks in and will take care of releasing that array for you!

And did you know it gets cooler? This __bridge modifier's awesome partner in crime: CFBridgingRelease()

makes it that much cooler! CFBridgingRelease has this function prototype:

   id CFBridgingRelease(CFTypeRef x);

And we see, this is exactly the same thing that happens when we cast with __bridge_transfer. And this function also transfers the ownership to the NS object! This is just fantastic!

Using the CFBridgingXXX functions maybe can make a little more sense at first, due to the fact that many objective-c programmers still have the notion of the NARC rule:

Everything that has been called with:

N ew

A lloc

R etain

C opy

must have a balancing -release call

So doing this:

     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

Can make the process of learning the __bridge casts easier due to the fact that the retain was matched with a release

If all this still may be confusing, think of it this way: You are a pointer to any NS object type. For the sake of consistency, let's say you're an NSArray pointer, that right now isn't pointing to anything. So you can sort of imagine that you, as the nil pointer, are standing in a bathroom with the lights turned off. ( The lights turned off represents that you aren't pointing to anything).

Then, later on in code, your programmer decides to assign you to a new NSArray. i.e, he/she says this:

      you = [[NSArray alloc]init];

Suddenly, the lights in the bathroom you were standing in, have turned on! You're pointing to an object! Now in normal program execution, when you're done using the object, you release it. So in this case, when you're done using the bathroom, you turn the lights off.

But the program you're in, unfortunately, isn't very "normal". The programmer decided to use some CoreFoundation objects! Bleh!

And he writes this line of code:

    CFArrayRef other = (__bridge_retained CFArrayRef) you;

So now what happens is that, another person walks into the bathroom at the same time you leave. Out of politeness, you don't turn the lights off because there's another person using the restroom and is responsible for turning the lights off when he/she leaves

In this case, because the new owner of the restroom is a CF object, the programmer must manually release it.

But what if he/she were to write this:

   CFArrayRef ref = (__bridge CFArrayRef) you;

what happens here is that, another person just barged into the same restroom as you without even asking! How rude! On top of that he expects you to clean up after him too! So you, being a gentlemen/lady turn off the lights when both of you finish.

However, since you are an NS type object, ARC comes and cleans it for you :)

And finally, what if the programmer writes this:

    you = (__bridge_transfer NSArray*)arrayRef;

What happens here is the exact opposite of the first scenario. Instead of you leaving the restroom at the same time as someone enters, you're the one who enters while the other person leaves

The same memory management rules apply. Since you took over "owning" the restroom, you must manually turn off the lights. And because you're an NS type object, ARC will do it for you... again :) Isn't ARC such a beauty!

I know this may seem a bit intimidating and confusing at first, but just work your way through it, read it again and you'll find out how incredible this ARC mechanism works!

Thanks everyone for reading! Hope this helped :)

Thanks to @rob mayoff and @ Krishnabhadra for all the extra help and suggestions!

这篇关于ARC __bridge修饰符已神秘化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆