Effective Objective-C 的讀書筆記

主要摘錄了《 Effective Objective-C》里的編寫高質(zhì)量的方法忽匈;

1 熟悉Objective -C

1.1 OC 起源

  • OC 為C語(yǔ)言增加了面對(duì)對(duì)象的特性,是 C 的超集溉躲,并且使用動(dòng)態(tài)綁定的消息結(jié)構(gòu);

1.2 在類的頭文件中盡量少引入其他頭文件

將引入頭文件的時(shí)機(jī)盡量延后瞒爬,只在確有需要時(shí)才引入卑惜,這樣就可以減少類的使用者所需要引入的頭文件的數(shù)量:

  • 除非確有必要,否則不要引入頭文件鼠哥,一般來說熟菲,應(yīng)在某個(gè)類的頭文件中使用向前聲明來提及別的類,并在實(shí)現(xiàn)文件中引入那些類的頭文件朴恳。這樣做可以盡量降低類之間的耦合抄罕;
  • 有時(shí)無法使用向前聲明,比如要聲明某個(gè)類遵循一項(xiàng)協(xié)議于颖。這種情況下呆贿,盡量把“該類遵循某協(xié)議”這條聲明移到 class-continuation 分類中。如果不行,則把協(xié)議單獨(dú)放在一個(gè)頭文件中做入,然后將其引入冒晰;

有時(shí)候在編寫頭文件時(shí),需要引入某個(gè)類A(如作為當(dāng)前類的某個(gè)屬性來使用)竟块,但是不需要知道這個(gè)類A的實(shí)現(xiàn)細(xì)節(jié)壶运,此時(shí)我們不需要直接引入這個(gè)類A的頭文件,只需要告訴編譯器浪秘,類A 是一個(gè)類就可以了蒋情,然后在實(shí)現(xiàn)文件里再引入類A的頭文件; 向前聲明的語(yǔ)法為: @class 類A ;

1.3 多用字面量語(yǔ)法 耸携,少用與之等價(jià)的方法

  • 應(yīng)用使用字面量語(yǔ)法來創(chuàng)建字符串棵癣、數(shù)值、數(shù)組夺衍、字典狈谊。與創(chuàng)建此類對(duì)象的常規(guī)方法相比,這么做更加簡(jiǎn)明扼要刷后;
  • 應(yīng)用通過取下標(biāo)操作來訪問數(shù)組下標(biāo)顴字典中的鍵所對(duì)應(yīng)的元素的畴;
  • 用字面量語(yǔ)法創(chuàng)建數(shù)組或字典時(shí),值中有 nil尝胆,則會(huì)拋出異常丧裁。因此,需要確保值里不含nil含衔;

如 :

NSNumber *someNumber = [NSNumber numberWithInt:1];
// 字面量語(yǔ)法
NSNumber *someNumber = @1;

NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys:
@"Matt",@"firstName",@"Ga",@"lastName",nil];
// 字面量語(yǔ)法
NSDictionary *persionData= @{@"firstName" : @"Matt",@"lastName" : @"Ga"};

NSString *lastName = [personData objectForKey:@"lastName"];
// 字面量語(yǔ)法
NSString *lastName = personData[@"lastName"];

由以上幾個(gè)例子中煎娇,可以很明顯可以看到字面量語(yǔ)法全更加簡(jiǎn)潔直觀;但字面量語(yǔ)法有個(gè)小限制贪染,就是除了字符串以外缓呛,所創(chuàng)建出來的對(duì)象必須屬于 Foundation框架;

1.4 多用類型常量杭隙,少用# define 預(yù)處理指令

  • 不要用預(yù)處理指令定義常量哟绊,這樣定義出來的常量不包含類型信息,編譯器只會(huì)在編譯前進(jìn)行查找與替換操作痰憎,但不確保準(zhǔn)確性票髓;
  • 在實(shí)現(xiàn)文件中使用 static const 來定義“只在編譯單元可見的常量”竹握,這類常量一舉地在全局符號(hào)表里出現(xiàn)闯狱;
  • 在頭文件中使用 extern來聲明全局常量,并在相關(guān)實(shí)現(xiàn)文件中定義其值坦弟。這類常量會(huì)出現(xiàn)在全局符號(hào)表里蜗细,所以其名稱應(yīng)加以區(qū)分裆操,通常使用類名做前綴;

全名法則: 若常量?jī)H在編譯單元內(nèi)可見,則在前面加字母k踪区,如果在類外可見昆烁,則通常以類名為前綴;

1.5 用枚舉表示狀態(tài)朽缴、選項(xiàng)善玫、狀態(tài)碼

  • 應(yīng)該用枚舉來表示狀態(tài)機(jī)的狀態(tài)水援,傳遞給方法的選項(xiàng)以及狀態(tài)碼等值密强,給這些值起個(gè)易懂的名字;
  • 如果把傳遞給某個(gè)方法的選項(xiàng)表示為枚舉類型蜗元,而多個(gè)選項(xiàng)又可以同時(shí)使用或渤,則將各選項(xiàng)值定義為2的冪,以便通過按位或操作將其組合起來奕扣;
  • NS_ENUMNS_OPTIONS宏來定義枚舉類型薪鹦,并指明其底層的數(shù)據(jù)類型;
  • 在處理枚舉類型的 switch 語(yǔ)句中不要實(shí)現(xiàn) default分支惯豆,這樣的話池磁,加入新枚舉類型后,編譯器就會(huì)提示switch 語(yǔ)句并沒有處理所有的枚舉楷兽;

2 對(duì)象地熄、消息、運(yùn)行期

