12

プロジェクトを ARC を使用するように変換中です。自動解放された CGColor 表現を返すメソッドを持つ NSColor のカテゴリがあります。

@implementation NSColor (MyCategory)

- (CGColorRef)CGColor
{
    NSColor *colorRGB = [self colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
    CGFloat components[4];
    [colorRGB getRed:&components[0]
               green:&components[1]
                blue:&components[2]
               alpha:&components[3]];
    CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CGColorRef theColor = CGColorCreate(space, components);
    CGColorSpaceRelease(space);
    return (CGColorRef)[(id)theColor autorelease];
}

@end

ARCでこれを行う正しい方法は何ですか? 保持された CGColor を返したくありません。

XCodeのARCコンバーターは、使用を提案しています

return (CGColorRef)[(__bridge id)theColor autorelease];

ただし、次のエラー メッセージが表示されます。

[リライタ] 「autorelease」メッセージの結果を「CGColorRef」にキャストするのは安全ではありません。__bridge キャストは、破棄されたオブジェクトへのポインターになる可能性があり、__bridge_retained はオブジェクトをリークする可能性があります

4

5 に答える 5

8

本質的には、ARCで次のコードを変換する良い方法がないためです。

CGColorRef a = ...;
id b = [(id)a autorelease];
CGColorRef c = (CGColorRef)b;
// do stuff with c

コンバーターは-autoreleaseいくつかのブリッジキャストを削除して追加しますが、スタックします:

CGColorRef a = ...;
id b = (__bridge_transfer id)a;
CGColorRef c = (__bridge_SOMETHING CGColorRef)b;
// do stuff with c. Except the compiler sees that b is no longer being used!

しかし、移民は何のために何を選ぶべき__bridge_SOMETHINGでしょうか?

  • を選択した場合、コンパイラはすぐに解放できるように__bridgebは使用されなくなります。これはクラッシュします。
  • を選択する__bridge_retainedと、所有権は「CF-land」に戻されますが、元のコードでは、オブジェクトが自動解放プールによって所有されると想定されていました。コードがリークするようになりました。

問題は、ARCが呼び出しを禁止して-autoreleaseいるが、オブジェクトが自動解放プールに追加されることを保証する文書化されたメソッドがないことです。これを行う唯一の理由は、自動解放されたCFタイプをメソッドから返すためですが、多くのUIKitクラスにはCFがあります。 -型付きプロパティ(および自動解放された値を返す必要MKOverlayPathViewがあるアトミック CGPathRefプロパティがあります)。

これは、私が本当に文書化したいと思っているARCのトリッキーな部分の1つです。

さまざまな成功の度合いで機能する可能性のある、ジャンプできるフープがいくつかあります。不快感を増すために:

  1. CFAutorelease()ARCなしでコンパイルされたファイルで関数を定義します(-fno-objc-arcターゲット設定→ビルドフェーズ→コンパイルソースのコンパイラフラグに追加します)。これは読者の練習問題として残しておきます。これは、ARCコードがMRCコードと相互運用する必要があるために機能します。これはおそらく最もクリーンなソリューションです。(これは、CFプレフィックスを使用すべきではないというコメントを引き付けることになりますが、リンクエラーが表示されない限り、「2レベルの名前空間」が導入されているため、Cシンボル名の衝突は一般的に安全です。 10.3かそこらで。)

  2. -autoreleaseメッセージまたは同等のものを送信するためのさまざまなフープ。これらはすべて、「だましている」ARCに依存しているため、少し厄介です。ただし、最後のidABIとの互換性があると想定しているものを除きvoid*ます。また、クラス/セレクターを検索する必要があるため、おそらく上記よりも低速です(高速objc_lookUpClass()sel_registerName()あるか、最適化されている可能性もありますが、私はそれには賭けません)。

    return (__bridge CGColorRef)[(__bridge id)theColor performSelector:NSSelectorFromString(@"autorelease")]
    
    [NSClassFromString(@"NSAutoreleasePool") addObject:(__bridge id)theColor]
    return theColor;
    
    return (__bridge CGColorRef)((id(*)(id,SEL))objc_msgSend)((__bridge id)theColor,NSSelectorFromString(@"autorelease"));
    
    return ((void*(*)(void*,SEL))objc_msgSend)(theColor,NSSelectorFromString(@"autorelease"));
    
  3. __autoreleasingコンパイラーが最適化できない変数に割り当てることにより、自動解放プールに追加するように強制します。これが保証されているかどうかはわかりません(特に、似たようなものobjc_autoreleaseReturnValue()objc_retainAutoreleasedReturnValue()可能かもしれませんが、の一般的なケースが遅くなるため、これはありそうもないと思います(NSError * __autoreleasing *)error)。

    -(id)forceAutorelease:(id)o into:(id __autoreleasing*)p
    {
      *p = o;
      return p;
    }
    
    -(CGColorRef)CGColor
    {
      ...
      CGColorRef theColor = CGColorCreate(...);
      CGColorSpaceRelease(space);
      id __autoreleasing temp;
      return (__bridge CGColorRef)[self forceAutorelease:(__bridge_transfer id)theColor into:&temp];
    }
    

    (コンパイラー/ランタイムが協力して、関連するメソッドがオーバーライドされるまで静的ディスパッチ/インライン化を使用することも可能かもしれませんが、それはトリッキーであり、それ自体に大きなオーバーヘッドがないわけではありません。)

  4. を使用typedef__attribute__((NSObject))ます。これは、 ARC仕様の中で最も紛らわしい文書化された部分ですが、次のようなものが機能しているようです

    typedef CGColorRef MyCGColorRef __attribute__((NSObject));
    -(MyCGColorRef)CGColor
    {
      ...
      return (__bridge MyCGColorRef)(__bridge_transfer id)theColor;  
    }
    

    これを機能させるには、2つのブリッジが必要だと思います(1つはARCに所有権を譲渡し、もう1つはに所有権を譲渡します)。あなたが単にreturn theColor;それが漏れているのではないかと思うなら。私がドキュメントを読ん(__bridge_transfer MyCGColorRef)ところ、非ARCポインター(CGColorRef)からARCポインター(MyCGColorRef)に変換されているため、必要なだけですが、コンパイラーは文句を言います。__attribute__((NSObject))残念ながら、ドキュメントにはtypedefの使用方法の例は示されていません。

    ヘッダーの戻りタイプを変更する必要はないことに注意してください。そうすることで、自動リリースされた戻り値の最適化が有効になる場合がありますが、コンパイラがMyCGColorRefからCGColorRefへの変換をどのように処理するかはわかりません。ルため息。

于 2012-08-14T19:51:33.907 に答える
7

CGColorCoreFoundationオブジェクトです。一緒に使用autoreleaseしないでください。代わりに、メソッドの名前を変更してcopyCGColor、保持されているオブジェクトを返す必要があります。

自動リリースはObjective-Cの概念です。CoreFoundationレベルには存在しません。

CGColorはObjective-Cクラスに無料でブリッジされていないため、自動リリースを試みるのは非常に奇妙です(それが機能する場合でも)。

数年後に更新

CoreFoundationレベルのCFAutorelease()があります(MavericksおよびiOS 7以降で使用可能)。

于 2012-07-24T17:51:43.333 に答える
1

実際、手動のメモリ管理ではretainreleaseおよびautorelease任意のCoreFoundationオブジェクトを使用できます。それらはすべて、少なくとも に無料でブリッジNSObjectされているためです。

ARC は手動メモリ管理の使用を禁止しているため、どうにかしてコンパイラに何をすべきかヒントを伝える必要があります。1 つの方法は、メソッドに名前を付けて- (CGColorRef)copyCGColor;、メソッドが保持カウント +1 のオブジェクトを返すことをコンパイラが認識できるようにすることです。

ただし、私のように、そのようなメソッドにプレーンな「CGColor」を好む場合は__attribute__((cf_returns_retained))、メソッド定義に追加するだけです。

@interface NSColor (MyCategory)

- (CGColorRef)CGColor __attribute__((cf_returns_retained));

@end
于 2013-02-21T08:22:44.950 に答える
0

この場合、__bridge_transferを使用したいと思います。

ドキュメント

于 2012-07-24T17:54:55.293 に答える