編寫高品質iOS與OS X代碼的52個有效方法

來源:互聯網
上載者:User

標籤:error   war   委託方   自動   系統架構   之間   queue   不能   方法   

本篇是我閱讀《Effective Objective-C 2.0》的摘要與總結,如有疑問,,我會附上例子解釋。

一、熟悉Objective-C
  1. 瞭解Objective-C語言的起源
  • Objective-C為C語言添加了物件導向特性,是其超集。Objective-C使用動態綁定的訊息結構,也就是說,在運行時才會檢查物件類型。接收一條訊息之後,究竟應執行何種代碼,由運行環境而非編譯器來決定。
  • 理解C語言的核心概念有助於寫好Objective-C程式。尤其要掌握記憶體模型與指標。
  1. 在類的標頭檔中盡量少引入其他標頭檔
  • 除非確有必要,否則不要引入標頭檔。一般來說,應在某個類的標頭檔中使用向前聲明(forward declaring)來提及別的類,並在實現檔案中引入那些類的標頭檔。這樣做可以盡量降低類之間的耦合。
  • 有時無法使用向前聲明,比如要聲明某個類遵循一項協議。這種情況下,盡量把“該類遵循某協議”的這條聲明移至“class-continuation”分類中。如果不行的話,就把協議單獨放在一個標頭檔中,然後將其引入。
  1. 多用literal文法,少用與之等價的方法
    比如多用NSArray *array = @[@1,@2];少用NSArray *array = [NSArray arrayWithObjects:@1,@2,nil];
  • 應該使用literal文法來建立字串,數值,數組,字典。與建立此類對象的常規方法相比,這麼做更加簡明扼要。
  • 應該通過取下標操作來訪問數組下標或字典中的鍵所對應的元素。
  • 用literal文法建立數組或字典時,若值中有nil,則會拋出異常。因此,務必確保值裡不含nil。
  1. 多用類型常量,少用#define預先處理指令
  • 不要用預先處理指令定義常量。這樣定義出來的常量不含類型資訊,編譯器只是會在編譯前據此執行尋找與替換操作。即使有人重新定義了常量值,編譯器也不會產生警告資訊,這將導致應用程式中的常量值不一致。
  • 在實現檔案中使用static const來定義只在編譯單元內可見的常量。由於此類常量不在全域符號表中,所以無需為其名稱加首碼。
  • 在標頭檔中使用extern來聲明全域常量,並在相關實現檔案中定義其值。這種常量要出現在全域符號表中,所以其名稱要加以區隔,通常用與之相關的類名做首碼。
  1. 用枚舉表示狀態、選項、狀態代碼
  • 應該用枚舉來表示狀態機器的狀態、傳遞給方法的選項遺迹狀態代碼等值,給這些值起個易懂的名字。
  • 如果把傳遞給某個方法的選項表示為枚舉型,而多個選項又可同時使用,那麼就將各選項值定義為2的冪,以便通過按位或者操作將其組合起來。
  • 用NS_ENUM與NS_OPTIONS宏來定義枚舉類型,並指明其底層資料類型。這樣做可以確保枚舉是用開發人員所選的底層資料類型實現出來的,而不會採用編譯器所選的類型。
  • 在處理枚舉類型的switch語句中不要實現default分支。這樣的話,加入新枚舉之後,編譯器就會提示開發人員:switch語句並未處理所有的枚舉。
