プロパティとインスタンス変数(ivar)¶
github/objective-c-conventions をベースとして書かれています。
getter/setter, init, dealloc 以外でivarにアクセスしない¶
インスタンス変数にアクセスする時は、基本的にgetter/setter(アクセッサメソッド、プロパティ)を経由してアクセスすべきです。
インスタンス変数を直接使わない理由としては、 インスタンス変数を直接使う場合は不必要なretain等、参照カウントを操作するコードが必要になり見通しが悪くなる事や、 KVO(Key-Value Observing)が使えない事や、アクセッサメソッドを経由しないため変更に弱い部分があることなどがあげられます。
以下の条件を満たしているならインスタンス変数を直接使っても問題は無いですが、統一性という観点から
インスタンス変数を直接参照するのは -init
と -dealloc
以外では避けるべきです:
1.Is declared in a class using ARC.
2.Is used solely for class implementation (not exposed as part of the class interface).
3.Does not require any KVO.
4.Does not require any custom getter/setter.
http://stackoverflow.com/questions/7836182/property-vs-ivar-in-times-of-arc
逆に -init
と -dealloc
ではインスタンス変数を参照するべき理由は、
下記の記事に詳細に書かれています。
単純にまとめると、プロパティでアクセスすためには self
が存在していないといけないため、
その self
が初期化(init)、破棄(dealloc)されているかを -init
と -dealloc
内では気をつけないといけません。
そのため、-init
と -dealloc
内では プロパティを使わない とすることで単純化できるため、そういう習慣としています。
外から書き込む必要のないプロパティはreadonly属性にする¶
Objective-C のプロパティにはreadonly属性が指定できます。 そのプロパティのスコープを小さくするために、外から書き込みが必要のないプロパティにはreadonly属性を指定しましょう。
/Code/ios-practice/ReadOnly.h
#import <Foundation/Foundation.h>
@interface ReadOnly : NSObject {
}
@property(nonatomic, strong, readonly) NSArray *array;
@end
/Code/ios-practice/ReadOnly.m
#import "ReadOnly.h"
@implementation ReadOnly {
@private
NSArray *_array;
}
@synthesize array = _array;
- (id)init {
self = [super init];
if (!self){
return nil;
}
// initで一回のみ初期化
_array = [NSArray arrayWithObjects:@"readonly", @"first", @"init", nil];
return self;
}
@end
また、readonly属性を指定すると、そのクラス内部からも self.array = @[];
のような代入はできなくなりますが、
クラスエクステンションを使いプライベートカテゴリ内で、プロパティに readwrite
を付けることで、
外からはreadonlyだけど、中からはreadwriteのプロパティを作ることができます。
インスタンス変数には接頭辞に_を付ける¶
インスタンス変数とプロパティの名前が被らないようにするため、頭か末尾に_(アンダーバー)を付けると思いますが、 基本的には統一されていることが大事なのでどちらでも構いませんが、 最近のAppleのドキュメントでは接頭辞に_を付けることを推奨しているため、新規に書くコード等はこの作法に則った方が良いでしょう。
インスタンス変数とプロパティのまとめ¶
ここまでをまとめるてみると
/Code/ios-practice/Property.h
#import <Foundation/Foundation.h>
@interface Property : NSObject
@property(nonatomic, strong, readonly) NSArray *array;// 必須なのはここのみ
@end
/Code/ios-practice/Property.m
#import "Property.h"
@interface Property ()
@property(nonatomic, strong, readwrite) NSArray *array;// readwriteを付ける
// 外から見えないようにするなら@interfaceのプロパティを消す
@end
@implementation Property {
// @implementにインスタンス変数が書けるようになったのはXcode 4.2から
@private
NSArray *_array;// Xcode 4.4からここは省略してもいい
}
@synthesize array = _array;// XCode4.5からはここも省略できる
- (id)init {
self = [super init];
if (!self){
return nil;
}
// 初期化
_array = [NSArray arrayWithObjects:@"a",@"b", nil];
return self;
}
@end
Xcodeのバージョン(Clang)が上がるに連れて省略出来る箇所が増えてきているので、 一般に理解できるであろう最低限の記述を考えながら書いていくとよい。
Note
January 22, 2016 現在では、@interface の @property 宣言だけでもコンパイルは問題なく通る。 どこまで省略するかは周りのチームの人に合わせて行うのがよいと思われる。
不必要なivarは宣言しない¶
そのインスタンス変数が本当に必要なのか、ローカル変数で間に合わないかを検討しましょう。