2.1 理解“屬性”這一概念

  • 可以用 @property語(yǔ)法來定義對(duì)象中所封裝的數(shù)據(jù)芯杀;
  • 通過“特質(zhì)”來指定存儲(chǔ)數(shù)據(jù)所需的正確說到底端考;
  • 在設(shè)置屬性所對(duì)應(yīng)的實(shí)例變量時(shí),一定要遵從該屬性所聲明的語(yǔ)義揭厚;
    屬性可以擁有的特質(zhì)分為四類:
  • 原子性却特,如果屬性具備nonatomic特質(zhì),則不使用同步鎖筛圆,否則它就是原子的裂明;
  • 讀寫權(quán)限,readwrite/readonly太援,
  • 內(nèi)存管理語(yǔ)義
    • assign 設(shè)置方法闽晦,只會(huì)執(zhí)行針對(duì)“純量類型”的簡(jiǎn)單賦值操作;
    • strong 表明該屬性定義了一種“擁有關(guān)系”設(shè)置方法會(huì)先保留新值粉寞,并釋放舊值尼荆,然后再將新值設(shè)置上去;
    • weak 表明該屬性定義了一種“非擁有關(guān)系”設(shè)置方法既不保留新值唧垦,也不釋放舊值捅儒,只是簡(jiǎn)單的將新值設(shè)置上去,如果該屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空巧还;
    • unsafe_unretained 語(yǔ)義與 assign 相同鞭莽,但是適用于“對(duì)象類型”,該特性表達(dá)一種“非擁有關(guān)系”麸祷,但目標(biāo)對(duì)象遭到摧毀時(shí)澎怒,屬性值不會(huì)自動(dòng)清空;
    • copy 所屬關(guān)系與 strong 類似阶牍,但設(shè)置方法不保留新值喷面,而是將其“copy";

2.2 在對(duì)象內(nèi)部盡量直接訪問實(shí)例變量

  • 在對(duì)象內(nèi)部讀取數(shù)據(jù)時(shí),應(yīng)該直接通過實(shí)例變量來讀走孽,而寫入數(shù)據(jù)時(shí)惧辈,則就通過屬性來寫;
  • 在初始化方法及 dealloc 方法中總是應(yīng)該直接通過實(shí)例變量來讀寫數(shù)據(jù)磕瓷;
  • 有時(shí)會(huì)使用惰性初始化技術(shù)配置某份數(shù)據(jù)盒齿,這種情況下,需要通過屬性來讀取數(shù)據(jù)困食;

2.3 理解”對(duì)象等同性“這一概念

  • 若想檢測(cè)對(duì)象的等同性边翁,請(qǐng)?zhí)峁?isEqual:hash方法;
  • 相同的對(duì)象必須具有相同的哈希碼硕盹,但是兩個(gè)哈希碼相同的對(duì)象卻未必相同符匾;
  • 不要盲目地逐個(gè)檢測(cè)每條屬性,而是應(yīng)該依照具體需求來制定檢測(cè)方案莱睁;
  • 編寫 hash方法時(shí)待讳,應(yīng)該使用計(jì)算速度快而且哈希碼碰撞機(jī)率低的算法;

==操作符比較的是兩個(gè)指針本身仰剿,而不是其所指對(duì)象创淡,一般常用 isEqual方法來判斷兩個(gè)對(duì)象的等同性;

2.4 以“類族模式” 隱藏實(shí)現(xiàn)細(xì)節(jié)

  • 類族模式可以把實(shí)現(xiàn)細(xì)節(jié)隱藏在一套簡(jiǎn)單的公共接口后面南吮;
  • 系統(tǒng)框架中經(jīng)常使用類族琳彩;
  • 從類族的公共抽象基類中繼承子類時(shí)要當(dāng)心,若有開發(fā)文檔部凑,則應(yīng)首先閱讀露乏;
    類似于Java 設(shè)計(jì)模式的抽象工廠或工廠方法;

2.5 在既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù)

  • 可以通過“關(guān)聯(lián)對(duì)象”機(jī)制來把兩個(gè)對(duì)象連起來涂邀;
  • 定義關(guān)聯(lián)對(duì)象時(shí)可以指定內(nèi)存管理語(yǔ)義瘟仿,用以模仿定義屬性時(shí)所采用的“擁有關(guān)系”與“非擁有關(guān)系”;
  • 只有在其他做法不可行時(shí)才應(yīng)選用關(guān)聯(lián)對(duì)象比勉,因?yàn)檫@種做法通常會(huì)引入難于查找的bug;

2.6 理解 objc_msgSend 作用

  • 消息由接收者劳较,選擇子及參數(shù)構(gòu)成驹止。給某對(duì)象“發(fā)送消息(invoke a message),也就相當(dāng)于在該對(duì)象上“調(diào)用方法”观蜗;
  • 發(fā)給某對(duì)象的全部消息都要由“動(dòng)態(tài)消息派發(fā)系統(tǒng)”來處理臊恋,該系統(tǒng)會(huì)查出對(duì)應(yīng)的方法,并執(zhí)行其代碼墓捻;

在OC中抖仅,如果向?qū)ο髠鬟f消息,那就會(huì)使用動(dòng)態(tài)綁定機(jī)制來決定需要調(diào)用的方法砖第。而在底層撤卢,所有方法都是普通的C語(yǔ)言函數(shù),然而對(duì)象在接收到消息后厂画,究竟該調(diào)用哪個(gè)方法則完全于運(yùn)行期決定凸丸。如:

//以下語(yǔ)句是給對(duì)象發(fā)送一條消息
int value = [someObject messageName :parameter];