二、對象、訊息、runtime
  1. 理解“屬性”這一概念
  • 可以通過@property文法來定義對象中所封裝的資料。
  • 通過“特質”來指定儲存資料所需的正確語義
  • 在設定屬性所對應的執行個體變數時,一定要遵從該屬性所聲明的語義。
  • 開發iOS程式時,應該使用nonatomic屬性,因為atomic屬性會嚴重影響效能。
  1. 在對象內部盡量直接存取執行個體變數
  • 在對象內部讀取資料時,應該直接通過執行個體變數來讀,而寫入資料時,應該通過屬性來寫。
  • 在初始化方法及dealloc方法中,總是應該直接通過執行個體變數來讀寫資料。
  • 有時會使用惰性初始化技術配置某份資料,這種情況下,需要通過屬性來讀取資料。
  1. 理解“對象等同性”這一概念
  • 若想檢測對象的等同性,請提供“isEqual:”與hash方法。
  • 相同的對象必須具有相同的hash碼,但是兩個hash碼相同的對象卻未必相同。
  • 不要盲目的逐個監測每條屬性,而是應該依照具體需求來制定檢測方案。
  • 編寫hash方法時,應該使用計算速度快而且雜湊碼碰撞幾率低的演算法。
  1. 以“類族模式”隱藏實現細節
  • 類族模式可以把實現細節隱藏在一套簡單的公用介面後面。
  • 系統架構中經常使用類族。
  • 從類族的公用抽象基類中繼承子類時要當心,若有開發文檔,則應首先閱讀。
  1. 在既有類中,使用關聯對象(Associated Object)存放自訂資料
  • 可以通過“關聯對象”機制來把兩個對象連起來。
  • 定義關聯對象時可指定記憶體管理語義,用以模仿定義屬性時所採用的“擁有關係”與“非擁有關係”。
  • 只有在其他做法不可行時才應選用關聯對象,因為這種做法通常會引入難於尋找的bug。
  1. 理解objc_msgSend的作用
  • 訊息由接受者,selector及參數構成。給某對象“發送訊息”也就相當於在該對象上調用方法。
  • 發給某對象的全部訊息都要由“動態訊息派發系統”來處理,該系統會查出對應的方法,並執行其代碼。
  1. 理解訊息轉寄機制
  • 若對象無法響應某個selector,則進入訊息轉寄流程。
  • 通過運行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中。
  • 對象可以將其無法解讀的某些selector轉交給其他對象處理。
  • 經過上述兩步後,如果還是沒辦法處理selector,那就啟動完整的訊息轉寄機制。
  1. 用method swizzling調試黑盒方法
  • 在runtime中,可以向類中新增或替換selector所對應的方法實現。
  • 使用另一份實現來替換原有的方法實現,這道工序叫做method swizzling,開發人員常用此技術向原有視線中添加功能。
  • 一般來說,只有偵錯工具的時候才需要在runtime中修改方法實現,這種做法不宜濫用。
  1. 理解“類對象”的用意
  • 每個執行個體都有一個指向Class對象的指標,用以表明其類型,而這些Class對象則構成了累的繼承體系。
  • 如果物件類型無法在編譯期確定,那麼就應該使用類型資訊查詢方法來探知。
  • 盡量使用類型資訊查詢方法來確定物件類型,而不要直接比較類對象,因為某些對象可能實現了訊息轉寄功能。
三、介面與API設計
  1. 用首碼避免命名空間衝突
  • 選擇與你公司、應用程式或者二者皆有關聯之名稱作為類名的首碼,並在所有代碼中均使用這一首碼。
  • 若自己所開發的程式庫中用到了第三方庫,則應為其中的名稱加上首碼。
  • Apple宣稱保留使用所有兩字母首碼的權利,所以自己所選用的首碼最好是三字母的。
  1. 提供“全能初始化方法”
  • 在類中提供一個全能初始化方法,並於文檔裡指明。其它初始化方法均應調用此方法。
  • 若全能初始化方法與超類不同,則需覆寫超類中對應方法。
  • 如果超類的初始化方法並不適用於子類,那麼應該覆寫這個超類方法,並在其中拋出異常。
  1. 實現description方法
  • 實現description方法返回一個有意義的字串,用以描述該執行個體。
  • 若想在調試時列印出更詳盡的對象描述資訊,則應該實現debugDescription方法。
  1. 盡量使用不可變對象
  • 盡量建立不可變的對象。
  • 若某屬性僅可於對象內部修改,則在“class-continuation分類”中將其由readonly屬性擴充為readwrite屬性。
  • 不要把可變的collection作為屬性公開,而應提供相關方法,一次修改對象中的可變collection。
  1. 使用清晰而協調的命名方式
  • 起名時應遵從標準的Objective-C命名規範,這樣建立出來的介面更容易為開發人員所理解。
  • 方法名要言簡意賅,從左至右讀起來要像個日常用語中的句子才好。
  • 方法名利不要使用縮減後的類型名稱。
  • 給方法嗎起名時的第一要務就是確保其風格與你自己的代碼或所要整合的架構相符。
  1. 為私人方法名加首碼
  • 給私人方法的名稱加上首碼,這樣可以很容易的將其通公用方法區分開。
  • 不要單用一個底線做私人方法的首碼,因為這種做法的預留給蘋果公司用的。
  1. 理解Objective-C錯誤模型
  • 只有發生了可使整個應用程式崩潰的嚴重錯誤時,才使用異常。
  • 在錯誤不那麼嚴重的情況下,可以指派委託方法來處理錯誤,也可把錯誤資訊放在NSError對象裡,經由輸出參數返回給調用者。
  1. 理解NSCopying協議
  • 若想令自己所寫的對象具有拷貝功能,則需實現NSCopying協議。
  • 如果自訂的對象分為可變版本與不可變版本,那麼就要同時實現NSCopying與NSMutableCopying協議。
  • 複製對象時需決定採用淺拷貝還是深拷貝,一般情況下應該盡量執行淺拷貝。
  • 如果你所寫的對象需要深拷貝,那麼可考慮新增一個專門執行深拷貝的方法。
