高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Property and Synthesize

image

上篇,因為在類別裡,instance variable(以下簡稱ivar)預設是protected的,也就是說只有該類別以及它的子類別才能存取它,如果要給外部使用的話,則需要來幫它加個setter/getter。但每次只為了一個ivar就要寫一對的setter/getter也太麻煩了,在Objective-C 2.0之後加入了@property@synthesize的語法,讓這個工作簡單多了。借用上一篇的例子:

1
2
3
4
5
6
7
8
9
10
@interface Book : NSObject
{
  int price;
}

-(int) price;
-(void) setPrice: (int) p;
+(void) printBookInfo

@end

如果改用@property來寫:

1
2
3
4
5
6
7
8
9
@interface Book : NSObject
{
  int price;
}

@property int price;
+(void) printBookInfo;

@end

原來的setter/getter就可以省下來不用寫,然後在@implementation的部份則是使用@synthesize語法:

1
2
3
4
5
6
7
8
9
10
@implementation Book
// 這個@synthesize語法幫忙產生setter/getter
@synthesize price;

+(void) printBookInfo
{
  NSLog(@"Hello, This is a book");
}

@end

這裡的@synthesize price,其實就相當於自動產生了我們在上一篇寫的那一對setter/getter的程式碼:

1
2
3
4
5
6
7
8
9
-(int) price
{
  return price;
}

-(void) setPrice: (int) value
{
  price = value;
}

這樣程式碼就簡潔許多了。雖然@synthesize會自動幫忙產生setter/getter,但如果你想要寫自己的setter/getter,或是想要額外添加功能在裡面,只要照著它預設生成的setter/getter方法命名規則就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// interface
@interface Book : NSObject
{
  int price;
}

@property int price;
+(void) printBookInfo;

@end

// implementation
@implementation Book

@synthesize price;

// 自定setter
-(void)setPrice:(int)p
{
  // 故意讓傳入值變2倍
  price = p * 2;
}

+(void)printBookInfo
{
  NSLog(@"Hello, This is a book");
}
@end

@synthesize雖然會自動幫你建立一對setter/getter,但還是會以你建立的為主。

另外,記得在別的程式語言裡可以用點”.”來存取物件的屬性嗎? Objective-C 2.0之後也可以這樣做了,直接看看語法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Book *b = [[Book alloc] init];

// 一般的setter用法
[b setPrice:300];

// 一般的getter用法
NSLog(@"the price of the book is %d", [b price]);

// Objective-C 2.0之後可使用點語法,它會自動呼叫setter並將值傳給它
b.price = 200;

// 這裡則是呼叫getter把值傳回來
NSLog(@"the price of the book is %d", b.price);

[b release];

這樣有比較習慣了嗎? 我相信很多人對Objective-C的方括號的語法很感冒,至於程式碼的可讀性就看個人了,方括號語法看久了也是滿習慣的。不過用點語法的時候要注意幾件事,例如你自己寫了一個setter,又在裡面用了點語法:

1
2
3
4
5
-(void) setPrice: (int) p
{
  // 如果這樣寫的話,會啟動setPrice這個method
  self.price = p;
}

然後..就變無窮迴圈了! 這點要特別注意一下。

還有個問題是其實這個點語法光從程式碼其實看不太出來到底是物件還是結構還是變數..再來看另一個例子:

1
2
3
CGPoint pos = player.position;
pos.x += acceleration.x * 10;
player.position = pos;

這是一段Cocos2D的語法,內容大意是說:「我要建立一個CGPoint變數叫做pos,而這個pos是由player這個CCSprite的position來的」。acceleration是一個UIAcceleration物件,取得acceleration的x的值,修改pos裡的x之後再把整個pos變數塞回去給player這個角色。

這段程式碼對老手來說大概會覺得很弱,覺得這大概是新手,還得要建一個暫存變數,遜! 要是他來寫根本就可以直接併成一行:

1
player.position.x += acceleration.x * 10;

但事實上這樣寫的話會丟出一個”lvalue required as left operand of assignment”的編譯錯誤。為什麼會這樣? 分段來看:

1
player.position.x

其實這種”點語法”是一種”語法糖衣(syntactic sugar)”,事實上它是:

1
[player position].x

這邊position是一個getter,可以讓你從player身上取得一個CGPoint型態的position(r-value),但因為並沒有把指定給某個變數(l-value),所以當你要想用setter把它寫回去的時候,這個值已經消失了。

感想:看來Objective-C不只語法不同,連一些習慣寫法也不同了,眉眉角角真多!

建議閱讀:

Comments