其中拷邢,someObject叫接收者袱院,messageName 叫選擇子(selector),選擇子和參數(shù)合起來稱為消息;

2.7 理解消息轉(zhuǎn)發(fā)機(jī)制

  • 若對(duì)象無法響應(yīng)某個(gè)選擇子瞭稼,則進(jìn)入消息轉(zhuǎn)發(fā)流程忽洛;
  • 通過運(yùn)行期的動(dòng)態(tài)方法解析功能,我們可以在需要用到某個(gè)方法時(shí)再將其加入類中环肘;
  • 對(duì)象可以把其無法解讀的某些選擇子轉(zhuǎn)交給其他對(duì)象來處理欲虚;
  • 經(jīng)過上述兩步后,如果還是沒有辦法處理選擇子悔雹,則啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制复哆;

2.8 用“方法調(diào)配技術(shù)”調(diào)試“黑盒方法”

  • 在運(yùn)行期,可以向類中新增或替換選擇子所對(duì)應(yīng)的方法實(shí)現(xiàn)腌零;
  • 使用另一份實(shí)現(xiàn)來替換原有的方法實(shí)現(xiàn)梯找,常用來向原有實(shí)現(xiàn)中添加新功能;
  • 一般來說益涧,只有調(diào)試程序的時(shí)候才需要在運(yùn)行期修改方法實(shí)現(xiàn)锈锤;
    類的方法列表會(huì)把選擇子名稱映射到相關(guān)的方法實(shí)現(xiàn)之上,使用“動(dòng)態(tài)消息派發(fā)系統(tǒng)能夠據(jù)此找到應(yīng)該調(diào)用的方法闲询。

2.9 理解”類對(duì)象“的用意

  • 每個(gè)實(shí)例都有一個(gè)指向 Class對(duì)象的指針久免,用以表明其類型,而這些Class 對(duì)象則構(gòu)成了類的繼承體系扭弧;
  • 如果對(duì)象類型無法在編譯期確定阎姥,那么就應(yīng)該使用類型信息查詢方法來探知;
  • 盡量使用類型信息查詢方法來確定對(duì)象類型鸽捻,而不要直接比較類對(duì)象呼巴,因?yàn)槟承?duì)象可能實(shí)現(xiàn)了消息轉(zhuǎn)發(fā)功能氨淌。

isMemberOfClass 能夠判斷出對(duì)象是否為某個(gè)特定類的實(shí)例;
isKindOfClass 能夠判斷出對(duì)象是否為某類或其派生類的實(shí)例伊磺;

3 接口與API設(shè)計(jì)

3.1 用前綴避免命名空間沖突

  • 選擇與你的公司盛正、應(yīng)用程序或二者皆有關(guān)聯(lián)之名作為類名的前綴,并在所有代碼中均使用這一前綴屑埋;
  • 若自己所開發(fā)的程序庫(kù)中用到了第三方庫(kù)豪筝,則應(yīng)為其中的名稱加上前綴;

OC 沒有其他語(yǔ)言那種內(nèi)置的命名空間機(jī)制摘能。因此我們?cè)谄鹈麜r(shí)需要設(shè)法避免潛在的例句沖突续崖。我們?cè)谶x擇前綴時(shí),應(yīng)該是三個(gè)字母的团搞;

3.2 提供“全能初始化方法”

  • 在類中提供現(xiàn)代戰(zhàn)爭(zhēng)全能初始化方法严望,并于文檔里指明。其他初始化方法均應(yīng)調(diào)用此方法逻恐;
  • 若全能初始化方法與超類不同像吻,則需覆寫超類中對(duì)應(yīng)的方法;
  • 如果超類的初始化方法不適用于子類复隆,則應(yīng)該覆寫這個(gè)超類方法拨匆,并在其中拋出異常;

全能初始化方法類似于 Java 中提供不同構(gòu)造參數(shù)的構(gòu)造方法挽拂,所有的構(gòu)造方法最終都會(huì)調(diào)用其中參數(shù)最完整的構(gòu)造方法惭每;

3.3 實(shí)現(xiàn) description 方法

  • 實(shí)現(xiàn) description 方法返回一個(gè)有意義的字符串,用以描述該實(shí)例亏栈;
  • 若想在調(diào)試時(shí)打印出更詳盡的對(duì)象描述信息台腥,則應(yīng)實(shí)現(xiàn) debugDescription 方法;

description 方法類似于 JavaObjecttoString 方法的功能绒北,而且在調(diào)試時(shí)黎侈,如果有實(shí)現(xiàn)debugDescription 方法,則會(huì)調(diào)用該方法來輸出更詳細(xì)的信息镇饮;

3.4 盡量使用不可變對(duì)象

  • 盡量創(chuàng)建不可變的對(duì)象蜓竹;
  • 若某屬性僅可用于對(duì)象內(nèi)部修改,則在 class-continuation分類中將其由 readonly屬性擴(kuò)展為 readwrite屬性储藐;
  • 不要把可變的collection作為屬性公開俱济,而應(yīng)提供相關(guān)方法,以此修改對(duì)象中的可變 collection钙勃;

3.5 使用清晰而協(xié)調(diào)的命名方式

  • 起名時(shí)應(yīng)遵從OC的命名規(guī)范蛛碌,這樣創(chuàng)建出來的接口更容易為開發(fā)者所理解;
  • 方法名要言簡(jiǎn)辖源。蔚携。希太。。
  • 方法名里不要使用縮略后的類型名稱酝蜒;
  • 給方法起名時(shí)的第一要?jiǎng)?wù)是確保其風(fēng)格與你自己的代碼或所要集成的框架相符誊辉;

