IBOutletsはARCの下で強いべきか弱いべきか?


551

ARCを使用してiOS 5専用に開発しています。万一IBOutletUIView秒(およびサブクラス)は可能strongweak

以下:

@property (nonatomic, weak) IBOutlet UIButton *button;

これをすべて取り除くでしょう:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

これを行うには問題がありますか?テンプレートはstrong、 'Interface Builder'エディターからヘッダーに直接接続するときに作成される自動生成プロパティと同じように使用していますが、なぜですか?はUIViewControllerすでにそのサブビューを保持strongするへの参照を持ってviewいます。


11
注として、であってはIBOutletCollection()なりませんweak。そうでない場合はとして返されnilます。
ohho 2013

Xcode 8.2.1は、インターフェイスビルダーを介してIBOutletsを作成する場合、weakを使用します。ただし、SOに関するここでの多くの回答は、strongを使用することをお勧めしています。
neoneye 2017

1
@neoneye私はxcode 8.3.2を試してみたところ、ストーリーボードからswiftファイルにドラッグしました。デフォルトはstrong
CupawnTae

回答:


252

Appleが現在推奨しているベストプラクティスは、保持サイクルを回避するために特に弱いものが必要でない限り、IBOutletを強くすることです。ヨハネスが前述したように、これはWWDC 2015の「Interface BuilderでのUI設計の実装」セッションでAppleエンジニアが述べたようにコメントされました:

そして、私が指摘したい最後のオプションは、ストレージのタイプです。これは、強くても弱くてもかまいません。一般的に、コンセントをサブビューまたはビュー階層によって常に保持されるとは限らない制約に接続する場合は特に、コンセントを強くする必要があります。本当にアウトレットを弱くする必要があるのは、ビュー階層のバックアップを参照するカスタムビューがある場合だけで、一般的には推奨されません。

TwitterでIBチームのエンジニアにこれについて尋ねたところ、彼はstrongがデフォルトであり、開発者ドキュメントが更新されていることを確認しました。

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104


33
これは本当に本当ですか、それとも300以上の回答が正解に賛成ですか?ストーリーボードから.hにCtrl
キーを押しながら

4
投票数が400を超えるものは正しいですが、時代遅れです。iOS 6のviewDidUnloadは呼び出されないため、弱いアウトレットを使用してもメリットはありません。
kjam 2016年

7
@kjam利点があります。何よりもまず、自分が作成していないものへの強い参照を保持するべきではありません。第二に、パフォーマンスの向上はごくわずかです。プログラミングのベストプラクティスに違反しないでください。これは、適切に配置された男でさえ、これが10マイクロ秒速いと言っているからです。明確な意図をコード化し、最適化コンパイラをプレイしようとしないでください。特定のケースで問題であると測定された場合のパフォーマンスのコードのみ。
Cameron Lowell Palmer

5
私はあなたに反対します。「自分で作成していないものへの強い参照を保持する」ことは、Objective-Cでは常に発生します。これが、単一の所有者ではなく参照カウントがある理由です。この推奨事項を裏付ける参考資料はありますか?弱いコンセントの他の利点をリストアップできますか?
kjam

4
ここでは、回答に記載されたWWDCのビデオですdeveloper.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn

450

警告、古い回答:この回答はWWDC 2015に従って最新ではありません。正しい回答については、上記の承認された回答(Daniel Hall)を参照してください。この回答は記録に残ります。


開発者ライブラリから要約:

実用的な観点から、iOSおよびOS Xでは、アウトレットを宣言されたプロパティとして定義する必要があります。アウトレットは一般に弱いはずです。ただし、ファイルのオーナーからnibファイル(またはiOSではストーリーボードシーン)の最上位のオブジェクトまで、アウトレットは強いはずです。したがって、作成するアウトレットは通常、デフォルトでは弱くなります。

  • たとえば、ビューコントローラーのビューやウィンドウコントローラーのウィンドウのサブビューに作成するアウトレットは、所有権を意味しないオブジェクト間の任意の参照です。

  • 強力なアウトレットは、フレームワーククラス(UIViewControllerのビューアウトレット、NSWindowControllerのウィンドウアウトレットなど)によって頻繁に指定されます。

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;

