Effective Objective C2 0 編寫高質量代碼的52個有效方法

閒來無事的時候就把effective OC這本書的52個知識點就謄寫了下,沒事的時候能夠看看。程序員

1、熟悉objective-C

1.瞭解OC語言的起源

(1)OC爲C語言添加了面向對象特性,是其超集。OC使用動態綁定的消息結構,也就是說,在運行時纔會檢查對象類型。接收一條消息後,究竟應該執行何種代碼,由運行環境而非編譯器決定。 (2)理解C語言的核心概念有助於寫好OC程序。尤爲要掌握內存模型與指針。objective-c

2.在類的頭文件中儘可能少引用其餘的頭文件

(1)除非確有必要,不然不要引入頭文件。通常來講,應在某個類的頭文件中使用向前聲明說起別的類,並在文件中引入那些類的頭文件。這樣作能夠儘可能下降類之間的耦合。 (2)有時沒法使用向前聲明,好比要聲明某個類遵循一項協議。這種狀況下,儘可能把"該類遵循某協議"的這條聲明移至"class-continuation分類"中。若是不行的話,就把協議單獨放在一個頭文件中,而後將其引入。算法

3.多用字面量語法,少用與之等價的方法

(1)應該使用字面量語法來建立字符串、數組、數值、字典。與建立此類對象的常規方法相比,這麼作更加簡明扼要。 (2)應該經過取下標操做來訪問數組下標或字典中的鍵所對應的元素。 (3)用字面常量沒法建立數組或字典時,若值中有nil,則會拋出異常。所以,務必確保值裏不含nil。編程

4.多用類型常量,少用#define預處理指令

(1)不要用預處理指令定義常量。這樣定義出來的常量不含類型信息,編譯器只是會在編譯前據此執行查找與替換操做。即便有人從新定義了常量值,編譯器也不會產生警告信息,這將致使應用程序中的常量不一致。 (2)在實現文件使用static const來定義"只在編譯單元內可見的常量"(translation-unit-specific constant)。因爲此類常量不在全局符號表中,因此無須爲其添加名稱前綴。 (3)在頭文件中使用extern來聲明全局常量,並在相關實現文件中定義其值。這種常量要出如今全局符號表中,因此其名稱應該加以區隔,一般用與之相關的類名做前綴。數組

5.用枚舉值表示狀態、選項、狀態碼

(1)應該用枚舉值來表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起一個易懂的名字。 (2)若是把傳遞給某個方法的選項表示爲枚舉類型,而多個選項又可同時使用,那麼就將各選項值定義爲2的冪,以便經過按位或操做將其組合起來。 (3)用NS_ENUM與NS_OPTIONS宏來定義枚舉類型,並指明其底層數據類型。這樣作能夠確保枚舉是用開發者所選的底層數據類型實現出來的,而不會採用編譯器所選的類型。 (4)在處理枚舉類型的switch語句中不要實現default分支。這樣的話,加入新枚舉以後,編譯器就會提示開發者:switch語句未處理全部枚舉。緩存

2、對象、消息、運行時

6.理解"屬性"這一律念

(1)能夠用@property語法來定義對象中所封裝的數據。 (2)經過"特質"來指定存儲數據所須要的正確語義。 (3)在設置屬性所對應的實例變量時,必定要遵循該屬性所聲明的語義。 (4)開發iOS程序時應該使用nonatomic屬性,由於atomic屬性會嚴重影響性能。安全

7.在對象內部儘可能直接訪問實例變量

(1)在對象內部讀取數據時,應該直接經過實例變量來讀取,而寫入數據時,則應該經過屬性來寫。 (2)在初始化方法及dealloc方法中,老是應該直接經過實例變量來讀寫數據。 (3)有時會使用惰性初始化技術配置某份數據,這種狀況下,須要經過屬性來讀取數據。網絡

8.理解"對象等同性"這一律念

(1)若想檢測對象的等同性,請提供"is Equal:"與hash方法。 (2)相同的對象必須具備相同的哈希碼,可是兩個哈希碼相同的對象卻未必相同。 (3)不要盲目地逐個檢測每條屬性,而是應該依照具體需求來指定檢測方案。 (4)編寫hash方法時,應該使用計算速度快並且哈希碼碰撞概率低的算法。數據結構

9.以"類族模式"隱藏實現細節

(1)類族模式能夠把實現細節隱藏在一套簡單的公共接口後面。 (2)系統框架中常用類族。 (3)從類族的公共抽象基類中繼承子類時要小心,如有開發文檔,則應首先閱讀。多線程