3.6 為私有方法名加前綴

  • 給私有方法的名稱加上前綴,這樣可以很容易地將其同公共方法區(qū)分開亡脑;
  • 不要單用現(xiàn)代戰(zhàn)爭(zhēng)下劃線做私有方法的前綴堕澄,因?yàn)檫@種做法是預(yù)留給蘋果使用的;

3.7 理解 OC 錯(cuò)誤類型

  • 只有發(fā)生了可使整個(gè)應(yīng)用程序崩潰的嚴(yán)重錯(cuò)誤時(shí)霉咨,才應(yīng)使用異常蛙紫;
  • 在錯(cuò)誤不那么嚴(yán)重的情況下,可以指派“委托方法”來處理錯(cuò)誤途戒,也可以把錯(cuò)誤信息放在NSError對(duì)象里坑傅,經(jīng)由“輸出參數(shù)”返回給調(diào)用者;

如果出現(xiàn)非致命的錯(cuò)誤時(shí)喷斋,則可以令方法返回 nil/0 或使用 NSError 來表明其中有錯(cuò)誤發(fā)生唁毒;

3.8 理解 NSCopying 協(xié)議

  • 若想令自己所寫的對(duì)象具有拷貝功能,則需實(shí)現(xiàn) NSCopying继准;
  • 如果自定義的對(duì)象分為可變版本與不可變版本枉证,那么就要同時(shí)實(shí)現(xiàn) NSCopyingNSMutableCopying協(xié)議;
  • 復(fù)制對(duì)象時(shí)需決定采用淺拷貝還是深拷貝移必;
  • 如果對(duì)象需要深拷貝,那么可考慮新增一個(gè)專門執(zhí)行深拷貝的就去毡鉴;

一般情況下崔泵,遵從了NSCopying協(xié)議的對(duì)象,執(zhí)行的都是淺拷貝猪瞬,除非該對(duì)象有特別說明它是用深拷貝來實(shí)現(xiàn)Copying憎瘸,否則應(yīng)該自己去編寫深拷貝的;

4 協(xié)議與分類

4.1 通過委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象通信

  • 委托模式為對(duì)象提供了一套接口陈瘦,使其可由此將相關(guān)事件告知其他對(duì)象幌甘;
  • 將委托對(duì)象應(yīng)該支持的接口定義成協(xié)議,在協(xié)議中把可能需要處理的事件定義成方法痊项;
  • 當(dāng)某對(duì)象需要從另外一個(gè)對(duì)象中獲取數(shù)據(jù)時(shí)锅风,可以使用委托協(xié)議。這種情況下鞍泉,該模式亦稱為data source protocal
  • 若有必要皱埠,可實(shí)現(xiàn)含有位段的結(jié)構(gòu)體,將委托對(duì)象是否能響應(yīng)相關(guān)協(xié)議方法這一信息緩存至其中咖驮;

OC 中廣泛使用 delegate pattern 的模式來實(shí)現(xiàn)對(duì)象間的通信边器,該模式的主旨是:定義一套接口训枢,某對(duì)象若想接受另一對(duì)象的委托,則需遵從此接口忘巧; 其實(shí)這就是 Java里的編程規(guī)則里的面向接口編程恒界;所謂的位段結(jié)構(gòu)體,就是用一個(gè)屬性來表明委托對(duì)象實(shí)現(xiàn)了哪些協(xié)議方法砚嘴,每個(gè)協(xié)議方法對(duì)應(yīng)于該屬性的一個(gè)二進(jìn)制位仗处;
需要注意的是 委托對(duì)象與被委托的對(duì)象之間的關(guān)系應(yīng)該是非擁有關(guān)系,也就是對(duì)應(yīng)的屬性得用weak來修飾枣宫;

4.2 將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中

  • 使用分類機(jī)制把類的實(shí)現(xiàn)代碼劃分成易于管理的小塊婆誓;
  • 將“私有”方法歸入名為Private的分類中,以隱藏實(shí)現(xiàn)細(xì)節(jié)也颤;

4.3 總是為第三方類的分類名稱加前綴

  • 向第三方類中添加分類時(shí)洋幻,總應(yīng)給其名稱加上你專用的前綴;
  • 向第三方類中添加分類時(shí)翅娶,總應(yīng)給其中的方法名加上你專用的前綴文留;

分類機(jī)制通常用于向無源碼的既有類中新增功能,分類中的方法是直接添加在類里面的竭沫;

如:

//給NSString 添加一個(gè)分類:ABC_HTTP
@interface NSString(ABC_HTTP)
- (NSString *) abc_urlEncodeString;

@end

4.4 勿在分類中聲明屬性

  • 把封裝數(shù)據(jù)所用的全部屬性都定義在接口里燥翅;
  • class-continuation 分類之外的其他分類中,可以定義存取方法蜕提,但盡量不要定義屬性森书;
    正確的做法是把所有屬性都定義在主接口里,類所封裝的全部數(shù)據(jù)都應(yīng)該定義在主接口里谎势;

4.5 使用class-continuation分類 隱藏實(shí)現(xiàn)細(xì)節(jié)

  • 通過class-continuation分類向類中新增實(shí)例變量凛膏;
  • 如果某屬性在主接口中聲明為"只讀",而類的內(nèi)部又要用設(shè)置方法修改此屬性脏榆,那么就在class-continuation分類中將其擴(kuò)展為:可讀寫猖毫;
  • 把私有方法的原型聲明在class-continuation分類里面;
  • 若想使所遵循的協(xié)議不為人所知须喂,則可于 class-continuation分類里聲明吁断;

例子:

