關于Objective-C
Objective-C 是一種通用、高級、面向對象的編程語言。它擴展了標準的 ANSI C 編程語言寞缝,將 Smalltalk 式的消息傳遞機制加入到 ANSI C 中。目前主要支持的編譯器有 GCC 和 Clang仰泻。
歷史
Objective-C 主要由 Stepstone 公司的 Brad Cox 和 Tom Love 在 19 世紀 80 年代發(fā)明荆陆。
1981年 Brad Cox 和 Tom Love 還在 ITT 公司技術中心任職時,接觸到了 SmallTalk語言集侯。Cox 當時對軟件設計和開發(fā)問題非常感興趣被啼,他很快地意識到 SmallTalk 語言在系統(tǒng)工程構建中具有無法估量的價值。
1983 年棠枉,Cox 與 Love 合伙成立了 Productivity Products International(PPI)公司浓体,將 Objective-C 及其相關庫商品化販售,并在之后將公司改名為StepStone辈讶。
1988年命浴,斯蒂夫·喬布斯(Steve Jobs)離開蘋果公司后成立了 NeXT Computer 公司,NeXT 公司買下 Objective-C 語言的授權贱除,并擴展了 GCC 使之支持 Objective-C 的編譯生闲,基于 Objective-C 開發(fā)了 AppKit 與 Foundation Kit 等庫,作為 NeXTSTEP 的的用戶界面與開發(fā)環(huán)境的基礎月幌。
1996年12月20日碍讯,蘋果公司宣布收購 NeXT Software 公司,NEXTSTEP/OPENSTEP 環(huán)境成為蘋果操作系統(tǒng)下一個主要發(fā)行版本 OS X 的基礎扯躺。
C語言的嚴格超集
- 任何C語言程序不經修改就可以直接通過Objective-C編譯器成功編譯
- Objective-C 源程序中可以直接使用任何C語言代碼
正是由于以上優(yōu)勢捉兴,在 Swift 推出之后,許多和 C 有直接交互的部分大多仍舊使用 Objective-C 編寫缅帘。
SmallTalk 式的消息傳遞模型
Objective-C 最大的特色是承自 Smalltalk 的消息傳遞模型(message passing)轴术,此機制與 C Family 式的主流風格差異甚大。
如在 java 中钦无,方法調用:
obj.method(argument);
Objective-C 里的方法調用:
[obj method:argument];
Objective-C 與其說調用對象的方法逗栽,不如說向對象傳遞消息更為精確。
二者并不僅僅是語法上的差異失暂,還有基本行為上的不同彼宠。
舉個??:
[car fly];
Java 的解讀是 “調用 car 類的 fly 方法”。
若 car 類里沒有定義 fly 方法弟塞,那編譯不會通過凭峡。
Objective-C 里,則解讀為 “向 car 對象發(fā)送 fly 消息”决记。
若 car 類內定義有 fly 方法就運行方法內的代碼摧冀,若 car 內不存在 fly 方法,則程序依舊可以通過編譯,運行期則拋出異常: unrecognized selector sent to instance 0x8871710
Objective-C 因為運行期才處理消息索昂,允許發(fā)送未知消息給對象建车。同時空對象 nil 接受消息后默認為不做事,所以送消息給 nil 也不用擔心程序崩潰椒惨。
布爾(BOOL)
Java 的布爾數(shù)據類型 boolean 具有 true 和 false 兩個值缤至,Objective-C 的 BOOL 具有 YES 和 NO 兩個值。
Objective-C 中的 BOOL 實際上一種對帶符號的字符類型(signed char)的類型定義康谆。通過 #define 把 YES 定義為1领斥,NO 定義為0。
字符串(NSString)
Objective-C 字符串由雙引號包裹沃暗,并在引號前加一個@符號月洛,如:
NSString *name = @"Tony";
import 語句
在C語言中,使用 #include 導入入頭文件孽锥。在Objective-C中膊存,類似的指令 #import 保證一個文件只會被包含一次,類似于一般頭文件中的:
#ifndef XXX
#define XXX ...
#endif
類的定義與實現(xiàn)
Objective-C 中將類的定義(interface)與實現(xiàn)(implementation)分為兩個部分忱叭。
類的定義文件遵循 C 語言慣例以 .h 為后綴,實現(xiàn)文件以 .m 為后綴今艺。
定義部分韵丑,定義類的名稱、數(shù)據成員和方法虚缎。 以關鍵字 @interface 開始撵彻,@end 結束:
@interface MyClass : NSObject {
int memberVar1; // 實體變量
id memberVar2;
}
+ (return_type)class_method; // 類方法
- (return_type)instance_method1; // 實例方法
- (return_type)instance_method2:(int)p1;
- (return_type)instance_method3:(int)p1 andPar:(int)p2;
@end
實現(xiàn)部分,以關鍵字 @implementation 開始实牡,@end 結束:
@implementation MyClass {
int memberVar3; // 私有實體變量
}
- (return_type)instance_method1 {
....
}
- (return_type)instance_method2:(int)p1 {
....
}
- (return_type)instance_method3:(int)p1 andPar:(int)p2 {
....
}
@end
上述代碼 Java 版對照:
public class MyClass {
protected int memberVar1;
protected pointer memberVar2;
private int memberVar3;
public (return_type) instance_method1() {
....
}
public (return_type) instance_method2(int p1) {
....
}
public (return_type) instance_method3andPar(int p1, int p2) {
....
}
}
方法前面的 +/- 代表函數(shù)的類型:加號(+)代表類方法(class method)陌僵,不需要實例就可以調用,與 Java 的靜態(tài)方法相似创坞。減號(-)即是一般的實例方法(instance method)碗短。
創(chuàng)建對象
Objective-C 創(chuàng)建對象需通過 alloc 和 init 兩個消息。alloc 是分配內存题涨,init 則是初始化對象偎谁。 init 與 alloc 都是定義在 NSObject 里的方法,父對象收到這兩個信息并做出正確回應后纲堵,新對象才創(chuàng)建完畢巡雨。
舉個??:
MyObject *obj = [[MyObject alloc] init];
MyObject *obj = [MyObject new]; // 也可以這么寫,new 相當于 alloc + init
Java 版本:
MyObject obj = new MyObject();
協(xié)議(Protocol)
協(xié)議類似與 Java 語言中的接口席函。
協(xié)議的定義以 @protocol 開頭铐望,@end 結尾:
@protocol SayHello
- (void)sayHello;
@end
協(xié)議中定義的方法分為必須實現(xiàn)的方法和可選實現(xiàn)的方法。協(xié)議中的方法默認必須實現(xiàn),可選實現(xiàn)的方法以 @optional 為標識正蛙。
@protocol SomeProtocol
- (void)method1; // 必須實現(xiàn)
@optional
- (void)method2; // 可選實現(xiàn)
- (void)method3; // 可選實現(xiàn)
@end
實現(xiàn)一個協(xié)議:
@interface SomeClass : SomeSuperClass <SomeProtocol>
@end
@implementation SomeClass
- (void)method1 {
// ...
}
- (void)method2 {
// ...
}
- (void)method3 {
// ...
}
@end
分類(Category)
分類可以給一個已經存在的類增加方法督弓,而不用去改它的源碼。類似于 Swift 和 Kotlin 中的擴展(extension)跟畅。
比如咽筋,NSString 是 Objective-C 內置的系統(tǒng)類,我們創(chuàng)建一個它的分類以支持加法運算:
// interface 部分
@interface NSString (Calculation)
- (NSString *)stringByAdding:(NSString *)aString; // 加
@end
// implementation 部分
@implementation NSObject (Calculation)
- (NSString *)stringByAdding:(NSString *)aString {
// ...
}
@end
使用的時候徊件,只要包含NSObject+Calculation.h奸攻,就可以使用了:
NSString *str = @"100";
NSString *result = [str stringByAdding:10]; // result is 110
引用計數(shù)
Objective-C 使用引用計數(shù)來管理對象的生命周期。
如果想使某個對象繼續(xù)存活虱痕,那就遞增其引用計數(shù)(reatain)睹耐;用完了之后,就遞減其計數(shù)(release)部翘。當計數(shù)為0時硝训,系統(tǒng)就會將它銷毀。
對象操作 | Objective-C方法 | 操作結果 |
---|---|---|
生成并持有對象 | alloc, new, copy, mutableCopy 等方法 | 生成對象并設置引用計數(shù) =1 |
持有對象 | reatain 方法 | 引用計數(shù) +1 |
釋放對象 | release 方法 | 引用計數(shù) -1 |
廢棄對象 | dealloc 方法 | 系統(tǒng)自動調用 引用計數(shù) =0 時調用 |
iOS5 開始引入了自動引用計數(shù) (ARC新思, Automatic Reference Counting)
簡單地說窖梁,ARC 在編譯時為代碼在合適的位置加上 retain 和 release。
屬性
屬性(property)是 Objective-C 的一項特性夹囚,用于封裝對象中的數(shù)據纵刘。實例變量一般通過“存取方法”(access method)來訪問。其中荸哟,“獲取方法”(getter)用于讀取變量值假哎,而“設置方法”(setter)用于寫入變量值。
Xcode 4.4時代之前鞍历,需要一個屬性的時候:
// .h
@property NSObejct *foo;
// .m
@synthesize foo = _foo;
頭文件中加上@property舵抹,那么編譯器會自動添加下面一段代碼:
- (NSObject *)foo;
- (void)setFoo:(NSObject *)newFoo;
在 .m 中實現(xiàn):
- (NSObject *)foo {
return _foo;
}
- (void)setFoo:(NSObject *)newFoo {
[foo retain];
[_foo release];
_foo = foo;
}
自動引用計數(shù)時代,喜大普奔劣砍,一切都變得簡單了惧蛹,只需要一句話:
@property (nonatomic, strong) NSObejct *foo;
@property(*) 括號中的屬性內容:
- 原子性:atomic 和 nonatomic,默認為atomic秆剪。
- 讀寫權限:readwrite 和 readonly赊淑, 默認為 readwrite,同時生成setter和getter仅讽,所以本質上是由@synthesize來實現(xiàn)的陶缺。而 readonly 就是代表只生成 getter 方法。
- 指定方法名:使用getter=<name>或setter=<name>來指定方法名洁灵。
- 內存管理:assign饱岸,strong掺出,weak,copy苫费。
當 B 是 A 的 strong 屬性汤锨,A 又是 B 的 strong 屬性,此時就會造成循環(huán)引用百框,如圖:
此時闲礼,A 若想被釋放,需要引用計數(shù)為0铐维。而 B 持有 A柬泽,所以想要 A dealloc,需要 B 發(fā)送 release 消息到 A嫁蛇。而 B 只有 dealloc 時锨并,才會發(fā)送 release 到 A,并且 B dealloc 也需要 A 向其發(fā)送 release 消息睬棚。這樣 A 和 B 互相等待對方的 release 消息第煮,造成循環(huán)引用,導致內存無法釋放抑党。
解決循環(huán)引用:將其中一方的屬性使用 weak 修飾包警。
點語法
屬性合成的方法可以通過點語法調用:
obj.foo = newFoo;
// 上述代碼等同于
[obj setFoo:newFoo];
點語法的本質還是方法調用,是一種編譯器行為底靠,編譯器會自動進行轉換揽趾,來判斷調用 set 方法還是 get 方法。
操作 | 點語法 | 使用消息表達式 |
---|---|---|
setter | obj.name = val; | [obj setName:val]; |
getter | val = obj.name; | val = [obj name]; |
代碼塊 (block)
block 對象是對函數(shù)的擴展苛骨。除了函數(shù)中的代碼,block 還包含變量綁定苟呐。block 也被稱為閉包(closure)痒芝。block 是以 “^” 開頭為標識的。后面跟的一個括號標示 block 需要的參數(shù)列表牵素。
一個簡單的??:
void (^myBlock) (int) = ^(int input) {
NSLog(@"input number is %d", input);
};
// 調用 block
myBlock(1);
// 輸出:input number is 1
block 使用局部變量:
NSString *name = @"Candy";
void (^myBlock) (void) = ^ {
NSLog(@"name is %@", name);
};
myBlock();
// 輸出:name is Candy
block 可以方便的使用局部變量 name严衬,但是你不能修改它,當你嘗試修改 name 時笆呆,會報錯 Variable is not assignable (missing __block type specifier)
请琳。
此時你需要用 __block
修飾 name:
__block NSString *name = @"Candy";
void (^myBlock) (void) = ^ {
name = @"Jack"
NSLog(@"name is %@", name);
};
myBlock();
// 輸出:name is Jack
block 使用實例變量:
// 定義的實例變量 userName
@property (nonatomic, copy) NSString *name;
// block 使用實例變量
void (^myBlock) (void) = ^ {
NSLog(@"ins is %@", self.name);
};
當 block 本身為實例變量,而 block 內部又使用了 實例變量赠幕,此時就會出現(xiàn)循環(huán)引用俄精。
舉個??:
// 定義的實例變量 userName 和 myBlock
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void (^myBlock)(void);
// block 使用實例變量
_myBlock = ^ {
NSLog(@"name is %@", self.name);
};
此時會有警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
簡單來說,是因為 self 引用了 myBlock榕堰,myBlock 又引用了 self竖慧。
解決 block 循環(huán)引用:
// 定義的實例變量 userName 和 myBlock
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, copy) void (^myBlock)(void);
// block 使用實例變量
__weak typeof(self) weakSelf = self;
self.myBlock = ^ {
NSLog(@"name is %@", weakSelf.userName);
};