《Effective Objective-C 2.0》 閱讀筆記 1

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)點:

  1. 簡潔易讀崭捍。
  2. 編寫、修改簡單啰脚。
  3. 對于數(shù)組和字典殷蛇,還可以及早拋出異常。比如其中有nil的元素,字面量語法會直接拋出異常粒梦,但普通的alloc方法生成的數(shù)組或字典只會截取nil之前的元素收擦,會誤導(dǎo)我們。

缺點:

  1. 除了字符串以外谍倦,字面量語法創(chuàng)建的對象必須屬于Foundation框架,不能屬于自定義的類泪勒。
  2. 字面量語法創(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。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宵晚,一起剝皮案震驚了整個濱河市垂攘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淤刃,老刑警劉巖晒他,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逸贾,居然都是意外死亡仪芒,警方通過查閱死者的電腦和手機(jī)唁影,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掂名,“玉大人据沈,你說我怎么就攤上這事〗让铮” “怎么了锌介?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猾警。 經(jīng)常有香客問我孔祸,道長,這世上最難降的妖魔是什么发皿? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任崔慧,我火速辦了婚禮,結(jié)果婚禮上穴墅,老公的妹妹穿的比我還像新娘惶室。我一直安慰自己,他們只是感情好玄货,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布皇钞。 她就那樣靜靜地躺著,像睡著了一般松捉。 火紅的嫁衣襯著肌膚如雪夹界。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天隘世,我揣著相機(jī)與錄音可柿,去河邊找鬼。 笑死丙者,一個胖子當(dāng)著我的面吹牛趾痘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔓钟,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼永票,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了滥沫?” 一聲冷哼從身側(cè)響起侣集,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兰绣,沒想到半個月后世分,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡缀辩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年臭埋,在試婚紗的時候發(fā)現(xiàn)自己被綠了踪央。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓢阴,死狀恐怖畅蹂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荣恐,我是刑警寧澤液斜,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叠穆,受9級特大地震影響少漆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硼被,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一示损、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚷硫,春花似錦检访、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽风响。三九已至嘉汰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間状勤,已是汗流浹背鞋怀。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留持搜,地道東北人密似。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像葫盼,于是被迫代替她去往敵國和親残腌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容