// EOCPerson.h
@interface EOCPerson :NSObject {
    .....
}

//EOCPerson.m
//這是EOCPerson的`class-continuation`分類
@interface EOCPerson(){
    .....
}

@implementation EOCPerson {
    ...
}

### 4.6 通過協(xié)議提供匿名對(duì)象
- 協(xié)議可在某種程度上提供匿名類型,具體的對(duì)象類型可以淡化成遵從某協(xié)議的 id 類型坞生,協(xié)議里規(guī)定了對(duì)象所應(yīng)實(shí)現(xiàn)的方法仔役;
- 使用匿名對(duì)象來隱藏類型名稱;
- 如果具體類型不重要恨胚,重要的是對(duì)象能夠響應(yīng)特定方法骂因,那么可使用匿名對(duì)象來表示;

OC 里的協(xié)議 就是 Java 里的接口赃泡,協(xié)議定義了一系列方法寒波,遵從些協(xié)議的對(duì)象實(shí)現(xiàn)它們


5 內(nèi)存管理

5.1 理解引用計(jì)數(shù)

  • 引用計(jì)數(shù)機(jī)制通過可以遞增遞減的計(jì)數(shù)器來管理內(nèi)存乘盼。對(duì)象創(chuàng)建好之后,其保留計(jì)數(shù)至少為1.若保留計(jì)數(shù)為正俄烁,則對(duì)象繼續(xù)存活绸栅。當(dāng)保留計(jì)數(shù)降為0時(shí),對(duì)象就被銷毀了页屠;
  • 在對(duì)象生命期中粹胯,其余對(duì)象通過引用來保留或釋放此對(duì)象。保留與釋放操作分別會(huì) 遞增及遞減保留計(jì)數(shù)辰企;

5.2 以 ARC 簡(jiǎn)化引用計(jì)數(shù)

  • 有 ARC 后风纠,就不需要擔(dān)心內(nèi)存管理問題了;
  • ARC 管理對(duì)象生命期的辦法基本上就是:在合適的地方插入“保留”和“釋放”操作牢贸;
  • 由方法反返回的對(duì)象竹观,其內(nèi)存管理語(yǔ)義總是通過方法名來體現(xiàn);ARC 將此對(duì)象確定為開發(fā)者必須遵守的規(guī)則潜索;
  • ARC 只負(fù)責(zé)管理 Objective-C對(duì)象的內(nèi)存臭增;

若方法名以下列詞語(yǔ)開頭,則其返回的對(duì)象歸調(diào)用者所有:

  • alloc
  • new
  • copy
  • mutableCopy

5.3 在 dealloc 方法中只釋放引用并解除監(jiān)聽

  • dealloc 方法里竹习,應(yīng)該做的事情就是釋放指向其他對(duì)象的引用誊抛,并取消原來訂閱的“鍵值觀測(cè)(KVO)”或 NSNotificationCenter 等通知,不要做其他事情整陌;
  • 如果對(duì)象持有文件描述符等系統(tǒng)資源拗窃,那么應(yīng)該專門編寫一個(gè)方法來釋放此資源;這樣的類要和其使用者約定蔓榄,用完資源后必須調(diào)用 close 方法并炮;
  • 執(zhí)行異步任務(wù)的方法不應(yīng)在 dealloc 里調(diào)用;只能在正常狀態(tài)下執(zhí)行的那些方法也不應(yīng)在 dealloc 里調(diào)用甥郑,因?yàn)榇藭r(shí)對(duì)象已處于正在回收的狀態(tài)了;

5.4 編寫 “異常安全代碼”時(shí)留意內(nèi)存管理問題

  • 在捕獲異常時(shí)荤西,一定要注意將 try 塊內(nèi)所創(chuàng)立的對(duì)象清理干凈澜搅;
  • 在默認(rèn)情況下,ARC 不生成安全處理異常所需的清理代碼邪锌。開啟編譯器標(biāo)志后勉躺,可生成這種代碼,不過會(huì)導(dǎo)致應(yīng)用程序變大觅丰,而且會(huì)降低運(yùn)行效率饵溅;

5.5 以弱引用避免保留環(huán)

  • 將某些引用設(shè)為 weak,可避免出現(xiàn)保留環(huán);
  • weak 引用可以自動(dòng)清空妇萄,也可以不自動(dòng)清空蜕企。自動(dòng)清空(autonilling)是隨著 ARC 而引入的新特性咬荷,由運(yùn)行期系統(tǒng)來實(shí)現(xiàn)。在具備自動(dòng)清空功能的弱引用上轻掩,可以隨意讀取其數(shù)據(jù)幸乒,因?yàn)檫@種引用不會(huì)指向已經(jīng)回收過的對(duì)象;

避免保留環(huán)的最佳方式就弱引用唇牧,這種引用經(jīng)常用來表示“非擁有關(guān)系”(nonowning relationship)罕扎。將屬性聲明為unsafe_unretained即可。

5.6 以“自動(dòng)釋放池塊” 降低內(nèi)存峰值

  • 自動(dòng)釋放池排布在棧中丐重,對(duì)象收到 autorelease消息后腔召,系統(tǒng)將其放入最頂端的池里。
  • 合理運(yùn)用自動(dòng)釋放池扮惦,可降低應(yīng)用程序的內(nèi)存峰值臀蛛;
  • @autoreleasepool這種新式寫法能創(chuàng)建出更為輕便的自動(dòng)釋放池;

