強火で進め

このブログではプログラム関連の記事を中心に書いてます。

Objective-Cの @property と @synthesize の組み合わせが何をやっているのかを解説

改めて説明するまでも無いかと思ってたけど意外と知らない人は知らないみたいので解説を書いておきます。

@property と @synthesize の組み合わせは端的に言うと「setterとgetter(アクセッサメソッド)をコンパイルの前に生成させる」以上。

Objective-Cの場合、 @ で始まるものはプログラムでは無く、コンパイラへの指示となります。この様な指示をObjective-Cではコンパイラディレクティブ(Compiler directive)と呼びます。

詳細説明

setterやgetterはメンバ変数を直接扱わずにメソッド経由で代入やデータの取得をする為に定義するメソッドです。setterがデータを代入する時のメソッド、getterがデータを取得する時のメソッドとなります。

メンバ変数をメソッド経由にすることにより代入前にチェックを行って有効な値のときのみメンバ変数に代入したり、そのメンバ変数の値が想定外の場合の値だった場合にその値が代入された箇所を簡単に特定でき、デバックが容易になったりする利点があります。

他の言語だと自分でそれぞれのメソッドを記述する必要が有ったりするものも有りますがObjective-Cでは @property と @synthesize を定義する事によりコンパイルの前にsetterやgetterのメソッドが追加されます。

例えば .h ファイルで以下の様に NSString *test_; とプロパティ宣言 @property (nonatomic, retain) IBOutlet NSString *test; が記述されていて

@interface TestAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    TestViewController *viewController;
    NSString *test_;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet TestViewController *viewController;
@property (nonatomic, retain) IBOutlet NSString *test;

.m ファイルで以下の様に @synthesize が記述されていると

@synthesize test=test_;

以下の様なメソッドが自動的に追加されます。
※メソッドの内容は予想です。しかし、恐らくこれに近い内容だと思われます。

- (NSString*)test {
    return test_;
}

- (void)setTest:(NSString*)newTest {
    if (test_ != newTest) {
        [test_ release];
        test_ = [newTest retain];
    }
}

setter、getterが追加されている事を確認

setter、getterが追加されている事は以下の様なプログラムを作成し、 @synthesize test=test_; にブレークポイントを設定して実行すると確認出来ます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    self.test = @"test";
    NSLog(@"%@ word.", self.test);

    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}


最初に以下の部分で呼ばれます。

self.test = @"test";

スタックトレースを確認すると [TestAppDelegate application:didFinishLaunchingWithOptions:] から [TestAppDelegate setTest:] が呼ばれているのが確認できるかと思います。

同様に以下の部分で止まった時に

NSLog(@"%@ word.", self.test);

スタックトレースを確認すると [TestAppDelegate application:didFinishLaunchingWithOptions:] から [TestAppDelegate test] が呼ばれているのが確認できるかと思います。

ちなみに以下の様な記述に変えるとプロパティ呼び出しでは無く、インスタンス変数へのアクセスとなるためブレークポイントで止まる事は無くなります。

NSLog(@"%@ word.", test_);

もちろん、同様に以下の記述でも止まりません。

NSLog(@"%@ word.", self->test_);

デフォルトのsetter、getterの定義を自分で実装する

コンパイラが作成するはずのsetter、getterと同じメソッド名のメソッドを記述する自分で実装を記述する事が可能です。以下の様に記述するとプロパティ(test)にアクセスした時にここで記述したメソッドが呼ばれます。

- (NSString*)testData {
    NSLog(@"testData");
    return test_;
}

- (void)setTestData:(NSString*)newTest {
    NSLog(@"setTestData");
    test_ = newTest;
}

setter、getterのメソッド名を自分の指定した名称にする。

ちなみに自身でsetter、getter定義する場合の名称は自分で指定する事も出来ます。
その場合、.h ファイルで以下の様にpropertyを宣言し、

@property (nonatomic, retain, setter=setTestData, getter=testData) IBOutlet NSString *test;

.m ファイルに以下の様にsetter、getterを記述します。

- (NSString*)testData {
    NSLog(@"testData");
    return test_;
}

- (void)setTestData:(NSString*)newTest {
    NSLog(@"setTestData");
    test_ = newTest;
}

今回のサンプルはこちらからDL出来ます。

関連サイト

Apple公式の日本語ドキュメントの「Objective-C 2.0 プログラミング言語

iOS Reference Library
http://developer.apple.com/jp/devcenter/ios/library/japanese.html

Objective-C 2.0プログラミング言語: プロパティの宣言と実装
http://developer.apple.com/jp/documentation/cocoa/conceptual/objectivec/Articles/chapter_5_section_3.html#//apple_ref/doc/uid/TP30001163-CH17-SW12

詳解 Objective-C 2.0

詳解 Objective-C 2.0

Dynamic Objective-C

Dynamic Objective-C