10
どのようにして「開発者ライブラリ」リンクを取得して、アップルのドキュメントページの特定の部分にジャンプしましたか?私がアップルのドキュメントにリンクするときはいつでも、それは常にページの上部にリンクします(興味のあるコンテンツがページの半分にある場合でも)。ありがとう。
bearMountain

68
左側のナビゲーションペインからリンクをコピーしました。:D
Alexsander Akers

27
「ファイルの所有者からnibファイル(またはiOSではストーリーボードシーン)の最上位オブジェクトまでのものを除いて」とはどういう意味ですか?
Van Du Tran

16
@VanDuTran-これはルートレベルにあるNIB内のオブジェクトを意味します。つまり、メインビューのサブビューではない別のビューをインスタンス化した場合、強い参照を持つ必要があります。
mattjgalloway 2012年

6
トップレベルは、ペン先を見ると、オブジェクトが左側のリストに表示されることを意味します。ほとんどすべてのnibにはUIViewが含まれています。これが唯一の最上位オブジェクトである可能性があります。他のアイテムを追加し、それらがリストに表示される場合、それらは「トップレベルのオブジェクト」です
David H

50

ドキュメントでweakはサブビューのonプロパティを使用することを推奨していますが、iOS 6以降では、strong代わりに(デフォルトの所有権修飾子)を使用するのが良いようです。これはUIViewController、ビューがアンロードされなくなったことの変更が原因です。

  • iOS 6より前のバージョンでは、コントローラーのビューのサブビューへの強力なリンクを保持している場合、ビューコントローラーのメインビューがアンロードされると、ビューコントローラーがある限り、それらはサブビューに保持されます。
  • iOS 6以降、ビューはアンロードされなくなりましたが、一度ロードされた後、コントローラーが存在する限り保持されます。そのため、強力なプロパティは重要ではありません。また、強参照グラフを下向きにするため、強参照サイクルを作成しません。

とは言っても、私は使用の間に引き裂かれています

@property (nonatomic, weak) IBOutlet UIButton *button;

そして

@property (nonatomic) IBOutlet UIButton *button;

iOS 6以降:

  • を使用するweakと、コントローラーがボタンの所有権を必要としないことが明確に示されます。

  • ただしweak、iOS 6ではビューのアンロードなしで省略しても害はなく、短くなります。これも速いと指摘する人もいますが、weak IBOutletsのせいで遅いアプリにはまだ出会っていません。

  • 使用しweakないことはエラーとして認識される場合があります。

結論:iOS 6以降、ビューのアンロードを使用しない限り、これで問題が発生することはありません。パーティーの時間。;)


それは事実ですが、ビューを自分でアンロードすることもできます。その場合は、すべてのコンセントをnil手動で設定する必要があります。
ハイパークリプト

PS:weakARM64:Dでかなり安くなります
hypercrypt

そうです、ビューのアンロードを実装する場合は、weakプロパティまたは__weakインスタンス変数が適しています。ここではエラーの可能性が少ないことを指摘したかっただけです。用としてweakarm64に安価であること、私もと実際のパフォーマンスの問題を見ていないweak IBOutletのARMv7で秒。:)
Tammo Freese

その場合strongも理にかなっています。strongビューのアンロードを使用する場合にのみ害があります—しかし、最近は誰が行うのですか?:)
Tammo Freese 2014

2
@Rocotilos最初のiPhoneのRAMは非常に限られていた。私が正しく思い出せば、128 MB、アクティブなアプリ用に約10 MBが残ります。メモリフットプリントが小さいことが重要だったため、ビューのアンロードが発生しました。RAMが増え、AppleがiOS 6でUIViewを最適化したため、メモリの警告時にビューをアンロードせずに多くのメモリを解放できるようになったため、状況は変わりました。
Tammo Freese、2016

34

何の問題もありません。ARC以前assignでは、スーパービューによって既に保持されているため、常にIBOutletsを作成してきました。それらを作成する場合weak、指摘するように、viewDidUnloadでそれらをnilする必要はありません。

1つの注意点:ARCプロジェクトでiOS 4.xをサポートできますが、サポートする場合は使用できませんweak。そのため、それらを作成する必要assignがあります。この場合、参照を無効にしviewDidUnloadて、ぶら下がりポインタ。これが私が経験したぶら下がりポインタのバグの例です:

