6

問題

いくつかのプロパティを遅延ロードするレガシーコード(iOS 5より前)を移行していreadonlyます。このコードをARCを使用してiOS5以降に更新したいと思います。しかし、私はARCについて学んでいます。

.h

@property (nonatomic, retain, readonly) NSDateFormatter *timeFormatter;

.m

- (NSDateFormatter *)timeFormatter {
    if (timeFormatter == nil) {
        timeFormatter = [[NSDateFormatter alloc] init];
        [timeFormatter setDateFormat:@"h:mm a"];
    }

    return timeFormatter;
}

私が試したこと

コードを更新しようとしましたが、読み取り専用プロパティへの割り当てを受け取りました。

.h

@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;

.m

- (NSDateFormatter *)timeFormatter {
    if (self.timeFormatter == nil) {
        self.timeFormatter = [[NSDateFormatter alloc] init];
        [self.timeFormatter setDateFormat:@"h:mm a"];
    }

    return self.timeFormatter;
}

私もレビューしました:

質問

readonlyARCを使用してiOS5以降でプロパティを遅延ロードする正しい方法は何ですか?.h.mの両方のコードサンプルをいただければ幸いです。

4

2 に答える 2

14

カスタム(レイジー)ゲッターメソッドの場合、インスタンス変数に直接アクセスする必要があります(ARCを使用するかどうかは関係ありません)。したがって、プロパティを次のように合成する必要があります

@synthesize timeFormatter = _timeFormatter;

次に、ゲッターメソッドは

- (NSDateFormatter *)timeFormatter {
    if (_timeFormatter == nil) {
        _timeFormatter = [[NSDateFormatter alloc] init];
        [_timeFormatter setDateFormat:@"h:mm a"];
    }

    return _timeFormatter;
}

プロパティが複数のスレッドから同時にアクセスされる場合、つまりARCに依存しない場合にのみ、同期メカニズムを追加する必要があります。

注:新しいバージョンのXcodeは、@synthesizeステートメントを自動的に作成し、インスタンス変数にアンダースコアプレフィックスを使用できます。ただし、この場合、プロパティは読み取り専用であり、getterメソッドを提供するため、Xcodeはプロパティを自動的に合成しません。)

追加:便利な完全なコード例を次に示します。

MyClass.h:

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;
@end

MyClass.m:

#import "MyClass.h"

@implementation MyClass
@synthesize timeFormatter = _timeFormatter;

- (NSDateFormatter *)timeFormatter {
    if (_timeFormatter == nil) {
        _timeFormatter = [[NSDateFormatter alloc] init];
        [_timeFormatter setDateFormat:@"h:mm a"];
    }

    return _timeFormatter;
}

@end

詳細:timeFormatter実際、プロパティが次のように合成されている場合、ARC以前のゲッターメソッドはARCでも変更なしで機能します。

@synthesize timeFormatter; // or: @synthesize timeFormatter = timeFormatter;

あなたが犯した唯一の「間違い」は、getterメソッドの内部で置き換えることでしtimeFormatterself.timeFormatter。これにより、2つの問題が発生します。

  • self.timeFormattergetterメソッド内を読み取ると、無限再帰が発生します。
  • self.timeFormatter読み取り専用属性のため、設定はできません。

したがって、timeFormatter(メソッド内のインスタンス変数を使用して)getterメソッドをそのままにしておくtimeFormatterと、ARCでも機能します。

Xcodeは自動的に合成されたプロパティに対して同じ方法を実行するため、コード例のように、プロパティのインスタンス変数の前にアンダースコアを付けることをお勧めします。

(これが混乱を助長し、増加させないことを願っています!)

于 2012-12-02T15:53:33.217 に答える
7

読み取り専用プロパティは、読み取り専用です。セッターが関与してはいけません。良い部分は、クラス拡張で変数を再宣言する場合(通常は空の括弧のペアを使用)、readwriteとして(または単にreadonlyを完全に削除することもできます)、.m内で変数に割り当てることができますが、インポートすると、読み取り専用として表示されます。

@interface MyClass ()

@property (nonatomic, strong) NSDateFormatter *timeFormatter;

@end

この再宣言により、脆弱なiVar合成に頼ることなく、プロパティに内部的にアクセスして変更するためのよりクリーンな方法が可能になります(これは、コンパイラーが自動的に行うようになったため、古くなりつつあります)。もちろん、他の回答に示されているようにiVarを使用することもできますが、-initまたは合成ゲッターの外部でiVarにアクセスする必要はありません。*

* Martinが正しく指摘したように、割り当てが成功した場合でも、無限再帰が発生するため、ゲッターを明示的に宣言しない限り、iVarアクセス​​が必要です。プロパティアクセスを使用できます。

于 2012-12-02T15:39:21.917 に答える