「Effective Objective-C 2.0」4、Objective-C類の属性


1、属性——property
Objective-Cの属性はパッケージオブジェクト中のデータに使用され、同じような役割をしているが、役割は普通のデータメンバーよりもはるかに大きい.宣言属性は@propertyキーワードを使用しています.このようにすると、インスタンス変数のためのアクセスを簡単に作成できます.ポイント文法でアクセサを使用することができます.属性の変数値を取得するには、getterメソッドを使用して、属性の変数値を設定します.
2、propertyの使用
属性を使う例は以下の通りです.
//
//  Vehicle.h
//

#import <Foundation/Foundation.h>

@interface Vehicle : NSObject

@property (nonatomic, copy) NSString *carName;
@property (nonatomic, copy) NSString *carType;

@end

//
//  Vehicle.m
//

#import "Vehicle.h"

@implementation Vehicle

@end
//
//  main.m
//

#import <Foundation/Foundation.h>
#import "Vehicle.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Vehicle *obj1 = [[Vehicle alloc] init];
        obj1.carName = @"Ferrari";
        obj1.carType = @"SuperSportCar";
        NSLog(@"Car name is %@ and type is %@", obj1.carName, obj1.carType);
        
        Vehicle *obj2 = [[Vehicle alloc] init];
        [obj2 setCarName:@"BMW X5"];
        [obj2 setCarType:@"SUV"];
        NSLog(@"Car name is %@ and type is %@", obj2.carName, obj2.carType);
    }
    return 0;
}
@propertyは、ヘッダファイルにsetとget方法を宣言するのと同じであり、同時に自動的に実行ファイルにsynthesizeを使用してsetとget方法を実現している.データとメソッドメンバーを手動で宣言することに対して、属性を使うとコードを大幅に簡略化することができます.
3、propertyの特性
属性を宣言する時、nonatomic、copyなどの属性を修飾するキーワードを制定します.属性を修飾するために使われる特性キーワードは、原子性、アクセサ制御、メモリ管理の三つがあります.
(1)、原子性:
  • atomic:この特性修飾を使用した属性はスレッドが互いに反発するもので、つまり最大1スレッドしか同時にアクセスできない.この属性はデフォルトで、スレッドの安全を厳格に要求する場合にのみ使用されます.
  • nonatonic:この特性修飾の属性を使用すると、スレッドが非安全であり、同時に複数のスレッドにアクセスすることができる.nonatonicを使用すると比較的高い属性効率が得られますが、多くの場合にこの特性が使われています.
  • (2)、アクセサ制御:
    主に属性を制御するsetとget方法に用いられる.
  • readwrite:デフォルト属性は、読み操作と書き込み操作を同時にサポートする属性を表しています.
  • readonly:この属性は読み取り専用で、getterだけがsetterがないということを示しています.
  • また、ユーザー定義のsetterとgetterの関数名を指定することもできます.
    @property (nonatoic, setter = mySetter:, getter = myGetter) NSString *name;
    (3)、メモリ管理
    メモリ管理特性は、この属性と保有対象との関係を示しています.
  • assign:値関係のためのデフォルト属性.通常int、floatなどの数値タイプはNSIntegerなどの属性をきわめてパッケージ化されており、エージェントは通常、assign特性を採用している.
  • retain:親オブジェクトは属性オブジェクトに対して所有権を持ち、親オブジェクトが作成された後、属性オブジェクトの参照カウントは1を加算します.親オブジェクトの参照が存在する限り、属性オブジェクトは解放されません.ほとんどの「クラス」タイプの属性については、retain特性を採用するべきである.
  • copy:retainと同様に、親オブジェクトは属性オブジェクトへの強い参照がありますが、親オブジェクトが作成されたときに属性オブジェクトのコピーを作成します.親オブジェクトはこのコピーを参照します.一番多く使われているのはNSStringタイプの属性です.
  • iOSがARCに加入した後、属性のメモリ管理特性に新たにstrongとweakの二つが追加されました.
  • strong:retainと同じで、属性オブジェクトの参照数+1は、属性オブジェクトのライフサイクルが終了する前にリリースされません.
  • weak:親オブジェクトが属性オブジェクトに弱い参照であり、属性オブジェクトの参照数は増加しない.親オブジェクトも属性オブジェクトライフサイクルに干渉できない.属性オブジェクトが他の場所でリリースされた後、親オブジェクトのこの属性はnilに設定される.
  • unsafe_unretain:この属性はassignに類似しており、親オブジェクトは属性オブジェクトに対して強い参照が存在しないと説明していますが、weakとは異なり、属性オブジェクトが解放された場合、親オブジェクトの属性はnilに設定されません.
  • 4、インスタンス変数に直接アクセスするか、それとも属性を通してクラスメンバーにアクセスするか
    この二つの訪問方法にはいくつかの違いがあります.
  • 直接クラスメンバーにアクセスする速度が速いです.Objective-Cの「メソッド割り当て」が省略されているため、コンパイルされたコードは直接にインスタンスメンバーのメモリにアクセスします.
  • 直接クラスのメンバーにアクセスし、そのsetterとgetter方法を迂回します.属性宣言時に規定された特性は無効になります.
  • 直接アクセスクラスのメンバーは、KVOには触れません.
  • オブジェクトの外部からオブジェクトにアクセスする属性のメンバーは、常に属性を使うべきです.オブジェクト内では、できるだけ直接的にインスタンスメンバーにアクセスする必要があります.また、以下の規定に従う必要があります.
  • オブジェクト内部でデータを読み出す時に直接アクセスし、データを書き込む時には属性によって書き込みます.
  • は、initおよびdealloc方法において、常にインスタンス変数に直接アクセスする.
  • は、不活性化が行われた場合、属性によってデータを読み出す.