UIViewControllerには、郵便番号用のUITextFieldがあります。CLLocationManagerを使用して、ユーザーの位置を逆ジオコーディングし、郵便番号を設定します。デリゲートコールバックは次のとおりです。

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

このビューを適切なタイミングで閉じ、viewDidUnloadでself.zipをnilにしないと、デリゲートコールバックがself.zip.textに不正なアクセス例外をスローする可能性があることがわかりました。


4
また、weakプロパティをnilledにする必要がないことも理解していますviewDidUnload。しかし、なぜアウトレットを作成するためのAppleのテンプレートには[self setMySubview:nil]
Yang Meyer、

3
IBOutletにstrong / retainedを使用すると問題が発生する可能性のある実際のケースはありますか?それとも単なる冗長な保持ですか?これは悪いコーディングスタイルを意味しますが、コードには影響しませんか?
Enzo Tran 2013年

1
冗長な保持のようなものはありますか?余分な保持がある場合、それは適切にカウントされないため、保持カウントに余分な保持があるため、できるだけ早く解放されません。
karlbecker_com 2014

25

IBOutletパフォーマンス上の理由から、強いはずです。ストーリーボードリファレンス、Strong IBOutlet、iOS 9のScene Dockを参照してください

この段落で説明したように、ビューコントローラーのビューのサブビューへのアウトレットは弱い可能性があります。これらのサブビューは、nibファイルの最上位オブジェクトによってすでに所有されているためです。ただし、アウトレットがウィークポインターとして定義され、ポインターが設定されている場合、ARCはランタイム関数を呼び出します。

id objc_storeWeak(id *object, id value);

これは、オブジェクト値をキーとして使用して、ポインター(オブジェクト)をテーブルに追加します。このテーブルは、ウィークテーブルと呼ばれます。ARCはこのテーブルを使用して、アプリケーションのすべてのウィークポインターを格納します。オブジェクト値が割り当て解除されると、ARCはウィークテーブルを反復処理し、ウィークリファレンスをnilに設定します。または、ARCは以下を呼び出すことができます。

void objc_destroyWeak(id * object)

次に、オブジェクトが登録解除され、objc_destroyWeakが再度呼び出します。

objc_storeWeak(id *object, nil)

弱い参照に関連するこの簿記は、強い参照のリリースに比べて2〜3倍長くかかることがあります。したがって、弱い参照は、アウトレットを強いと定義するだけで回避できるランタイムのオーバーヘッドをもたらします。

Xcode 7の時点で、 strong

