Objective-C 最初起源于 NeXTSTEP 操作系統(tǒng),之后喬布斯回到蘋果,便將它在OS X和iOS中繼承了下來。
20世紀(jì)80年代初,Brad Box和Tom Love以SmallTalk-80語言為基礎(chǔ)發(fā)明了Objective-C,Smalltalk是歷史上第二個面向?qū)ο蟮某绦蛟O(shè)計語言,也就是說:Objective-C就是C語言面向?qū)ο骃mallTalk語法話的結(jié)果。
讓我們來看看早起SmallTalk的語法特點:
在 Smalltalk 中一切皆對象,一切調(diào)用都是發(fā)消息
比如下面的表達式:
2 + 3
它的意義是:向?qū)ο?code>2發(fā)送消息+,參數(shù)為對象3。
再比如用一個工廠方法來實例化一個對象:
p := Person name: 'Sai DiCaprio' age: 12
為Person類添加一個方法
greet: name
| message |
message := 'Hello ', name.
Transcript show: message.
在方法定義里,管道|包著的本地變量,然后是方法的實現(xiàn),把‘Hello’放到了變量message里,然后用逗號符把它和變量name連接起來。
p := Person new.
p greet: 'Jack'.
這時Transcript會輸出 Hello Jack。
從上可以看出,消息傳遞機制成了C語言進化為OC的最大障礙,要實現(xiàn)向一個target ( class / instance )發(fā)送消息名selector 動態(tài)尋找到函數(shù)實現(xiàn)地址IMP并調(diào)用,為了解決這一難題,需要提供一系列在Build Time無法實現(xiàn)的運行時函數(shù)支持,這些函數(shù)慢慢封裝完整,便出現(xiàn)了一套API:Runtime。
早期的Objective-C = C + Preprocessor + Runtime
既然已經(jīng)形成了一套健全的面向?qū)ο蟮腃語言體系,那么我們來看看早期的Objective-C 代碼是如何書寫的
@interface Person{
NSString *_name;
int _age;
}
- (NSString *)name;
- (void)setName:(NSString *)name;
- (int)age;
- (void)setAge:(int)age;
@end
上面的代碼聲明了一個類Person,他有2個成員變量,并分別為其提供了set和get方法。
秉著誰創(chuàng)建,誰釋放;誰引用,誰管理的MRC原則,在實現(xiàn)文件中便有了下面的代碼
@implementation Person
- (NSString *)name{
return _name;
}
- (void)setName:(NSString *)name{
if (_name != name){
[_name release];
_name = [name copy];
}
return _name;
}
- (int)age{
return _age;
}
- (void)setAge:(int)age{
_age = age;
}
@end
可以看到,僅僅創(chuàng)建了2個變量就為類帶來了這么大的代碼量,在成員變量多的情況下,通篇垃圾代碼,于是Objective-C 2.0馬上就出現(xiàn)了新的語法@property關(guān)鍵字
它用來讓編譯器自動幫我們生成成員變量和其對應(yīng)的get和set方法的聲明。
比如Person類的聲明中便化為這樣:
@interface Person
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
注意:這時的@property并不像現(xiàn)在一樣會幫我們自動生成成員變量的setter和getter方法的實現(xiàn)
在實現(xiàn)方面有另外一個關(guān)鍵字:@synthesize幫我們根據(jù)屬性修飾符自動合成setter和getter的實現(xiàn)。
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
后來自動合成的@synthesize可以不用寫了,默認就是上面的代碼。這樣一來,整個類變得輕快了很多。
但是也會有這樣的需求:比如不希望系統(tǒng)幫我們自動實現(xiàn)setter和getter,而由我們自己來實現(xiàn),便出現(xiàn)了@dynamic關(guān)鍵字,雖然編譯器會通過,但是如果在運行過程中方法調(diào)用了對應(yīng)的setter和getter方法,但是發(fā)現(xiàn)沒有手動實現(xiàn)那么就會崩潰報unrecognized selector sent to instance 0x.......的錯誤。編譯時沒問題,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定。
總結(jié):
- @synthesize 告訴編譯器幫我們合成屬性的getter和setter的實現(xiàn)
- @dynamic 告訴編譯器不要幫我自動合成屬性,而由我們自己實現(xiàn)getter和setter的實現(xiàn)
既然現(xiàn)在的@property會幫我們自動合成autosynthesize那么@dynamic和synthesize如今還有什么意義呢?
回答這個問題前,我們要搞清楚一個問題,什么情況下不會autosynthesis(自動合成)?
- 同時重寫了 setter 和 getter 時
- 重寫了只讀屬性的 getter 時
- 使用了 @dynamic 時
- 在 @protocol 中定義的所有屬性
- 在 category 中定義的所有屬性
- 重載的屬性
當(dāng)同時重寫了setter和getter或者重寫了只讀屬性的getter時,系統(tǒng)就不會幫我們自動合成,就意味著ivar也不會被生成,所以,這種情況下有兩種方案:
- 自己添加ivar
- 使用@synthesize
也就是說,當(dāng)你想手動管理 @property 的所有內(nèi)容時,你就會嘗試通過實@property 的所有存取方法或者使用@dynamic來達到這個目的,這時編譯器就會認為你打算手動管理 @property,于是編譯器就禁用了自動合成。