2008年10月23日木曜日

Objective-C 2.0の'@synthesize'の力 / getter, setterを書かない.

'NSProgressIndicatorのサンプルを作ってみた.'で'getter/setterについては…'と書いたところをObjective-C2.0方向で一応やってみた.
MyObject.h
#import <Cocoa/Cocoa.h>
@interface MyObject : NSObject {
BOOL _animate;
BOOL _indeterminate;
float _progressValue;
NSString *_buttonTitle;
BOOL _buttonEnabled;
NSString *_button2Title;
BOOL _button2Enabled;
}
- (IBAction)start:(id)sender;
- (IBAction)start2:(id)sender;
@property (getter=animate,setter=setAnimate:) BOOL _animate;
@property (getter=indeterminate,setter=setIndeterminate:) BOOL _indeterminate;
@property (getter=progressValue,setter=setProgressValue:) float _progressValue;
@property (retain,setter=setButtonTitle:) NSString *_buttonTitle;
@property (setter=setButtonEnabled:) BOOL _buttonEnabled;
@property (retain,setter=setButton2Title:) NSString *_button2Title;
@property (getter=button2Enabled,setter=setButton2Enabled:) BOOL _button2Enabled;
@end

MyObject.m
#import "MyObject.h"
@implementation MyObject
@synthesize _buttonTitle;
@synthesize _buttonEnabled;
@synthesize _button2Title;
@synthesize _button2Enabled;
@synthesize _animate;
@synthesize _indeterminate;
@synthesize _progressValue;
- (id)init{…}
- (IBAction)start2:(id)sender{…}
- (void)startJob{…}
@end


さてgetter,setter名を@propertyで宣言しないで@synthesizeを使用したらどういう命名規則となるか.基本的に変数名(ここでは'BOOL _animate'を参考)に基づき次のようになる
[self _animate];
[self set_animate:YES];


もしBOOL animate;なら以下らしい.
[self animate];
[self setAnimate:YES];


ということでコードは次のようになる.
MyObject.h
#import <Cocoa/Cocoa.h>
@interface MyObject : NSObject {
BOOL _animate;
BOOL _indeterminate;
float _progressValue;
NSString *_buttonTitle;
BOOL _buttonEnabled;
NSString *_button2Title;
BOOL _button2Enabled;
}
- (IBAction)start:(id)sender;
- (IBAction)start2:(id)sender;
@property BOOL _animate;
@property BOOL _indeterminate;
@property float _progressValue;
@property (retain) NSString *_buttonTitle;
@property BOOL _buttonEnabled;
@property (retain) NSString *_button2Title;
@property BOOL _button2Enabled;
@end

MyObject.m
#import "MyObject.h"
@implementation MyObject
@synthesize _buttonTitle;
@synthesize _buttonEnabled;
@synthesize _button2Title;
@synthesize _button2Enabled;
@synthesize _animate;
@synthesize _indeterminate;
@synthesize _progressValue;
- (id)init
{
if (self= [super init]) {
_animate= NO;
_indeterminate= YES;
_buttonTitle= [NSString stringWithString:@"Start"];
_buttonEnabled= YES;
_button2Title= [NSString stringWithString:@"Start"];
_button2Enabled= YES;
}
return self;
}
- (IBAction)start2:(id)sender
{
[self set_indeterminate:NO];
[NSThread detachNewThreadSelector:@selector(startJob)
toTarget:self withObject:nil];
}
- (void)startJob
{
NSAutoreleasePool* pool;
pool = [[NSAutoreleasePool alloc]init];
//------------------------------------
{
[self set_animate:YES];
[self set_buttonEnabled:NO];
[self set_button2Enabled:NO];
[self set_progressValue:0.0];
}
int i, j, k;
for(i= 0; i<100; i++) {
for(j= 0; j<36000;) {
for(k= 0; k<360;) {
k++;
}
j++;
}
[self set_progressValue:i];
[self set_button2Title:[NSString stringWithFormat:@"%d", i]];
}
{
[self set_progressValue:100.0];
[self set_button2Title:[NSString stringWithString:@"Start"]];
[self set_button2Enabled:YES];
[self set_buttonEnabled:YES];
[self set_animate:NO];
}
//------------------------------------
[pool release];
[NSThread exit];
}
- (IBAction)start:(id)sender
{
if ([self _animate]) {
[self set_button2Enabled:YES];
[self set_animate:NO];
[self set_buttonTitle:[NSString stringWithString:@"Start"]];
}
else {
[self set_button2Enabled:NO];
[self set_indeterminate:YES];
[self set_animate:YES];
[self set_buttonTitle:[NSString stringWithString:@"Stop"]];
}
}
@end


