熟悉OC
-
了解OC的起源
- oc由smalltalk演化而來枝嘶,使用’消息結(jié)構(gòu)‘而非函數(shù)調(diào)用
- ’消息結(jié)構(gòu)‘其所運行時所之行的代碼由環(huán)境來決定 ((fn))
- ’函數(shù)調(diào)用‘由編譯器決定,按照編譯后的‘virtual table’來查找到底該之行哪個函數(shù)實現(xiàn)
- oc是c的‘超集’冤狡。
- oc由smalltalk演化而來枝嘶,使用’消息結(jié)構(gòu)‘而非函數(shù)調(diào)用
-
在類的頭文件中盡量少的引入其它頭文件
- 頭文件只是聲明,并不需藥知道細節(jié)揍魂,所以使用‘{forward declaring}’有助于減少由于引用的類改變而造成的重新編譯
- I還會阻止循環(huán)引用問題
- 有時候無法使用向前聲明粥烁,比如要聲明某個類遵循一項協(xié)議。這種情況下留荔,第一步要判斷 該類遵循的某項協(xié)議是否是要放在頭文件中吟孙?考慮將其一道?聚蝶?杰妓??碘勉?todo
多用字面量語法巷挥,少用與之等價的方法
要用
NSString * str = @"somestring";
NSArray *arr = @[@"1",@"2"];
代替
NSArray *arr = [NSArrayWithObjects:@"1",@"2",nil];
...代替原來的通過比較老舊的方法-
多用類型定義,少用#define預處理指令
- 預處理指令只是文本替換验靡,無法了解類型信息
- 預處理指令定義的變量可能會被再次通過define替換倍宾,而static const不會如果需要全局性的常量 ,可以使用extern static const來聲明)
-
用枚舉來表示狀態(tài)胜嗓,選項高职,狀態(tài)碼
- 使用NS_ENUM(type,enumname)和NS_OPTIONS (type,enumname)來制作枚舉
- 可以使用FLAG枚舉類做組合操作
- 在處理美劇類型的switch語句中要處理所有的狀態(tài),并且不要事先default分支兼蕊。這樣在新加入一個枚舉狀態(tài)的時候能夠獲得編譯器的通知初厚。
對象件蚕,消息孙技,運行期
-
理解屬性 todo
- 屬性是為實例變量封裝了自定義存取方法的語法糖
- 屬性特質(zhì)
- 原子性->nonatiomic
- 讀寫權(quán)限
- readwrite
- readonly
- 內(nèi)存管理語義
- assign
- strong
- weak
- unsafe_unreatined
- copy
-
在對象內(nèi)部盡量直接訪問實例變量
- 優(yōu)點
- 不經(jīng)過‘method dispatch’步驟,代碼直接訪問實例變量的內(nèi)存排作,訪問速度快
- 可以繞過為相關(guān)屬性定義的‘內(nèi)存管理語義’牵啦,例如在ARC下訪問一個copy屬性的實例變量,那么并不會拷貝該屬性妄痪,只會保留新值并釋放舊值哈雏?
- 爭議點
- 不會觸發(fā)KVO通知,好壞看具體情況
- 折中實踐
- 在對象內(nèi)部讀取數(shù)據(jù)的時候衫生,直接通過實例變量來讀取裳瘪,寫入的時候通過set方法。
- 在初始化方法以及dealloc中罪针,總是通過實例變量來讀寫
- 在lazy load的情況下只能通過屬性訪問
- 優(yōu)點
-
理解對象等同性這一概念
- == 操作符比較的是兩個指針本身彭羹,而不是所指的對象
- 要判斷兩個方法相等請自己定義相等條件(按照實際需求來),重寫并使用NSObject isEqual方法泪酱,并且實現(xiàn)hash方法派殷。
hash方法實現(xiàn)的技巧还最,最好要根據(jù)對象的實例來進行生成,不要相同類型的對象的hash碼都是一樣毡惜。(不要先將所有實例屬性放到string中再進行hash拓轻,會增大內(nèi)存占用率)
~~ -(NSUInterager)hash{
~~ NSUInteger firstNameHash = [_firstName hash];
~~ NSUInteger lastNameHash = [_lastName hash];
~~ NSUInteger ageHash = _age;
~~ return firstNameHash ^ lastNameHash ^ ageHash;
~~ } - 等同性約定:若兩對象相等,但是兩個hash相等的對象卻未必相等
- 特定類實現(xiàn)的Equal方法
- ~~NSString isEqualToString
- ~~NSArray isEqualToArray
~~NSDictionary isEqualToDictionary
- 編寫特定實現(xiàn)的Equal方法的時候也要一并復寫‘isEqual’方法
- 特殊情況:容器中可變類的Equal经伙!
當容器中放入可變類對象的時候扶叉,如果某個對象放入collection中后,再修改這個對象帕膜,那么很可能會兩個相同對象的hash值不同(set中存放NSMutableArray對象)辜梳。[盡量使用不可變對象]有探討這個問題。
-
以"{class cluster}"隱藏實現(xiàn)細節(jié)
- 用于隱藏{abstract class}下面一個系列的實現(xiàn)細節(jié)泳叠,通常使用xxxWithType:工廠方法來進行實例化(ButtonWithType)
- 由于oc沒有辦法指明某個類是‘a(chǎn)bstract’作瞄,所以應該在文檔中寫明,并且在接口中不提供initXXX的成員方法危纫,暗示該類的實例不應該由用戶直接創(chuàng)建宗挥。
- 類族中的類在查詢{type instrospection類型信息}的時候要小心,創(chuàng)建的可能是子類實例
- //todo關(guān)于NSArray 的系統(tǒng)類族
-
在既有類中使用關(guān)聯(lián)對象存放自定義數(shù)據(jù)
-
理解objec_msgSend的作用
- 調(diào)用方法的原理[Object MessageName:parameter]會被編譯器轉(zhuǎn)換為一條標準的C語言函數(shù)調(diào)用种蝶,所調(diào)用的函數(shù)乃是消息傳遞機制中的核心函數(shù)契耿,也就是 objec_msgSend
~~ void objc_msgSend(id self,SEL cmd,...) - **//todo 詳細描述 **
- 調(diào)用方法的原理[Object MessageName:parameter]會被編譯器轉(zhuǎn)換為一條標準的C語言函數(shù)調(diào)用种蝶,所調(diào)用的函數(shù)乃是消息傳遞機制中的核心函數(shù)契耿,也就是 objec_msgSend
-
理解消息轉(zhuǎn)發(fā)機制
- 在無法接收到消息的情況下
用"方法調(diào)配技術(shù)"調(diào)試"黑盒方法"
-
理解“類對象”的用意
接口與api設(shè)計
-
用前綴避免命名空間沖突
- 由于OC沒有namespace,所以在{static link}和{dynamic loader}的時候(更加麻煩)可能會出現(xiàn)‘{duplicate symbol error}’
- 唯一辦法就是給所有名稱都加上適當前綴
- Apple保留使用所有‘兩字母前綴’的權(quán)利螃征,所以我們只能使用三個+
- 不僅是類名搪桂,應用程序中的所有名稱都應該加前綴。
- category 要在category和實現(xiàn)的方法中加上前綴(25條解釋原因)
- 在實現(xiàn)文件中的純c函數(shù)以及全局變量比較容易忽略盯滚,要注意踢械!因為即使在實現(xiàn)文件中,它們也算作是Top Symbol
- 若自己所開發(fā)的項目庫中用到了第三方庫魄藕,并且自己的項目也準備座位第三方庫發(fā)布出去内列,則應為其中的名稱加上前綴。因為如果其它引用你的項目等人也引用了你引用的第三方庫的話背率,就會引起沖突
- 規(guī)定了话瞧,我的前綴為ZYS!
-
提供“{designated initializer}”
- 就是定義一個最全面的初始化方法寝姿,擁有所有條件作為參數(shù)交排,其它有固定條件的初始化方法對它調(diào)用就行了。
- 要在文檔中指明這個是designated initializer
- 如果超類的初始化不適用于子類饵筑,那么應該覆寫這個超類方法埃篓,提供一個默認實現(xiàn),或在其中拋出異常
-
實現(xiàn)description方法
- description方法所返回的描述信息將取代format string里面的%@
- 最好把指針地址也打印出來
- 可以借助Dictionary來進行對屬性的打印
- debugDescription是用于調(diào)試打印的時候調(diào)用的方法翻翩。LLDB po命令
-
盡量使用不可變對象
- 盡量把對外公布出來的屬性設(shè)為之都都许,而且只在的確有必要的時候才對外公布
- 若某屬性僅可于對象內(nèi)部修改稻薇,則在 class-continuation分類中講起由readonly擴展為readwrite屬性//todo 27
- KVC技術(shù)仍然能修改readonly的屬性
- 不要把可辨的collection作為屬性公開,只能在內(nèi)部用可變的collection來維護胶征,只對外提供相關(guān)方法塞椎。
-
使用清晰而協(xié)調(diào)的命名方式
- 給方法起名的第一要務(wù)就是確保其飛哥與你自己的代碼或所要集成的框架相符。
- 如果方法的返回值是新創(chuàng)建的睛低,那么方法的首詞應該是返回值的類型案狠,除非前面還有修飾語(eg:localizeString)。屬性的存取方法不遵守這種命名方式钱雷,因為認為這些方法不創(chuàng)建新的對象骂铁,即便創(chuàng)建了新對象,我們也認為那相當于原有對象罩抗。這些存取方法應該按照其所對應的屬性來命名
- 應該把表示參數(shù)類型的名詞放在參數(shù)前面
- 如果方法要在當前對象上進行操作拉庵,那么就應該包含動詞;若執(zhí)行操作時還需要參數(shù)套蒂,則應該在動詞后面加上一個或多個名詞
- 不要使用str這種簡稱钞支,應該使用string這樣的全稱
- Boolean屬性的取方法加上is。如果方法返回非屬性的Boolean操刀,那么根據(jù)功能選has或is
- 將get這個前綴留給那些借由“輸出參數(shù)“來保存返回值的方法烁挟。(ref參數(shù))
- 委托代理是類名+方法
-
為私有方法名加前綴
- 內(nèi)部方法名稱加上前綴,有助于調(diào)試骨坑,因為可以和共有方法區(qū)分開
- 因為OC無法將方法標識為私有:成ぁ!欢唾!詳見11且警,12,14條匈辱。
-
理解Objective-c的錯誤模型
- 忘記c#振湾、java的異常處理方式
- ARC在默認情況下不是{exception safe}杀迹。如果拋出異常亡脸,本應該在作用域結(jié)尾處釋放的對象現(xiàn)在不會自動釋放了。
- 生成{exception safe}的代碼可以通過設(shè)置編譯器的標識 -fobjc-arc-exceptions來實現(xiàn)树酪,不過將引入一些額外的代碼浅碾,在不拋出異常的時候,也照樣要執(zhí)行這部分代碼续语。
- 只在及其嚴重的情況下才使用異常垂谢,非嚴重錯誤的情況下的編程范式為
- 令方法返回1
- 使用NSError
- 結(jié)構(gòu)
- Error domain(string)
eg:NSURLErrorDomain - Error code(int)
eg:http error code - User Info(dict)
- Error domain(string)
- 用法
- 通過委托協(xié)議來傳遞錯誤
- 通過輸出參數(shù)返回給調(diào)用者
- 結(jié)構(gòu)
-
理解NSCopying協(xié)議
- 要支持拷貝操作,就要實現(xiàn)NSCopying協(xié)議疮茄,該協(xié)議只有下面一個方法
~~- (id) copyWithZone:(NSZone *)zone //zone 無須擔心
~~//實現(xiàn)
~~- (id) copyWithZone:(NSZone *)zone{
~~ ClassName *copy = [[[self class] allocWithZone:zone]initWithXXX...];
~~return copy
~~- 深復制(指針指向的對象都復制一遍)滥朱、淺復制(只復制指針)
- 可變版本的拷貝應該實現(xiàn)NSMutableCoping協(xié)議
- 系統(tǒng)中一般都是淺拷貝根暑,不要自以為是深拷貝
協(xié)議與分類
- 要支持拷貝操作,就要實現(xiàn)NSCopying協(xié)議疮茄,該協(xié)議只有下面一個方法
通過委托與數(shù)據(jù)源協(xié)議進行對象間通信
-
將類的實現(xiàn)代碼分散到便于管理的數(shù)個分類之中
- 分類不僅可以用來做擴展方法,還可以按照需求來進行分類管理
- 分類還利于調(diào)試徙邻,因為分類名稱會出現(xiàn)在其符號中
- 將應該視為私有的方法歸入名為Private的分類中排嫌,以隱藏實現(xiàn)細節(jié)
-
總是為第三方的分類名稱加前綴
- 如果有多個分類都實現(xiàn)了同一個方法,按照加載最晚的哪一個為準缰犁。為了防止這種情況淳地,要在名稱加前綴
-
勿在分類中聲明屬性
- 類所封裝的全部數(shù)據(jù)都應該定義在主接口中!分類只是一種擴展手段帅容,而非封裝數(shù)據(jù)
-
使用“class-continuation分類”隱藏實現(xiàn)細節(jié)
- class-continuation就是實現(xiàn)文件中的@interface颇象,用于隱藏私有變量和方法
-
通過協(xié)議提供匿名對象
- 不同于其他語言的{anonymous object},在OC中是指純id類型的對象并徘,這樣就可以不被任何具體的類來替換(通過Delegate來規(guī)定要實現(xiàn)的行為)遣钳。PS:當做C#里面的接口
內(nèi)存管理
-
理解引用計數(shù)
- like智能指針,創(chuàng)建一個對象麦乞,每被指向一次就reatin,每個指向被釋放就release
- 原則
- 類自己控制+ -
- {retain cycle} 耍贾,在c#、java中會被垃圾回收器判定為island of isolation路幸,會被直接回收荐开;但是在OC中的refercence count中需要我們采用弱引用來解決這個問題,或是修改對象简肴,讓其不在保留另外一個對象晃听。
-
以ARC簡化引用計數(shù)
- CoreFoundation對象不歸ARC管理,開發(fā)者必須適時調(diào)用CFRetain/CGRelease
-
在dealloc方法中只釋放引用并解除監(jiān)聽
- 釋放引用減少計數(shù)器已經(jīng)在ARC環(huán)境下由編譯器做了砰识,我們要做的只是解除監(jiān)聽
- 開銷較大或者系統(tǒng)內(nèi)稀缺資源例如:file descriptor能扒、socket、大塊內(nèi)存等辫狼,不能期待它們的自動dealloc初斑。要單獨編寫一個方法來釋放此種資源。
- //todo 異步
-
編寫“異常安全代碼”時留意內(nèi)存管理問題
以弱引用避免保留環(huán)
以“自動釋放池塊”降低內(nèi)存峰值
用“僵尸對象”調(diào)試內(nèi)存管理問題
-
不用使用retainCount
塊與大中樞派發(fā)
理解“塊”這一概念
為常用的塊類型創(chuàng)建typedef
用handler塊降低代碼分散程度
用塊引用其所屬對象時不要出現(xiàn)保留環(huán)
多用派發(fā)隊列膨处,少用同步鎖
多用GCD见秤,少用performSelector系列方法
掌握GCD以及操作隊列使用的時機
通過Dispatch Group 機制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)
使用dispatch_once來之行只需運行一次的代碼
-
不要使用dispathc_get_current_queue##系統(tǒng)框架
系統(tǒng)框架
熟悉系統(tǒng)框架
多用塊枚舉真椿,少用for循環(huán)
對自定義其內(nèi)存管理語義對collection使用無縫橋接
構(gòu)建緩存時選用NSCache而非NSDictionary
精簡initialize與load的實現(xiàn)代碼
-
別忘了NSTimer會保留其目標對象
未整理