WWDC 2015セッション407 Interface BuilderでのUIデザインの実装を見ると、それが示唆されています(http://asciiwwdc.com/2015/sessions/407からのトランスクリプト)

そして、私が指摘したい最後のオプションは、ストレージのタイプです。これは、強くても弱くてもかまいません。

一般に、特にコンセントをサブビューまたはビュー階層によって常に保持されるわけではない制約に接続する場合は、コンセントを強くする必要があります。

本当にアウトレットを弱くする必要があるのは、ビュー階層のバックアップを参照するカスタムビューがある場合だけで、一般的には推奨されません。

だから私は強いを選択するつもりであり、私は私の接続を生成する接続をクリックします。


1
実際の理由を説明する素晴らしい答え
なぜ

それは良いことですが、ストーリーボードに実装されたジェスチャー認識機能からのリークを見たことはあります。
ティボーノア

1
この行は理解できません。「コンセントを弱くする必要があるのは、ビュー階層のバックアップを参照するカスタムビューがある場合のみで、一般的には推奨されません。」例はありますか?
user1872384

私はウィークとストロングがかかるdeinit時間を計算しました、そしてそれはまったく同じです。
touti

しかし、迅速に言えば、これはより当てはまります。弱い参照はより高速です。
thesummersign 19/07/30

20

iOS開発では、NIBロードはMac開発とは少し異なります。

Mac開発では通常、IBOutletは弱い参照です。NSViewControllerのサブクラスがある場合、最上位のビューのみが保持され、コントローラーの割り当てを解除すると、そのすべてのサブビューとアウトレットが自動的に解放されます。

UiViewControllerはキー値コーディングを使用して、強い参照を使用してアウトレットを設定します。したがって、UIViewControllerの割り当てを解除すると、トップビューは自動的に割り当て解除されますが、deallocメソッドですべてのアウトレットの割り当ても解除する必要があります。

Big Nerd Ranchのこの投稿では、このトピックについて説明し、IBOutletで強参照を使用するのが適切でない理由を説明しています(この場合、Appleが推奨している場合でも)。


16
それは2009年のようにそれを説明します。ARCでは、これは大幅に変更されました。
Dafyddウィリアムズ

1
:( Big Nerd Ranchリンクは機能していません...でも、私は本当にそれを読む必要があります。誰でもその投稿の詳細を知っているので、私はそれを見つけることができますか?
Motti Shneor '

@MottiShneorは心配する必要はありません。リンクがARCの前の時代であり、もはや関連がないため、それは大した問題ではありません。
Sergey Grischyov 2014

18

ここで私が指摘したいことの1つは、アップルのエンジニアが自分たちのWWDC 2015のビデオでここに述べたにもかかわらず、

https://developer.apple.com/videos/play/wwdc2015/407/

アップルはこの問題について彼らの考えを変え続けています、それはこの質問に対する単一の正しい答えはないことを私たちに伝えます。Appleのエンジニアでさえこの問題について意見が分かれていることを示すために、Appleの最新のサンプルコードを見てください。弱い人が使用している人と使用していない人がいます。

このApple Payの例では、weakを使用してい ます。

このピクチャーインピクチャーの例と同様に、https//developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID4

リスターの例と同様に:https : //developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

コアロケーションの例と同様に:https : //developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

ビューコントローラーのプレビューの例と同じように:https : //developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsing_elewiElementElement5Link

HomeKitの例と同様:https ://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Homes_Action_Sets_ActionSetViewControllerLinksElement23LinkElementElements

これらはすべてiOS 9用に完全に更新されており、すべて弱いコンセントを使用しています。このことから、Aが問題であることがわかります。この問題は、一部の人が考えているほど単純ではありません。B.アップルは繰り返し考えを変えました、そしてC.あなたは幸せになるものなら何でも使うことができます:)

説明とこの回答の参照を提供してくれたPaul Hudson(www.hackingwithsift.comの作成者)に特に感謝します。

これにより、主題が少し明確になることを願っています!

気を付けて。


私はしばらくの間この問題をチェックしてきましたが、具体的な答えは見つかりませんでした。上記のリンクは、どちらも問題なく、一般にXcodeが自動提案するものと一致することを示しています。
subin272



5

長年の間に何かが変わったように見えますが、今ではAppleは一般的にstrongを使用することを推奨しています。WWDCセッションに関する証拠は、セッション407-Interface BuilderでのUIデザインの実装にあり、32:30に始まります。彼の言ったことからの私のメモは(正確ではないにしても、ほとんど彼を引用している):

  • アウトレット接続は、特にビュー階層によって常に保持されているわけではないサブビューまたは制約を接続する場合は特に、強いはずです。

  • ビュー階層のバックアップに何らかの参照があるカスタムビューを作成する場合、弱いアウトレット接続が必要になる場合があり、一般的には推奨されません

他のワードでは、カスタムビューの一部がビュー階層の一部のビューで保持サイクルを作成しない限り、常に強いはずです。

編集:

一部は質問をするかもしれません。強力な参照でそれを維持しても、ルートビューコントローラーと所有ビューが参照を保持するため、保持サイクルは作成されませんか?それともなぜ変化が起こったのですか?答えは、nibがxibからどのように作成されるかについて説明するときに、この講演の前半にあると思います。VCとビュー用に作成された個別のペン先があります。これが彼らが推奨事項を変更する理由かもしれないと思います。それでも、Appleからより深い説明を得るのは素晴らしいことです。


4

最も重要な情報は次のとおりです。xibの要素は自動的にビューのサブビューにあります。サブビューはNSArrayです。NSArrayはその要素を所有しています。などに強いポインタがあります。したがって、ほとんどの場合、別の強力なポインタ(IBOutlet)を作成する必要はありません。

ARCでは、何もする必要はありません。 viewDidUnload

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.