強火で進め このページをアンテナに追加 RSSフィード

整理された情報は こちら へどうぞ。

2010年11月01日

[]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;
}

f:id:nakamura001:20101102021418p:image

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

self.test = @"test";

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

f:id:nakamura001:20101102021456p:image

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

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

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

f:id:nakamura001:20101102021508p:image

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

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

たけぽんたけぽん 2010/11/02 09:26 追加されるであろうコードの部分ですが、testのpropertyにretainが指定されてるから恐らく実際のコードとしては
古い値をreleaseしてから新しい値をretainして代入、というコードになりますね。

NyohoNyoho 2010/11/02 10:37 setter は本文のコードではリークしますよね。
たけぽんさんの「古い値を release してから新しい値を retain して代入」ではなく、「新しい値を retain してから古い値を release」としないとクラッシュしてしまう気がします。あるいは古い値を autorelease してから 新しい値を retain して代入ですかね。

nakamura001nakamura001 2010/11/02 12:42 たけぽんさん、Nyohoさんコメントありがとうございます。

retainを指定してるのにあの記述じゃ不味かったですね。以下のURLの「パフォーマンスとスレッド」の項目のretainの部分を参考に記述を変更しました。ご指摘ありがとうございました。

http://developer.apple.com/jp/documentation/cocoa/conceptual/objectivec/Articles/chapter_5_section_3.html#//apple_ref/doc/uid/TP30001163-CH17-SW12

たけぽんたけぽん 2010/11/02 13:04 一瞬そうかなと思ったけどnonatomic指定なので先にreleaseでも関係無いですね。
デバッガでステップ実行すればコード見れないかな…?

投稿したコメントは管理者が承認するまで公開されません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。

リンク元