四、協議與分類
  1. 通過委託與資料來源協議進行對象間通訊
  • 委託模式為對象提供了一套介面,使其可由此將相關事件告知其他對象。
  • 將委派物件應該支援的介面定義成協議,在協議中把可能需要吃力的事件定義成方法。
  • 當某對象需要從另外一個對象中擷取資料時,可使用委託模式。在這種情況下,該模式亦稱資料來源協議。
  • 若有必要,可實現含有位段的結構體,將委派物件是否能響應相關協議方法這一資訊緩衝至其中。
  1. 將類的實現代碼分散到便於管理的數個分類之中
  • 使用分類機制把類的實現代碼劃分成易於管理的小塊。
  • 將應該視為私人的方法歸入名叫Private的分類中,以隱藏實現細節。
  1. 總是為第三方類的分類名稱加首碼
  • 向第三方類中添加分類時,總應給其名稱加上你專用的首碼。
  • 向第三方類中添加分類時,總應給其中的方法名加上你專用的首碼。
    26.勿在分類中聲明屬性
  • 把封裝資料所用的全部屬性都定義在主介面裡。
  • 在class-continuation分類之外的其他分類中,可以定義存取方法,但盡量不要定義屬性。
  1. 使用class-continuation分類隱藏實現細節
  • 通過class-continuation分類向類中新增執行個體變數。
  • 如果某屬性在主介面中聲明為唯讀,而類的內部又要用設定方法修改此屬性,那麼就在class-continuation分類中將其擴充為可讀寫。
  • 把私人方法的原型聲明在class-continuation分類裡面。
  • 若想使類遵循的協議不為人所知,則可於class-continuation分類中聲明。
  1. 通過協議提供匿名對象
  • 協議可在某種程度上提供匿名型別。具體的物件類型可以淡化成遵從某些一的id類型,協議裡規定了對象所應實現的方法。
  • 使用匿名對象來隱藏類型名稱或類名。
  • 如果具體類型不重要,重要的是對象能夠響應(定義在協議裡的)特定方法,那麼可使用匿名對象來表示。
五、記憶體管理
  1. 理解引用計數
  • 引用計數機制通過可以遞增遞減的計數器來管理記憶體。對象建立好後,其保留計數至少為1.若保留計數為正,則對象繼續存活。當保留計數降為0時,對象就被銷毀了。
  • 在對象生命期中,其餘對象通過引用來保留或釋放此對象。保留與釋放操作分別會遞增及遞減保留計數。
  1. 以ARC簡化引用計數
  • 在ARC之後,程式員就無須擔心記憶體管理問題了。使用ARC來編程,可省去類中的許多樣板代碼。
  • ARC管理對象生命期的辦法基本上就是:在合適的地方插入保留及釋放操作。在ARC環境下,變數的記憶體管理語義總是通過方法名來體現。ARC將此確定為開發人員必須遵守的規則。
  • ARC只負責管理Objective-C對象的記憶體。尤其要注意:CoreFoundation對象不歸ARC管理,開發人員必須適時調用CFRetain/CFRelease。
  1. 在dealloc方法中只釋放引用並解除監聽
  • 在dealloc方法裡,應該做的事情就是釋放指向其它對象的引用,並取消原來訂閱的索引值觀測或NSNotificationCenter等通知,不要做其他事情。
  • 如果對象持有檔案描述符等系統資源,那麼應該專門編寫一個方法來釋放此種資源。這樣的類要和其使用者約定“用完資源後必須調用close方法。
  • 執行非同步任務的方法不應在dealloc裡調用;只能在正常狀態下執行的那些方法也不應在dealloc裡調用,因為此時對象已處於正在回收的狀態了。
  1. 編寫異常安全的程式碼時留意記憶體管理問題
  • 捕獲異常時,一定要注意將try塊內所創立的對象清理乾淨。
  • 在預設情況下,ARC不產生安全處理異常所需的清理代碼。開啟編譯器標誌後,可產生這種代碼,不過會導致應用程式變大,而且會降低運行效率。
  1. 以弱引用避免重複引用
  • 將某些引用設為weak,可避免出現重複引用。
  • weak引用可以自動清空,也可以不自動清空。自動清空是隨著ARC而引入的新特性,由runtime來實現,在具備自動清空功能的弱引用上,可以隨意讀取其資料,因為這種引用不會指向已經回收過的對象。
  1. 以自動釋放池塊降低記憶體峰值
  • 自動釋放池排布在棧中,對象收到autorelease訊息後,系統將其放入最頂端的池裡。
  • 合理運用自動釋放池,可降低應用程式的記憶體峰值。
  • @autoreleasepool這種新式寫法能建立出更為輕便的自動釋放池。
  1. 用“殭屍對象”調試記憶體管理問題
  • 系統在回收對象時,可以不將其真的回收,而是把它轉化為殭屍對象。通過環境變數NSZombieEnabled可開啟此功能。
  • 系統會修改對象的isa指標,令其指向特殊的殭屍類,從而使該對象變為殭屍對象。殭屍類能夠響應所有的selector回應程式式為:列印一條包含訊息內容及其接受者的訊息,然後終止應用程式。
  1. 不要使用retainCount
  • 對象的保留計數看似有用,實則不然,因為任何給定時間點上的絕對保留計數都無法反映對象生命期的全貌。
  • 引入ARC後,retainCount方法就正式廢止了,在ARC下調用該方法會導致編譯器報錯。