釋放對(duì)象有兩種方式:一種是調(diào)用realease方法径缅,使其保留計(jì)數(shù)立即遞減掺栅;另一種是調(diào)用autorealease方法,將其加入“自動(dòng)釋放池”中纳猪。自動(dòng)釋放池用于存放那些需要在稍后某個(gè)時(shí)刻釋放的對(duì)象氧卧;

5.7 用“僵尸對(duì)象”調(diào)試內(nèi)存管理問題

  • 系統(tǒng)在回收對(duì)象時(shí),可以不將其真的回收氏堤,而是把它轉(zhuǎn)化為僵尸對(duì)象沙绝。通過環(huán)境變量NSZombieEnabled可開啟此功能;
  • 系統(tǒng)會(huì)修改對(duì)象的 isa指針鼠锈,令其指向特殊的僵尸類闪檬。從而使該對(duì)象變?yōu)榻┦瑢?duì)象;
  • 僵尸類能夠響應(yīng)所有的選擇子购笆,響應(yīng)方式為:打印一條包含消息內(nèi)容及其接收者的消息粗悯,然后終止應(yīng)用程序;

5.8 不要使用retainCount

  • 對(duì)象的保留計(jì)數(shù)看似有用同欠,實(shí)則不然样傍,因?yàn)槿魏谓o定時(shí)間點(diǎn)上的“絕對(duì)保留計(jì)數(shù)”(absolute retain count)都無法反映對(duì)象生命期的全貌;
  • 引入 ARC 之后铺遂,retainCount 方法就正式廢止了衫哥,在ARC下調(diào)用該方法會(huì)導(dǎo)致編譯器報(bào)錯(cuò);

6 塊與大中樞派發(fā)

6.1 理解“塊”這一概念

  • 塊是 C襟锐、C++撤逢、Objective-C 中詞法的閉包;
  • 塊可接受參數(shù),也可返回值蚊荣;
  • 塊可以分配在棾跽或堆上,也可以是全局的妇押。分配在棧上的塊可拷貝到堆里跷究,這樣的話,就和標(biāo)準(zhǔn)的Objective-C 對(duì)象一樣敲霍,具備引用計(jì)數(shù)了俊马;

塊所占的內(nèi)存區(qū)域是分配在棧中的。這也就是說塊只在定義它的那個(gè)范圍內(nèi)有效肩杈。

6.2 為常用的塊類型創(chuàng)建 typedef

  • typedef重新定義塊類型柴我,可令塊變量用起來更加簡(jiǎn)單;
  • 定義新類型時(shí)應(yīng)遵從現(xiàn)有的命名習(xí)慣扩然,勿使其名稱與別的類型相沖突艘儒;
  • 不妨為同一個(gè)塊簽名定義多個(gè)類型別名,如果要重構(gòu)的代碼使用了塊類型的某個(gè)別名夫偶,那么只需修改相應(yīng) typedef中的塊簽名界睁,無須改動(dòng)其他 typedef;

6.3 用 handler 塊降低代碼分散程度

  • 在創(chuàng)建對(duì)象時(shí),可以使用內(nèi)聯(lián)的 handler塊將相關(guān)業(yè)務(wù)邏輯一并聲明兵拢;
  • 在有多個(gè)實(shí)例需要監(jiān)控時(shí)翻斟,如果采用委托模式,那么經(jīng)常需要根據(jù)傳入的對(duì)象來切換说铃,而若改用handler塊來實(shí)現(xiàn)访惜,則可以直接將塊與相關(guān)對(duì)象放在一起;
  • 設(shè)計(jì) API 時(shí)如果用到了 handler塊腻扇,那么可以增加一個(gè)參數(shù)债热,使調(diào)用者可通過此參數(shù)來決定應(yīng)該把塊安排在哪個(gè)隊(duì)列執(zhí)行;

6.4 用塊引用其所屬對(duì)象時(shí)不要出現(xiàn)保留環(huán)

  • 如果塊所捕獲的對(duì)象直接或間接地保留了塊本身幼苛,那么就得當(dāng)心保留環(huán)的問題窒篱;
  • 一定要找個(gè)適當(dāng)?shù)臅r(shí)機(jī)解除保留環(huán),而不能把責(zé)任推給API的調(diào)用者舶沿;

6.5 多用派發(fā)隊(duì)列舌剂,少用同步鎖

  • 派發(fā)隊(duì)列可用來表述同步語(yǔ)義synchronization semantic ,這種做法要比使用 @synchronized 塊或 NSLock 對(duì)象更簡(jiǎn)單暑椰;
  • 將同步與異步派發(fā)結(jié)合起來,可以實(shí)現(xiàn)與普通加鎖機(jī)制一樣的同步行為荐绝,而這么做卻不會(huì)阻塞執(zhí)行異步派發(fā)的線程一汽;
  • 使用同步隊(duì)列及柵欄塊,可以令同步更加高效;

6.6 多用 GCD,少用 @performSelector系統(tǒng)方法

  • performSelector 系統(tǒng)方法在內(nèi)存管理方面容易有缺失召夹。它無法確定將要執(zhí)行的選擇子具體是什么岩喷;
  • performSelector 系列方法所能處理的選擇子太過局限,選擇子的返回值類型及發(fā)送給方法的參數(shù)個(gè)數(shù)都受到限制监憎;
  • 如果想把任務(wù)放在另一個(gè)線程上執(zhí)行纱意,那么最好不要用 performSelector 系列方法,而是應(yīng)該把任務(wù)封裝到塊里鲸阔,然后調(diào)用GCD的相關(guān)方法來實(shí)現(xiàn)偷霉;

