Fork me on GitHub

プロパティとインスタンス変数(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は宣言しない

そのインスタンス変数が本当に必要なのか、ローカル変数で間に合わないかを検討しましょう。