最近、友人の 1 人から、ARC の下でアクティブになった新しいブリッジ修飾子について尋ねられました。彼は私に、特定の時間にどれを使うべきか、そして異なる __bridge 修飾子の違いを知っているかどうか尋ねました。彼は私に尋ねました。
注: これは「知識を共有する」タイプの質問であるはずで、自分で質問に答えましたが、適切に設定したかどうかわかりません。
最近、友人の 1 人から、ARC の下でアクティブになった新しいブリッジ修飾子について尋ねられました。彼は私に、特定の時間にどれを使うべきか、そして異なる __bridge 修飾子の違いを知っているかどうか尋ねました。彼は私に尋ねました。
注: これは「知識を共有する」タイプの質問であるはずで、自分で質問に答えましたが、適切に設定したかどうかわかりません。
私はそれらが何であり、どのように機能するかをつい最近知ったので、無料の橋渡しが行われていたという事実のために混乱の原因となる可能性がある ARC の下での __bridge 修飾子について知りたい他の人と共有したいと思います。シンプルなキャストで。
以前は、何らかの目的で NSArray オブジェクトを CFArrayRef にキャストしたい場合は、次のような単純なキャストで実行できました。
NSArray* myArray = [NSArray alloc]initWithObjects:....]; //insert objects
CFArrayRef arrayRef = (CFArrayRef) myArray;
この場合、おそらく色の NSArray があり、それを CoreGraphics で使用してグラデーションを描画するために CFArrayRef にキャストする必要がありました。
ただし、ARC では、これは機能しなくなり、次のエラーが表示されます。
いったいこれはどういう意味ですか!?
結局のところ、この種のキャストを行うと、ARC はそれを好まず、いくつかの「修正」ソリューションを提供することさえあります。これらのソリューションにはすべて、同じ__bridgeキーワードが含まれているようです。
ARC では、主な __bridge 修飾子が 3 つあります。
__橋
__bridge_retained (これはCFBridgingRetain関数と提携しています)
__bridge_transfer ( CFBridgingRelease関数と連携)
それでは、__bridge から始めましょう。それは何ですか?__bridge は別の言い方です。コンパイラは喜んでそうし、好みのキャストされたオブジェクトを返します!
ただし、そのような「自由に」キャストされたオブジェクトが必要なため、最初に割り当てられたオブジェクトのメモリを解放する責任は引き続きあります。この場合、私がこれを行うとしたら:
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) 概念全体が 1 つの関数に単純化されているだけです。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: 配列の ownsership を arrayRef オブジェクトに渡しました
// ステップ 3 に進む前に、この時点で arrayRef が所有者であることを理解しましょう
ステップ 3: arrayRef によって所有されていた所有権を NSArray* に再転送します。
この時点で、otherArray ポインターが所有者であるため、完了したら [otherArray を解放] と言うのがこの時点で自然に思えますよね? ここで ARC が起動し、そのアレイを解放します。
そして、あなたはそれが涼しくなることを知っていましたか?この __bridge 修飾子の犯罪における素晴らしいパートナー: CFBridgingRelease()
それをずっと涼しくします!CFBridgingRelease には、次の関数プロトタイプがあります。
id CFBridgingRelease(CFTypeRef x);
これは、__bridge_transfer でキャストした場合とまったく同じです。そして、この関数は所有権も NS オブジェクトに移します! これはただ素晴らしいです!
CFBridgingXXX 関数を使用すると、最初は少し意味がわかるかもしれません。これは、多くの客観的な c プログラマーがまだ NARC ルールの概念を持っているためです。
以下で呼び出されたものすべて:
新しい_
割り当て_
保持_
コピー_
バランシング -release 呼び出しが必要です
これを行う:
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 ポインターであるとしましょう。したがって、あなたは、nil ポインタとして、電気を消したバスルームに立っていると想像できます。(消灯したライトは、何も指していないことを表します)。
その後、コードの後半で、プログラマーがユーザーを新しい 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 に、すべての追加のヘルプと提案に感謝します!