6.7 掌握 GCD 及操作隊(duì)列的使用時(shí)機(jī)

  • 在解決多線程與任務(wù)管理問題時(shí),派發(fā)隊(duì)列并非唯一實(shí)現(xiàn)方案褐筛;
  • 操作隊(duì)列提供了一套高層的 Objective-CAPI类少,能實(shí)現(xiàn)純 GCD 所具備的絕大部分功能,而且還能完成一些更為復(fù)雜的操作渔扎,那些操作若改用 GCD 來實(shí)現(xiàn)硫狞,則需另外編碼;

在執(zhí)行后臺(tái)任務(wù)時(shí)晃痴,GCD 并不一定是最佳方式残吩,還有一種技術(shù)叫 NSOperationQueue,它雖然與 GCD 不同倘核,但是卻與之相關(guān)泣侮,開發(fā)者可以把操作以 NSOperation子類的形式放在隊(duì)列中,而這些操作也能夠并發(fā)執(zhí)行笤虫;
使用NSOperationNSOperationQueue的好外:

  • 取消某個(gè)操作旁瘫,如果使用操作隊(duì)列,則想要取消操作是很容易的琼蚯,只需要調(diào)用cancel 就可以了酬凳;若使用 GCD 則沒有辦法取消;
  • 指定操作間的依賴關(guān)系遭庶;
  • 通過鍵值觀測(cè)機(jī)制監(jiān)控 NsOperation 對(duì)象的屬性宁仔;
  • 指定操作的優(yōu)先級(jí);

6.8 通過 Dispatch Group機(jī)制峦睡,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

  • 一系列任務(wù)可歸入一個(gè) dispatch group 之中翎苫,開發(fā)者可以在這組任務(wù)執(zhí)行完畢時(shí)獲得通知;
  • 通過 dispatch group榨了,可以在并發(fā)派發(fā)隊(duì)列里同時(shí)執(zhí)行多項(xiàng)任務(wù)煎谍,此時(shí) GCD 會(huì)根據(jù)系統(tǒng)資源來調(diào)度這些并發(fā)執(zhí)行的任務(wù);

dispatch group 是 GCD 的一項(xiàng)特性龙屉,能夠把任務(wù)分組呐粘。調(diào)用者可以等待這組任務(wù)執(zhí)行完畢满俗,也可以在提供回調(diào)函數(shù)之后繼續(xù)往下執(zhí)行,這組任務(wù)完成時(shí)作岖,調(diào)用者會(huì)得到通知唆垃;

6.9 使用diapatch_once 來執(zhí)行只需要運(yùn)行一次的線程安全代碼

  • 經(jīng)常需要編寫“只需執(zhí)行一次的線程安全代碼”,通過GCD 所提供的 disptch_once來實(shí)現(xiàn)痘儡;
    -標(biāo)記應(yīng)該聲明在 staticglobal 作用域中辕万,這樣的話,在把只需執(zhí)行一次的塊傳給dispatch_once函數(shù)時(shí)沉删,傳進(jìn)去的標(biāo)記也是相同的渐尿;

常用 diapatch_once來編寫線程安全的單例;

6.10 不要使用dispatch_get_current_queue

  • dispatch_get_current_queue 函數(shù)的行為常常與開發(fā)者所預(yù)期的不同丑念,此函數(shù)已經(jīng)廢棄涡戳,只應(yīng)做為開發(fā)調(diào)試用;
  • 由于派發(fā)隊(duì)列是按層級(jí)來組織的脯倚,所以無法單用某個(gè)隊(duì)列對(duì)象來描述“當(dāng)前隊(duì)列”這一概念渔彰;
  • dispatch_get_current_queue 函數(shù)用于解決由不可重入代碼所引發(fā)的死鎖,然而能用此函數(shù)解決的問題推正,也能改用“隊(duì)列待定數(shù)據(jù)”來解決恍涂;

7 系統(tǒng)框架

7.1 熟悉系統(tǒng)框架

  • 許多系統(tǒng)框架都可以直接使用。其中最重要的是 FoundatoinCoreFoundation 植榕,這兩個(gè)框架提供了構(gòu)建應(yīng)用程序所需的許多核心功能再沧;

7.2 多用塊枚舉,少用 for 循環(huán)

  • 遍歷collection有四種方式尊残。最基本的是 for循環(huán)炒瘸,其次是 NSEnumerator遍歷法及快速遍歷法,最新寝衫,最先進(jìn)的方式則是"塊枚舉法";
  • "塊枚舉"法,本身就能通過 GCD 來并發(fā)執(zhí)行遍歷操作顷扩,無須額外編寫代碼。而采用其他遍歷方式則無法輕易實(shí)現(xiàn)這一點(diǎn)慰毅;
  • 若提前知道等遍歷的 collectioin含有何種對(duì)象隘截,則應(yīng)修改塊簽名,指出對(duì)象的具體類型汹胃;
NSEnumerator遍歷法

NSEnumerator是個(gè)抽象基類婶芭,其中只定義了兩個(gè)方法,供子類來實(shí)現(xiàn):

  • (NSArray*)allObjects
  • (id)nextObject

想遍歷數(shù)組時(shí)着饥,則可以這樣來寫代碼:

NSArray *anArray = .....;
NSEnumerator *enumerator = [anArray objectEnumeator];
id object;
while((object = [enumerator nextObject])!= nil) {
    //dosomething with object
}

遍歷字典時(shí)犀农,也可以使用類似的代碼。并且NSEnumerator 有多種枚舉器供選擇宰掉,如反向遍歷等井赌,使用時(shí)可以根據(jù)需要選擇不同的枚舉器谤逼;

快速遍歷

快速遍歷其實(shí)就是在基本for循環(huán)的基礎(chǔ)上加了個(gè) in關(guān)鍵字:

for(id object in anArray){}

基于塊的遍歷方式

NSArray 中定義了下面的方法,實(shí)現(xiàn)最基本的遍歷功能:

-(void)enumeratorObjetsUsingBlock:(void(^)(id object,NSUInteger idx,BOOL *stop))block;

還有其他類似的遍歷方法仇穗,可以接受各種選項(xiàng)來控制遍歷操作

7.3 對(duì)自定義其內(nèi)存管理語(yǔ)義的 collection 使用無縫橋接

  • 通過無縫橋接技術(shù),可以在 Foundation框架中的 Object-C對(duì)象與 CoreFoundation 框架中的 c 語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之間來回轉(zhuǎn)換戚绕;
  • CoreFoundation 層面創(chuàng)建collection時(shí)纹坐,可以指定許多回調(diào)函數(shù),這些函數(shù)表示此collection應(yīng)如何處理其元素舞丛;

無縫橋接就是用來對(duì)Foundation框架和CoreFoundation 框架中的等價(jià)的類進(jìn)行轉(zhuǎn)換耘子,
簡(jiǎn)單的無縫橋接:

NSArray *anArray = ......;
CFArrayRef afarray = (__bridge CFArrayRef)anArray;

關(guān)鍵字__bridge告訴 ARC 如何處理轉(zhuǎn)換所涉及的 OC 對(duì)象,而反向轉(zhuǎn)換(CoreFoundation 類 轉(zhuǎn)換為等價(jià)的 Foundation 類 )則使用關(guān)鍵字__bridge_transfer 來實(shí)現(xiàn)球切;

7.4 構(gòu)建緩存時(shí)選用 NSCache 而非 NSDictionary

  • NSCache 可以提供更優(yōu)雅的自動(dòng)刪減功能谷誓,而且是線程安全的;
  • 可以給 NSCache 對(duì)象設(shè)置上限吨凑,用于限制緩存中的對(duì)象總個(gè)數(shù)及“總成本”捍歪;
  • NSPurgeableDataNSCache 搭配使用,可實(shí)現(xiàn)自動(dòng)清除數(shù)據(jù)功能鸵钝;

7.5 精簡(jiǎn) initialize 與 load 的實(shí)現(xiàn)代碼

  • 在加載階段糙臼,如果類實(shí)現(xiàn)了 load 方法,那么系統(tǒng)就用調(diào)用它恩商。分類里也可以定義此方法变逃,類的 load 方法要比分類中的先調(diào)用,與其他方法不同怠堪, load 方法不參與覆寫機(jī)制揽乱;
  • 首次使用某個(gè)類之前。系統(tǒng)會(huì)向其發(fā)送 initialize消息粟矿。由于此方法遵從普通的覆寫規(guī)范凰棉,所以通常應(yīng)該在里面判斷當(dāng)前要初始化的是哪個(gè)類;
  • initializeload 方法應(yīng)實(shí)現(xiàn)得精簡(jiǎn)一點(diǎn)嚷炉,有助于保持應(yīng)用程序的響應(yīng)能力渊啰;
  • 無法在編譯期設(shè)定的全局變量,可以放在initialize方法城初始化申屹;

7.6 別忘了 NSTimer 會(huì)保留其目標(biāo)對(duì)象

  • NSTimer對(duì)象會(huì)保留其目標(biāo)绘证,直到計(jì)時(shí)器本身失效為止,調(diào)用 invalidate方法可令計(jì)時(shí)器失效哗讥;
  • 反復(fù)執(zhí)行任務(wù)的計(jì)時(shí)器嚷那,很容易導(dǎo)致保留環(huán);
  • 可以擴(kuò)充NSTimer的功能杆煞,用“塊”來打破保留環(huán)魏宽,不過腐泻,除非NSTimer 將來在公共接口里提供此功能,否則必須創(chuàng)建分類队询,將相關(guān)實(shí)現(xiàn)代碼加入其中派桩;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蚌斩,隨后出現(xiàn)的幾起案子铆惑,更是在濱河造成了極大的恐慌,老刑警劉巖送膳,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件员魏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叠聋,警方通過查閱死者的電腦和手機(jī)撕阎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碌补,“玉大人虏束,你說我怎么就攤上這事∧曰郏” “怎么了魄眉?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)闷袒。 經(jīng)常有香客問我坑律,道長(zhǎng),這世上最難降的妖魔是什么囊骤? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任晃择,我火速辦了婚禮,結(jié)果婚禮上也物,老公的妹妹穿的比我還像新娘宫屠。我一直安慰自己,他們只是感情好滑蚯,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布浪蹂。 她就那樣靜靜地躺著,像睡著了一般告材。 火紅的嫁衣襯著肌膚如雪坤次。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天斥赋,我揣著相機(jī)與錄音缰猴,去河邊找鬼。 笑死疤剑,一個(gè)胖子當(dāng)著我的面吹牛滑绒,可吹牛的內(nèi)容都是我干的闷堡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疑故,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼杠览!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起焰扳,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤倦零,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吨悍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹋嵌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年育瓜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了永丝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志秃。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攀细,死狀恐怖禾唁,靈堂內(nèi)的尸體忽然破棺而出今妄,到底是詐尸還是另有隱情定鸟,我是刑警寧澤孽拷,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布荠藤,位于F島的核電站怀喉,受9級(jí)特大地震影響书妻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躬拢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一躲履、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聊闯,春花似錦工猜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拴泌,卻和暖如春魏身,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弛针。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工叠骑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人削茁。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓宙枷,卻偏偏與公主長(zhǎng)得像掉房,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慰丛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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