六、block與GCD
  1. 理解block的概念
  • block是C、C++、Objective-C中的詞法閉包。
  • block可接收參數,也可傳回值。
  • block可以分配在棧或堆上,也可以是全域的。分配在棧上的block可拷貝到堆裡,這樣的話,就和標準的Objective-C對象一樣,具備引用計數了。
  1. 為常用的block類型建立typedef
  • 以typedef重新定義block類型,可以令block變數用起來更加簡單。
  • 定義新類型時應遵循現有的命名習慣,勿使其名稱與別的的類型相衝突。
  • 不妨為同一個block簽名定義多個類型別名。如果要重構的代碼使用了block類型的某個別名,那麼只需修改相應的typedef中的block簽名即可,無需改動其他typedef。
  1. 用handler塊降低代碼分散程度
  • 在建立對象時,使用內聯的handler塊將相關商務邏輯一併聲明。
  • 在有多個執行個體需要監控時,如果採用委託模式,那麼經常需要根據傳入的對象來切換,而若改用handler塊來實現,則可直接將block與相關對象放在一起。
  • 設計API時如果用到了handler塊,那麼可以增加一個參數,使調用者可通過此參數來決定應該把block安排在哪個隊列上執行。
  1. 用block引用其所屬對象時不要出現循環參考
  • 如果block所捕獲的對象直接或間接的保留了block本身,那麼就得當心循環參考的問題。
  • 一定要找個適當的時機解除循環參考,而不能把責任推給API的調用者。
  1. 多用派發隊列,少用同步鎖
  • 派發隊列可用來表述同步語義,這種做法要比使用@synchronized塊或NSLock對象更簡單。
  • 將同步與非同步派髮結合起來,可以實現與普通加鎖機制一樣的同步行為,而這麼做卻不會阻塞執行非同步派發的線程。
  • 使用同步隊列及柵欄塊,可以領同步行為更加高效。
  1. 多用GCD,少用performSelector系列方法
  • performSelector系列方法在記憶體管理方面容易有疏失。它無法確定將要執行的selector具體是什麼,因而ARC編譯器就無法插入適當的記憶體管理方法。
  • performSelector系列方法所能處理的selector太過局限了,selector的傳回值類型及發送給方法的參數個數都受到限制。
  • 如果想把人物放在另一個線程上執行,那麼最好不要用performSelector系列方法而是應該把任務封裝到block裡然後調用GCD機制的相關方法來實現。
  1. 掌握GCD及操作隊列的使用時機
  • 在解決多線程與任務管理問題時,派發隊列並非唯一方案。
  • 操作隊列提供了一套高層的Objective-C API,能實現純GCD所具備的絕大部分功能,而且還能完成一些更為複雜的操作,那些操作若改用GCD來實現,則需另外編寫代碼。
  1. 通過Dispatch Group機制根據系統資源狀況來執行任務
  • 一系列任務可貴如一個dispatch group之中。開發人員可以在這組任務執行完畢時獲得通知。
  • 通過dispatch group,可以在並髮式派發隊列裡同時執行多項任務。此時GCD會根據系統資源狀況來調度這些並發執行的任務。開發人員若自己來實現此功能,則需編寫大量代碼。
  1. 使用dispatch_once來執行只需運行一次的安全執行緒代碼
  • 經常需要編寫只需執行一次的安全執行緒代碼。通過GCD所提供的dispatch_once函數,很容易就能實現此功能。
  • 標記應該聲明在static或global範圍中,這樣的話,在把只需執行一次的block傳給dispatch_once函數時,傳進去的標記也是相同的。
  1. 不要使用dispatch_get_current_queue
  • dispatch_get_current_queue函數的行為常常與開發人員所預期的不同。此函數已廢棄,只應做調試之用。
  • 由於派發隊列是按層級來組織的,所以無法單用某個隊列對象來描述當前隊列這一概念。
  • dispatch_get_current_queue函數用於解決由不可重新進入的代碼所引發的死結,然而能用此函數解決的問題,通常也能改用“隊列特定資料”來解決。