10.在既有類中使用關聯對象存放自定義數據

(1)能夠經過"關聯對象"機制來把兩個對象連起來。 (2)定義關聯對象時可指定內存管理語義,用以模仿定義屬性時所採用的"擁有關係"與"非擁有關係"。 (3)只有在其餘作法不可行時才應選用關聯對象,由於這種作法一般會引入難於查找的bug。

11.理解objc_msgSend的做用

(1)消息由接收者、選擇子及參數構成。給某對象"發送消息"(invoke amessage)也就至關於在該對象上調用"調用方法"( call amethod)。 (2)發給某對象的所有消息都要由"動態消息派發系統"(dynamic message dispatch system)來處理,該系統會調查出對應的方法,並執行其代碼。

12.理解消息轉發機制

(1)若對象沒法響應某個選擇子,則進入消息轉發流程。 (2)經過運行期的動態方法解析功能,咱們能夠在須要用到某個方法時再將其加入類中。 (3)對象能夠把其餘沒法解讀的某些選擇子交給其餘對象進行處理。 (4)通過上述兩步以後,若是仍是沒辦法處理選擇子,那就啓動完整的消息轉發機制。

13.用"方法調配技術"調試"黑盒方法"

(1)在運行期,能夠向類中新增或替換選擇子所對應的方法實現。 (2)使用另外一份實現來替換原有的方法實現,這道工序叫作"方法調配",開發者經常使用此技術向原有實現中添加新功能。 (3)通常來講,只有調試程序的時候才須要在運行期修改方法實現,這種作法不宜濫用。

14.理解"類對象"的用意

(1)每一個實例都有一個指向Class對象的指針,用以代表其類型,而這些Class對象則構成了類的繼承體系。 (2)若是對象類型沒法在編譯期肯定,那麼就應該使用類型信息查詢方法來探知。 (3)儘可能使用類型信息查詢方法肯定對象類型,而不要直接比較類對象,由於某些對象可能實現了消息轉發功能。

3、接口與API設計

15.用前綴避免命名空間衝突

(1)選擇與你的公司、應用程序或兩者皆有關聯之名稱做爲類名的前綴,並在全部代碼中均適用這一前綴。 (2)若本身所開發的程序庫中用到了第三方庫,則應爲其中的名稱加上前綴。

16.提供"全能初始化方法"

(1)在類中提供一個全能初始化方法,並於文檔裏指明。其餘初始化方法均應調用此方法。 (2)若全能初始化方法與超類不一樣,則須要覆寫超類中對應方法。 (3)若是超類的初始化方法不適用於子類,難麼應該覆寫這個超類方法,並在其中拋出異常。

17.實現description方法

(1)實現description方法返回一個有意義的字符串,用以描述該實例。 (2)若想在調試時打印出更詳盡的對象描述信息,則應該實現debugDescription方法。

18.儘可能使用不可變對象

(1)儘可能建立不可變對象。 (2)若某屬性僅可於對象內部修改,則在"class-continuation分類"中將其由readonly屬性擴展爲readwrite屬性。 (3)不要把可變的collection做爲屬性公開,而應提供相關方法,以此修改對象中的可變collection。

19.使用清晰而協調的命名方式

(1)起名時應聽從OC命名規範,這樣建立出來的接口更容易爲開發者所理解。 (2)方法名要言簡意賅,從左至右讀起來要像個平常用語中的句子纔好。 (3)方法名裏不要使用縮略後的類型名稱。 (4)給方法起名時的第一要務就是確保其風格與你本身的代碼或所要集成的框架相符。

20.爲私有方法名前加前綴

(1)給私有方法的名稱前加上前綴,這樣能夠很容易地將其同公共方法區分開。 (2)不要單用一個下劃線作私有方法的前綴,由於這種作法是預留給蘋果公司用的。

21.理解OC的錯誤模型

(1)只有發生了可以使整個應用程序崩潰的嚴重錯誤時,才應使用異常。 (2)在錯誤不那麼嚴重的狀況下,能夠指派"委託方法"(delegate method)來處理錯誤,也能夠把錯誤信息放在NSError對象裏,經由"輸出參數"返回給調用者。

22.理解NSCopying協議

(1)若想令本身所寫的對象具備拷貝功能,則須要實現NSCopying協議。 (2)若是自定義的對象分爲可變版本與不可變版本,那麼就要同時實現NSCopying與NSMutableCopyig協議。 (3)複製對象時需決定採用淺拷貝仍是深拷貝,通常狀況下應該儘可能執行淺拷貝。 (4)若是你所寫的對象須要深拷貝,那麼考慮新增一個專門執行深拷貝的方法。

