web-dev-qa-db-ja.com

IBOutletはプロパティであり、合成される必要がありますか?

ほとんどの例では、次のIBOutletsの設定が表示されます。



(Example A)

FooController.h:

@interface FooController : UIViewController {
    UILabel *fooLabel;
}

@property (nonatomic, retain) IBOutlet UILabel *fooLabel;

@end

FooController.m:

@implementation FooController

@synthesize fooLabel;

@end

しかし、これもうまく機能します(注意:プロパティも合成もありません)。



(Example B)

FooController.h:

@interface FooController : UIViewController {
    IBOutlet UILabel *fooLabel;
}

@end

FooController.m:

@implementation FooController

@end

例BのようにIBOutletsを定義することの欠点はありますか?メモリリークのような?正常に動作しているようですが、IBOutletsはそのように使用されないため、パブリックプロパティとして公開しないことをお勧めします。これらはコントローラーの実装でのみ使用されます。実際に必要とせずに3か所​​で定義することは、DRY(Do n't Repeat Yourself))ほど私には影響しません。

53
Ward Bekker

Mac OS Xでは、IBOutletは次のように接続されます。

  1. Set <OutletName>:というメソッドを探します。存在する場合は呼び出します。
  2. メソッドが存在しない場合は、<OutletName>という名前のインスタンス変数を探し、保持せずに設定します。

IPhone OSでは、IBOutletは次のように接続されます。

  1. [object setValue:outletValue forKey:@ "<OutletName>"]を呼び出します

キーの設定値の動作は次のようなことです:

  1. Set <OutletName>:というメソッドを探します。存在する場合は呼び出します。
  2. メソッドが存在しない場合は、<OutletName>という名前のインスタンス変数を探して設定し、retainそれを設定します。

プロパティを使用する場合、両方のプラットフォームで "Look for the method called set <OutletName>:..."ケースに陥ります。インスタンス変数だけを使用する場合、Mac OS X VS iPhone OSでは保持/解放の動作が異なります。インスタンス変数の使用には何の問題もありません。プラットフォームを切り替えるときに、この動作の違いに対処する必要があります。

このトピックに関する完全なドキュメントへのリンクは次のとおりです。 https://developer.Apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//Apple_ref/doc/uid/10000051i-CH4-SW6

96
Jon Hess

Mac OS Xでは、IBOutletsはデフォルトでは保持されません。これはiPhone OSでの動作の逆です。iPhoneOSでは、プロパティを宣言しない場合、プロパティは保持され、deallocメソッドでこのプロパティを解放する必要があります。さらに、64ビットランタイムは、プロパティ宣言を使用してインスタンス変数を合成できます。つまり、いつかインスタンス変数(IBOutlet付き)が省略される可能性があります。

これらの理由により、常にプロパティを作成し、そのプロパティでのみIBOutletを使用する方がより均一で互換性があります。残念ながら、それもより冗長です。

最初の例では、常にdeallocメソッドでアウトレットを解放する必要があります。 2番目の例では、iPhone OSでのみコンセントを解放する必要があります。

12
Freeman

最終結果はまったく同じですが、いくつかの点に留意する必要があります。

  • インスタンスフィールドをアウトレットとして使用する場合、それらをdeallocで解放しないでください。

  • (retain)属性を持つプロパティを使用する場合、プロパティを解放する必要がありますdealloc(usingself.property=nilまたはバッキング変数を解放する)。これにより、何が起こっているのかがより明確になります。

実際、それはすべて同じ古いルールに帰着します:「あなたはあなたが割り当て/保持するものを解放する必要があります」。したがって、インスタンスフィールドをアウトレットとして使用する場合は、それを割り当てたり保持したりしなかったため、解放しないでください。

4

サンプルコードがプログラムでUILabelを割り当てて初期化し、それをUIViewに追加しているため、これらの例では保持を使用する可能性があります。これは多くの例に当てはまります。InterfaceBuilderの使用方法を学ぶことは彼らのポイントではないことが多いからです。

IBOutletを使用した2番目の例(プロパティなし、合成なし)は、開発者がインターフェイスビルダー内でUILabel(ボタン、ビューなど)を「割り当てる」ときに、IBOuletをラベルまたはその他のビューコンポーネントにドラッグすることによって使用されます。私の意見では、前述のドラッグアンドドロップアクション(Label on View)もサブビューを追加し、Labelをビューに追加します。ラベルはビューによって保持されます。ビューはウィンドウによって保持されます。ウィンドウはファイルの所有者によって保持されます。ファイルの所有者は通常、メインで起動されるドキュメントです。

プログラムをステップ実行すると(awakeFromNibを追加して)

- (void)awakeFromNib
{
    [fooLabel blahblah];
}

そのfooLabelにはすでにメモリアドレスがあります。

これは、LabelがinitではなくinitWithCoderを使用してファイルバンドル(nibファイル)から初期化されたためです。これは基本的にファイルストリームをオブジェクトに逆シリアル化し、次にIBOutlet変数を設定します。 (私たちはまだIBOutletメソッドについて話し合っています)。

また、前述のiOSメソッドはKey Valueメソッドを使用することに注意してください。

  call [object setValue:outletValue forKey:@"<OutletName>"]

これは、Observer/Observableパターンです。このパターンでは、ObservableオブジェクトがSet/Array内の各Observerを参照する必要があります。値を変更すると、セット/配列が繰り返され、すべてのオブザーバーが等しく更新されます。そのセットはすでに各オブザーバーを保持するため、iOSでは保持されません。

さらに、残りは推測です。

あなたがインターフェイスビルダーを使用するケースは

 @property (nonatomic, retain) IBOutlet UILabel *fooLabel;

おそらく変更する必要があります

@property (nonatomic, weak) IBOutlet UILabel *fooLabel;

または@property(非アトミック、割り当て)IBOutlet UILabel * fooLabel;

そして、それはdeallocメソッドで解放される必要はありません。さらに、OSXおよびiOSの要件も満たします。

それはロジックに基づいており、ここでいくつか欠けている可能性があります。

それにもかかわらず、ビューがプログラムの存続期間を通して永続的であるかどうかは問題ではない場合があります。一方、モーダルダイアログボックスのラベル(開く、閉じる、開く、閉じる)は、実際には保持されすぎてサイクルごとにリークする可能性があります。そして、それは(再び推測)各閉じたダイアログボックスがファイルシステムにシリアル化され、したがって、X、Yの位置とサイズ、およびそのサブビューなどが永続化されるためです。その後、次のセッションが開いたときに逆シリアル化されます(minimizとは反対)または非表示。)

1
Gabe Rainbow