1: Objective-C語言起源
Objective-C(以下簡稱OC)由SmallTalk語言演化而來终蒂。OC采用"消息結(jié)構(gòu)"的語法方式绊诲,是一種動態(tài)語言梧躺。與傳統(tǒng)的“函數(shù)調(diào)用”式語言相比奥吩,OC實際執(zhí)行的動作由運(yùn)行時而非編譯期決定。就好像是“函數(shù)調(diào)用”式的函數(shù)是多態(tài)一樣构拳。
OC的對象總是分配在“堆”上的咆爽。但是我們不需要使用 malloc 和 free來分配和釋放這些內(nèi)存,這些工作由OC的“引用計數(shù)“自動完成置森。
2: 在類的頭文件中盡量少引用其他頭文件
在C語言中我們已經(jīng)知道這一規(guī)則斗埂,即“前置聲明“(forward declaring)。在不需要知道某個類的詳細(xì)細(xì)節(jié)的時候凫海,我們最好在頭文件中前置聲明該類呛凶,然后在實現(xiàn)文件中引用類的頭文件。如EPerson類有一個EEmployer的成員:
//EPerson.h
@class EEmployer;//前置聲明
@interface EPerson : NSObject
...
@property (nonatomic, strong) EEmployer *employer;
@end
//EPerson.mm
#import "EPerson.h"
#import "EEmployer.h"
@implementation EPerson
...
@end
這樣做有幾個好處: 一是可以優(yōu)化編譯時間行贪;而是可以避免頭文件循環(huán)引用漾稀。
雖然#import指令可以避免死循環(huán),但意味著有一個類文件無法被正確編譯建瘫。
3. 多用字面量語法(string literal)
用類似C語言的語法,如:
NSString *somStr = @"This is a string literal";
NSNumber *someNum = @1;
NSNumber *floatNum = @2.5f;
NSNumber *boolNum = @YES;
NSArray *animals = @[@"cat",@"dog",@"mouse"];
NSDictionary *personDic = @{@"firstName":@"Matt", @"lastName":@"Galloway"};
優(yōu)點:
- 簡潔易讀崭捍。
- 編寫、修改簡單啰脚。
- 對于數(shù)組和字典殷蛇,還可以及早拋出異常。比如其中有nil的元素,字面量語法會直接拋出異常粒梦,但普通的alloc方法生成的數(shù)組或字典只會截取nil之前的元素收擦,會誤導(dǎo)我們。
缺點:
- 除了字符串以外谍倦,字面量語法創(chuàng)建的對象必須屬于Foundation框架,不能屬于自定義的類泪勒。
- 字面量語法創(chuàng)建的對象是不可變的昼蛀,若要可變版本的對象,還要復(fù)制一份圆存。
4.用類型常量代替宏定義
這點在C語言中就提到過叼旋,好處就是利用編譯器特性,可以驗證類型沦辙。
如果常量只用在一個編譯單元內(nèi)夫植,則在其.m文件中用static const
修飾
如果常量需要全局可見,則在一個頭文件中使用extern聲明全局變量油讯,并在某一個實現(xiàn)文件中定義其值详民。這種常量出現(xiàn)在全局符號表中,通常用與之相關(guān)的類名做前綴陌兑。
5. 枚舉類型
typedef NS_ENUM(NSUInteger, EOCConnectionState) {
EOCConnectStateDisconnected,
EOCConnectStateConnecting,
EOCConnectStateConnected
}
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectDown = 1 << 1,
EOCPermittedDirectLeft = 1 << 2,
EOCPermittedDirectRight = 1 << 3,
}
6. 理解"屬性"
@synthesize 可以更改默認(rèn)的實例變量名沈跨,但一半不推薦使用,為了使代碼可讀性更強(qiáng)兔综。
@dynamic 可以阻止編譯器自動合成存取方法饿凛。而且編譯時發(fā)現(xiàn)沒有定義存取方法,也不會報錯软驰,它相信這些方法能在運(yùn)行期間找到涧窒。
- 原子性
用在多線程同時訪問一個屬性的場景。開發(fā)中我們一般都用的是nonatomic
锭亏,原因是原子性要使用同步鎖纠吴,這種開銷比較大,而且在一個線程在連續(xù)多次讀取某屬性值的時候有別的線程在同時改寫該值慧瘤,那么即便將該屬性聲明為nonatomic
呜象,還是會讀到不同的屬性值,因而還是不能保證“線程安全”碑隆。若真想實現(xiàn)“線程安全“恭陡,還要更深層的鎖定機(jī)制。
讀寫權(quán)限
內(nèi)存管理語義
方法名
7. 對象內(nèi)部盡量直接訪問實例變量
直接使用實例變量_firstName
與使用存取方法self.firstName
有幾個區(qū)別:
1). 直接訪問實例變量不需要消息轉(zhuǎn)發(fā)機(jī)制上煤,編譯器生成帶啊直接訪問實例變量所在的內(nèi)存區(qū)域休玩,速度快。
2). 直接訪問實例變量不會調(diào)用”設(shè)置方法“,這樣繞過了屬性相關(guān)的"內(nèi)存管理語義“拴疤,這樣不太好永部。
3). 直接訪問實例變量,不會觸發(fā)KVO通知呐矾,也有可能出現(xiàn)問題苔埋。
4). 使用屬性方法助于斷點調(diào)試
這種方案是: 寫入實例變量時,使用設(shè)置方法蜒犯,讀取實例變量時组橄,直接訪問。這樣既可以提高讀寫速度罚随,又可以確保屬性的“內(nèi)存管理語義”玉工。
這個方案注意亮點:
1). 在初始化方法中基本總是應(yīng)該直接訪問實例變量,除非待初始化的變量是聲明在超類里淘菩,我們又在子類中無法直接訪問遵班。
2). 使用了懶加載技術(shù)后,都要通過存取方法來訪問潮改。
8. 理解“對象等同性”
比較對象時狭郑,“==”操作符只是比較兩者指針本身,應(yīng)該使用"isEqual"方法或者對象本身提供的"等同性判斷方法"愿阐,后者要求受測對象屬于同一個類。
NSObject協(xié)議中趾疚,有兩個用于判斷等同性的關(guān)鍵方法:
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
如果isEqual方法判定兩個對象相等缨历,那么hash方法也必須返回同一個值; 如果兩個對象的hash方法返回同一個值糙麦,那么isEqual方法未必認(rèn)為兩者相等辛孵。
覆寫hash方法時,既要考慮效率也要考慮碰撞率赡磅。
一些特定類具有自己的等同性判斷方法:
NSString -> isEqualToString
NSArray -> isEqualToArray
NSDictionary -> isEqualToDictionary
我們可以自己來判斷等同性魄缚,這樣既可以無須檢查參數(shù)類型,提升檢測速度焚廊,也使代碼更美觀易讀冶匹。
等同性判定有深度之分,比如NSArray可以比較每個元素是否相等(深度等同性判定)咆瘟,也可以只判定部分?jǐn)?shù)據(jù)是否相等嚼隘。要根據(jù)具體需求制定檢測方案。
把可變對象放入容器之后袒餐,盡量不要再改變對象內(nèi)容飞蛹,這樣有隱患谤狡。
9. 類族模式
類族模式可以隱藏抽象基類背后的實現(xiàn)細(xì)節(jié)。
“工廠模式”是其中之一卧檐。
Cocoa系統(tǒng)框架中有很多類族墓懂,如UIKit、NSArray等霉囚。
10. 關(guān)聯(lián)對象
這個在做method swizzling的時候會經(jīng)常用到捕仔。
將兩個對象關(guān)聯(lián)起來,再別的地方需要用到的時候再讀取出來盈罐,類似給對象動態(tài)添加屬性榜跌。
關(guān)聯(lián)時要指定存儲策略,類似于屬性添加內(nèi)存語義暖呕。
UIAlertView是一個好例子:
- (void)askUserQuestion {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question"
message:"What do you want to do?" delegate:self
cancelButtonTitle:@"cancel" otherButtonTitle:@"ok", nil];
void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
if (buttonIndex == 0) {
[self doCancel];
} else {
[self doContinue];
}
};
objc_setAssociatedObject(alert, EOCMyAlertViewKey, block, BJC_ASSOCIATION_COPY);
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey);
block(buttonIndex);
}
11. 理解消息傳遞(objc_msgSend)
這個術(shù)語我們已經(jīng)很熟悉了,Objective-C中就是objc_msgSend苞氮,使用動態(tài)綁定機(jī)制湾揽,在運(yùn)行時才決定調(diào)用那種方法。
編譯器會將所有的消息轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言調(diào)用:
void objc_msgSend(id self, SEL cmd, ...);
objc_msgSend會依據(jù)接受者與選擇器的類型來動態(tài)調(diào)用適當(dāng)?shù)姆椒鳌J紫瓤馕铮诮邮照咚鶎俚念愔兴褜ぁ胺椒斜怼保粽业脚c選擇器名稱相符合的方法贷帮,就跳轉(zhuǎn)至其實現(xiàn)的代碼戚揭;若找不到,就沿著繼承體系繼續(xù)向上查找撵枢;若最終沒有找到民晒,就執(zhí)行“消息轉(zhuǎn)發(fā)“(message forwarding)機(jī)制。
同時锄禽,objc_msgSend會將匹配的結(jié)果緩存在類的“快速映射表”中潜必,以后遇到與選擇器相同的消息就可以直接執(zhí)行了。
每個類中有函數(shù)指針表(類似于C++中的虛函數(shù)表)沃但,指針指向函數(shù)的實現(xiàn)地址磁滚,選擇器的名稱是查表時用的key。