4、協議與分類

23.經過委託與數據源協議進行對象間通訊

(1)委託模式爲對象提供了一套接口,使其可由此將相關事件告知其餘對象。 (2)將委託對象應該支持的接口定義成協議,在協議中把可能須要處理的事件定義成方法。 (3)當某對象須要從另外一個對象中獲取數據時,可使用委託模式。這種狀況下,該模式亦稱"數據源協議"(data source protocal)。 (4)如有必要,可實現含有位段的結構體,將委託對象是否能響應相關協議方法這一信息緩存至其中。

24.將類的實現代碼分散到便於管理的數個分類之中

(1)使用分類機制把類的實現代碼劃分紅爲易於管理的小塊。 (2)將應該視爲"私有"的方法納入名爲Private的分類中,以隱藏實現細節。

25.老是爲第三方類的分類名稱加前綴

(1)向第三方類中添加分類時,老是給其名稱加上專用的前綴。 (2)向第三方類中添加分類時,總給其中的方法名加上你專用的前綴。

26.勿在分類中聲明屬性

(1)把封裝數據所用的所有屬性都定義在主接口裏。 (2)在"class-continuation分類"以外的其餘分類中,能夠定義存取方法,但儘可能不要定義屬性。

27.使用"class-continuation分類"隱藏實現細節

(1)經過"class-continuation分類"向類中新增實例變量。 (2)若是某屬性在主接口中聲明爲"只讀",而類的內部又要設置方法修改此屬性,那麼就在"class-continuation分類"中將其擴展爲"可讀寫"。 (3)把私有方法的原型聲明在"class-continuation分類"裏面。 (4)若想使類所遵循的協議不爲人所知,則可於"class-continuation分類"中聲明。

28.經過協議提供的匿名對象

(1)協議可在某種程度上提供匿名類型。具體的對象類型可於淡化成聽從某協議的id類型,協議裏規定了對象所應該實現的方法。 (2)使用匿名對象來隱藏類型名稱(或類名)。 (3)若是具體類型不重要,重要的是對象可以響應(定義在協議裏的)特定方法,那麼可以使用匿名對象來表示。

5、內存管理

29.理解引用計數

(1)引用計數機制能夠遞增遞減的計數器來管理內存。對象建立好以後,其保留計數至少爲1.若保留計數爲正,則對象繼續存活。 當保留計數降爲0時,對象被銷燬。 (2)在對象生命週期中,其他對象經過引用來保留或釋放此對象。保留與釋放操做分別會遞增及遞減保留計數。

30.以ARC簡化引用計數

(1)有ARC以後,程序員就無須擔憂內存管理問題了。使用ARC來編程,可省去類中的許多「樣板代碼」。 (2)ARC管理對象生命期的辦法基本上就是:在合適的地方插入「保留」及「釋放」操做。在ARC環境下,變量的內存管理語義能夠經過修飾符指明,而原來則須要手工執行「保留」及「釋放」操做。 (3)由方法所返回的對象,其內存管理語義老是經過方法名體現。ARC將此肯定爲開發者必須遵照的規則。 (4)ARC只負責管理objective-C對象的內存,尤爲要注意:CoreFoundation對象不歸ARC管理,開發者必須適時調用CFRetain/CFRelease。

31.在dealloc方法中釋放引用並解除監聽

(1)在dealloc方法裏,應該作的事情就是釋放指向其餘對象的引用,並取消原來訂閱的「鍵值觀察」(KVO)或NSNotificationCenter等通知,不要作其餘事情。 (2)若是對象持有文件描述等系統資源,那麼應該專門編寫一個方法來釋放此種資源。這樣的類要和其使用者約定:用完資源後必須調用close方法。 (3)執行異步任務的方法不該在dealloc裏調用;只能在正常狀態下執行的那些方法也不該在dealloc裏調用,由於此時對象已處於正在回收的狀態了。

32.編寫「異常安全代碼」時留意內存管理問題

(1)捕獲異常時,必定要注意將try塊內所創立的對象清理乾淨。 (2)在默認狀況下,ARC不生成安全處理異常所需的清理代碼。開啓編譯器標誌後,可生成這種代碼,不過會致使應用程序變大,並且會下降運行效率。

33.以若引用避免保留環

(1)講某些引用設爲weak,可避免出現「保留環」。 (2)weak引用能夠自動清空,也能夠不自動清空。自動清空(autoniling)是隨着ARC而引入的新特性,由運行期系統來實現。在具有清空功能的弱引用上,能夠隨意讀取其數據,由於這種引用不會指向已經回收過的對象。