ただこれをやると当然Interface Builderでの定義もself.animateでなくself._animate等にしなくてはいけない.始めからそうすればいいのだが.
(サンプルだとButtonのBindingsでEnabled, Title, Proress IndicatorのBindingsでValue, Animate, Is IndetaminateのModel Key Path値の修正が必要)(注意:コメント参照のこと.(2008/10/28に 匿名さんにご指摘いただいてます.感謝))

もう一つ.こうなるとなにも[self _animate];なんて書かなくてもIBの定義と同じアクセスでよくなる.つまりMyObject.mは以下のように書ける.
MyObject.m
#import "MyObject.h"
@implementation MyObject
@synthesize _buttonTitle;
@synthesize _buttonEnabled;
@synthesize _button2Title;
@synthesize _button2Enabled;
@synthesize _animate;
@synthesize _indeterminate;
@synthesize _progressValue;
- (id)init
{
if (self= [super init]) {
self._animate= NO;
self._indeterminate= YES;
self._buttonTitle= [NSString stringWithString:@"Start"];
self._buttonEnabled= YES;
self._button2Title= [NSString stringWithString:@"Start"];
self._button2Enabled= YES;
}
return self;
}
- (IBAction)start2:(id)sender
{
self._indeterminate= NO;
[NSThread detachNewThreadSelector:@selector(startJob)
toTarget:self withObject:nil];
}
- (void)startJob
{
NSAutoreleasePool* pool;
pool = [[NSAutoreleasePool alloc]init];
//------------------------------------
{
self._animate= YES;
self._buttonEnabled= NO;
self._button2Enabled= NO;
self._progressValue= 0.0;
}
int i, j, k;
for(i= 0; i<100; i++) {
for(j= 0; j<36000;) {
for(k= 0; k<360;) {
k++;
}
j++;
}
self._progressValue= i;
self._button2Title= [NSString stringWithFormat:@"%d", i];
}
{
self._progressValue= 100.0;
self._button2Title= [NSString stringWithString:@"Start"];
self._button2Enabled= YES;
self._buttonEnabled= YES;
self._animate= NO;
}
//------------------------------------
[pool release];
[NSThread exit];


}
- (IBAction)start:(id)sender
{
if (self._animate) {
self._button2Enabled= YES;
self._animate= NO;
self._buttonTitle= [NSString stringWithString:@"Start"];
}
else {
self._button2Enabled= NO;
self._indeterminate= YES;
self._animate= YES;
self._buttonTitle= [NSString stringWithString:@"Stop"];
}
}
@end


この辺りの書き方は2.0環境のみでよいか?とか,[]の方が明示的でよいとか.素直に好みなどなどで変わるな.

間違いありましたらご指摘いただければ幸いです.

4 件のコメント:

匿名 さんのコメント...

@synthesize animate = _animate;

としておけば、

self.animate

でアクセスできますよ。

p_g_ さんのコメント...

匿名さん コメント,ご指摘ありがとうございます.
synthesizeでの別名指定ですね.
なるほどここで使えばよいのか.
 外部(?)アクセス名= 実態(?)名;
って感じで定義するんでしたね.
利便性がよくわかってないで,流していました.
ありがとうございます.
今後ともご指導ご鞭撻いただければ幸いです.

ほんわか さんのコメント...

ダイナミックObjective-C
104 プロパティ(4) - プロパティの宣言
(http://journal.mycom.co.jp/column/objc/104/index.html)
を、読んで思うのですが、最初のサンプルコードの"Mybject.h"に
@property ( setter=setButtonEnabled:) BOOL _buttonEnabled;
とあり、これを省略化するかのように解説を挟んだ後の"Mybject.h"コードには、
@property BOOL _buttonEnabled;
となっている。

これは、当初予定した動きを変更して、
@property (readwrite) BOOL _buttonEnabled;
にした、または、インスタンス変数に対するアクセス制限を緩くした、と解釈しても良いでしょうか?

p_g_ さんのコメント...

xcodeさん コメントありがとうございます.

確かにご指摘の通り副次的にgetterもできていることにもなり,アクセス制限を緩くはしてはいますね.
私はそこは意識していませんでした.

ここでは@propertyに特に記述せずに@synthesizeを使うと
 set+変数名というsetterができるよ
という話がメインで書いただけです.

書かなくてはならないコードが短くなって
 うれしい
というそれだけのお話のつもりでした.