七、系統架構
  1. 熟悉系統架構
  • 許多系統架構都可以直接使用。其中最重要的是Foundation與CoreFoundation,這兩個架構提供了構建應用程式所需的許多核心功能。
  • 很多常見任務都能用架構來做,例如音頻與視頻處理、網路通訊
    資料管理等。
  • 請記住,用純C寫成的架構與用Objective-C寫成的一樣重要,若想成為優秀的Objective-C開發人員,應該掌握C語言的核心概念。
  1. 多用塊枚舉,少用for迴圈
  • 遍曆collection有4種方式。最基本的辦法是for迴圈,其次是NSEnumerator遍曆方法及快速遍曆方法,最新、最先進的方式則是塊枚舉法。
  • 塊枚舉法本身就能通過GCD來並發執行遍曆操作,無需另行編寫代碼。而採用其他遍曆方式則無法輕易實現這一點。
  • 若提前知道待遍曆的collection含有何種對象,則應修改塊簽名,指出對象的具體類型。
  1. 隊自訂其記憶體管理語義的collection使用無縫橋接
  • 通過無縫橋接技術,可以在Foundation架構中的Objective-C對象與CoreFoundation架構中的C語言資料結構之前來迴轉換。
  • 在CoreFoundation層面建立collection時,可以指定許多回呼函數,這些函數表示此collection應如何處理其元素。然後,可運用無縫橋接技術,將其轉換成具備特殊記憶體管理語義的Objective-C collection。
  1. 構建緩衝是選用NSCachae而非NSDictionary
  • 實現緩衝時應選用NSCache而非NSDictionary對象。因為NSCache可以提供優雅的自動刪減功能,而且是安全執行緒的,此外,它與字典不同,並不會拷貝鍵。
  • 可以給NSCache對象設定上限,用以限制緩衝中的對象總個數及總成本,而這些尺度則定義了緩衝刪減其中對象的時機。但是絕對不要把這些尺度當成可靠的硬限制,他們僅對NSCache起指導作用。
  • 將NSPurgeableData與NSCache搭配使用,可實現自動清除資料的功能,也就是說,當NSPurgeableData對象所佔記憶體為系統丟棄時,該對象自身也會從緩衝中移除4.
  • 如果緩衝使用得當。那麼應用程式的響應速度就能提高。只有那種重新計算起來很費事的資料,才值得放入緩衝,比如那些需要從網路擷取或從磁碟讀取的資料。
  1. 精簡initialize與load的實現代碼
  • 在載入階段,如果實現了load方法,那麼系統就會調用它。分類裡也可以定義此方法,類load方法要比分類中的先調用。與其他方法不同,load方法不參與覆寫機制。
  • 首次使用某個類之前,系統會向其發送initialize訊息。由於此方法遵從普通的複寫規則,所以通常應該在裡面判斷當前要初始化的是哪個類。
  • load與initialize方法都應該實現的精簡一些,這有助於保持應用程式的響應能力,也能減少引入依賴環的幾率。
  • 無法在編譯期設定的全域常量,可以放在initialize方法裡初始化。
  1. 別忘了NSTimer會保留其目標對象
  • NSTimer對象會保留其目標,直到計時器本身失效為止,調用invalidate方法可令計時器失效,另外,一次性的計時器在觸發完任務之後也會失效。
  • 反覆執行任務的計時器,很容易引入循環參考,進入過這種計時器的目標對象又保留了計時器本身,那肯定會導致循環參考。這種循環參考,可能是直接發生的,也可能是通過對象圖裡的其他對象間接發生的。
  • 可以擴充NSTimer的功能,用塊來打破循環參考。不過,除非NSTimer將來在公用介面裡提供此功能,否則必須建立分類,將相關實現代碼加入其中。


千煌89
連結:https://www.jianshu.com/p/d440d06994ac#
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

編寫高品質iOS與OS X代碼的52個有效方法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.