34.以「自動釋放池塊」下降內存峯值

(1)自動釋放池排布在棧中,對象收到autorelease消息後,系統將其放入最頂端的池裏。 (2)合理運用自動釋放池,可下降應用程序的內存峯值。 (3)@autoreleasepool這種新式的寫法能建立出更爲輕便的自動釋放池。

35.用「殭屍對象」調試內存管理問題

(1)系統在回收對象時,能夠將其不真的回收,而把他轉化爲殭屍對象。經過環境變量NSZombieEnabled可開啓此功能。 (2)系統會修改對象的isa指針,令其指向特殊的殭屍類,從而使該對象變成爲殭屍對象。殭屍類可以響應全部的選擇子,響應方式爲:打印一條包含消息內容及其接受者的消息,而後終止應用程序。

36.不用使用retainCount

(1)對象的保留計數看似有用,實則否則,由於任什麼時候間點給定的「絕對保留計數」(absolute retain count)都沒法反應對象生命週期的全貌。 (2)引入ARC以後,retainCount方法就真是廢止了,在ARC下調用此方法會致使編譯器報錯。

6、塊與大中樞派發

37.理解「塊」這一律念

(1)塊是C、C++、Objective-C中的詞法閉包。 (2)塊能夠接受參數,也可返回值。 (3)塊能夠分配在棧或堆上,也能夠是全局的。分配在棧上的快能夠拷貝到堆上,這樣的話,就和標準的Objective-C對象一致了,具有引用計數了。

38.經常使用的塊類型建立typedef

(1)以typedef從新定義塊類型,可令塊變量用起來更加簡單。 (2)定義新類型時應聽從現有的命名習慣,勿使其名稱與別的類型相沖突。 (3)不妨爲同一個塊簽名定義多個類型的別名。若是要重構的代碼使用了塊類型的某個別名,那麼只須要修改相應的typedef中的塊簽名便可,無須改動其餘的typedef。

39.用handler塊下降代碼分散程度

(1)在建立對象,可使用內聯的handler塊將業務邏輯一併聲明。 (2)在多個實例須要監控時,若是採用委託模式,那麼常常須要須要根據傳入的對象來切換,而若改用handler塊來實現,則可直接將塊與相關對象放在一塊兒。 (3)設計API時若是用到handler塊,那麼能夠增長一個參數,使調用者能夠經過此參數來決定應該把塊安排在哪一個隊列上執行。

40.用塊引用所屬對象時不要出現保留環

(1)若是塊所捕獲的對象直接或間接的保留了塊自己,那麼就要小心保留環問題。 (2)必定要找個適當的時機接觸保留環,而不能把責任推給API的調用者。

41.多用派發隊列,少用同步鎖

(1)派發隊列能夠用來表述同步語義(synchronization semantic),這種作法要比時候用@synchronized塊或NSLock對象更爲簡單。 (2)將同步或異步結合起來,可實現與普通加鎖機制同樣的同步行爲,而這麼作卻不會阻塞執行異步派發的線程。 (3)使用同步隊列或柵欄快,能夠令同步行爲更加高效。

42.多用GCD,少用performSelector系列方法

(1)performSelector系列方法在內存管理方面容易有疏失。它沒法肯定將要執行的選擇子的具體是什麼,於是ARC編譯器沒法插入適當的內存管理方法。 (2)performSelector系列方法所能處理的選擇子太多侷限了,選擇子的返回值類型及發送給方法的參數個數都受到限制。 (3)若是想把任務方法哦另外一個線程上執行,那麼最好不要用performSelector系列方法,而是應該把任務封裝到塊裏,而後調用大中樞派發機制的相關方法來實現。

43.掌握GCD和操做隊列的使用時機

(1)在解決多線程和任務管理問題時,派發隊列並不是惟一方案。 (2)操做隊列提供了一套高層的Objective-C API,能實現純GCD的所具有的絕大部分的功能,並且還能完成一些更爲複雜的操做,那些操做若是改成GCD實現,則須要另外編寫代碼。

44.經過Dispatch Group機制,根據系統資源情況來執行任務

(1)一系列任務可納入一個dispatch group中。開發者能夠在這組任務執行完畢的時候得到通知。 (2)經過dispatch group,能夠在併發式派發隊列中同時執行多項任務。此時GCD會根據系統資源來調度這些併發執行的任務。開發者若要實現此功能,則會須要寫大量的代碼。

45.使用dispatch_once來執行只須要運行一次的線程安全代碼

