Edited at

インスタンス変数と@propertyと@synthesizeを理解する

More than 5 years have passed since last update.

インスタンス外からのインスタンス変数へのアクセシビリティの制御に使われる。

手を動かして理解してみる。


@propertyおよび@synthesizeを使わない場合

インスタンス変数を参照または変更する際は、クラス側でアクセッサメソッド(セッター、ゲッター)を実装する必要がある。

まずヘッダ内でインスタンス変数、セッター、ゲッター、あとインスタンス変数を出力するインスタンスメソッドを宣言


Test.h


#import <Foundation/Foundation.h>

@interface Test : NSObject{
int fuga; //インスタンス変数
}
- (void) fuga:(int)i; //整数iをfugaに代入するインスタンスメソッド (セッター)
- (int) fuga; //整数iを取り出すインスタンスメソッド(ゲッター)
- (void) echo; //インスタンス変数を出力するインスタンスメソッド
@end

クラスの実装


Test.c


#import "Test.h"

@implementation Test
- (void)fuga:(int)i{
fuga = i;
}
- (int)fuga{
return fuga;
}
- (void)echo{
printf("%d\n",fuga); //fugaの出力
}
@end


実際に使ってみる。


main.c

#import <Foundation/Foundation.h>

#import "Test.h"

int main(int argc, const char * argv[])
{
@autoreleasepool {
Test *a = [Test new];
[a fuga:4];
printf("%d \n",[a fuga]);// printf("%d \n",a.fuga);でもよい(なぜ?)
[a echo];
}
return 0;
}



2014/08/01追記:

a.fugaは[a fuga]の省略記法と助言いただきました。

プログラミング言語では簡易記法の仕組みをsugar syntaxというらしいです。


結果



4

4

Program ended with exit code: 0



となる。

インスタンス変数には、インスタンス内部からのみアクセスできるというobjectivecの仕様(?)にのっとったコーディングになる。

じゃあ毎回これ書く?となると、めんどくさい。可読性も低い。

インスタンス変数ごとに、アクセス権限を管理したい。そこで登場property。

(ここら辺の感想は適当に書いたので、適宜修正突っ込みねがいます。。)


@propertyおよび@synthesizeを使う場合

propertyを使えば,アクセッサメソッドを宣言、実装しなくても、コンパイル時にobjectivecがアクセッサメソッドを用意してくれる。


Test.h

#import <Foundation/Foundation.h>

@interface Test : NSObject{
int fuga;//インスタンス変数宣言
}
@property int hoge; //プロパティ宣言
- (void) echo;
@end


Test.c

#import "Test.h"

@implementation Test
@synthesize hoge = fuga;//インスタンス外部からfugaへのアクセッサメソッドをプロパティhogeが提供
- (void)echo{
printf("%d\n",fuga);//インスタンス変数内なのでfugaへアクセス可能
}
@end


main.c


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

int main(int argc, const char * argv[])
{
@autoreleasepool {
Test *a = [Test new];
a.hoge = 4; //セッターを利用したインスタンス変数fugaへのアクセス
printf("%d \n",a.hoge); // ゲッターを利用したインスタンス変数fugaへのアクセス
[a echo]; //インスタンスメソッドでのインスタンス変数fuga出力
}
return 0;
}

@property@synthesizeを利用すると、アクセッサメソッドを宣言、実装しなくてすむ。またプロパティにはアクセス権限の設定もできる。でもいつのインスタンス変数を扱うために、2つも変数を宣言するなんてまだ冗長だよね。。。ってことでもっと簡潔な方法が以下です。

(ここも個人的な思いなので突っ込み願います)


2014/08/01追記:

こちらも助言いただきました。


コメントより:

プロパティだけ宣言してインスタンス変数は自動生成にまかせることが推奨されています

(参考:Programming with Objective-C: Encapsulating Data)


なるほど。。。。



@propertyだけ利用

もうアクセッサメソッドの提供と変数の宣言分けんでもええやんってことで@propertyだけ使用


Test.h

#import <Foundation/Foundation.h>


@interface Test : NSObject
@property int fuga;
- (void) echo;
@end


Test.m

#import "Test.h"


@implementation Test
- (void)echo{
printf("%d\n",_fuga); // _プロパティ名でインスタンス変数を参照できる
}
@end


main.c


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

int main(int argc, const char * argv[])
{
@autoreleasepool {
Test *a = [Test new];
a.fuga = 4;
printf("%d \n",a.fuga);
[a echo];
}
return 0;
}


これって@propertyが、コンパイル時にインスタンス変数を作るって仕様なのかなあ。

まだまだ調べる必要があるなあ。


2014/08/01

こちらもいただいた助言を追記します。

ありがとうございます!


コメント:

readwrite が指定された @property に対応する @synthesize がなければ、アンダース

コアつきのインスタンス変数が自動生成されるようになっています(readwrite はデフォルトで指定されます)。 readonly を指定したプロパティでは自動生成されません。 (thx @tomohisaota)



とりあえず今日はここまで。