(1)常常須要編寫「只須要執行一次的線程安全代碼」(thread-safe single-code excution)。經過GCD所提供的dispatch_once函數,就很容易實現次功能。 (2)標記應該聲明在static或是global做用域中,這樣的話,在只須要執行一次的塊傳給dispatch_once函數時,傳進去的標記也是相同的。

46.不要使用dispatch_get_current_queue

(1)dispatch_get_current_queue函數的行爲經常與開發者所預期的不一樣。此函數已經廢棄,只作調試的時候使用。 (2)因爲派發隊列是按層級來組織的,因此沒法單用某個隊列對象來描述「當前隊列」這一律念。 (3)dispatch_get_current_queue函數經常使用於解決由不可重入的代碼引起的死鎖,然而能用此函數解決的問題,一般也能用改用"隊列特定數據"來解決。

7、系統框架

47.熟悉系統框架

(1)許多系統框架均可以直接使用。其中最重要的是Foundation與CoreFoundation,這兩個框架提供了構建應用程序所需的許多核心功能。 (2)不少常見功能都能用框架來作,例如音頻與食視頻處理、網絡通訊、數據管理等。 (3)請記住:用純C寫的框架和與用objective-c寫成的框架是同樣的重要的,若想成爲優秀的oc開發者,須要掌握C語言的核心。

48.多用塊枚舉,少用for循環

(1)遍歷collection有四種方法。最基本的方式就是for循環,其次是NSEnumerator遍歷法以及快速遍歷法,最新、最早進的方式則是"塊枚舉法"。 (2)"塊枚舉法"自己就是經過GCD來併發執行遍歷操做,無須另行編寫代碼。而採用其餘的遍歷方式則沒法輕易實現這一點。 (3)若提早得知被遍歷的collection是何種對象,則應該修改塊簽名,指出對象的具體類型。

49.對自定義其內存管理語義的collection使用無縫橋接

(1)使用無縫橋接技術,能夠在Foundation框架中的objective-c對象與CoreFoundation框架中的C語言數據結構之間來回轉換。 (2)在CoreFoundation層面建立collection時,能夠指定許多回調函數,此函數表示collection應如何處理元素。而後,可運用無縫橋接技術,將其轉換成具有特殊內存管理語義的objective-c的collection。

50.構建緩存的時候選用NSCache而非NSDictionary

(1)實現緩時應選用NSCache而非NSDictionary對象。由於NSCache能夠提供優雅的自動刪減功能,並且是"線程安全的",此外,它與字典不一樣,並不會拷貝鍵。 (2)能夠給NSCache對象設置上限,用以限制緩存中的對象總個數以及"總成本",而這些尺度則定義了緩存刪除其中對象的時機。但絕對不用這些尺度當成可靠的「硬限制」,他們進隊NSCache起一個引導做用。 (3)將NSPurgeableData與NSCache搭配使用,可實現自動清除數據的功能,也就是說,當NSPurgeableData對象所佔內存爲系統所丟棄時,該對象也會從從緩存中移除。 (4)如緩存使用得當,那麼應用程序的響應速度就能提升。只有那種「從新計算很費勁」的數據,才能得放入緩存,好比那些須要從網絡獲取或磁盤讀取的數據。

51.精簡initialize和load的實現代碼

(1)在加載階段,若是實現了load方法,那麼系統就會調用它。分類裏也能夠定義此方法,類的load方法要比分類中的先調用。與其餘方法不一樣,load方法不會參與覆寫機制。 (2)首次使用某個類以前,系統會向其發送initialize消息。因爲此方法聽從普通的覆寫規則,因此一般應該在裏面判斷當前要初始化的是哪一個類。 (3)load和initialize方法都應該實現得精簡些,這有助於保持應用程序的響應能力,也能減小引入"依賴環"(interdependcy circle)的概率。 (4)沒法再編譯器設定的全局常量,能夠在initialize方法裏進行初始化。

52.別忘了NSTimer會保留其目標對象

(1)NSTimer對象會保留其目標,知道計時器自己失效爲止,調用invalidate方法能夠令計時器失效,另外,一次性的計時器在觸發完任務以後也會失效。 (2)反覆執行任務的計時器(repeating timer),很容易引入保留環,若是這種定時器的目標對象又保留了計時器自己,那確定會致使保留環。這種環裝保留關係,多是直接發生的,也有多是經過對象圖裏其餘對象間接發生的。 (3)能夠擴充NSTimer的功能,用"塊"來打破保留環。不過,除非NSTimer未來在公共接口裏提供此功能,不然必須建立分類,將相關實現代碼加入其中。

相關文章
相關標籤/搜索