Effective Objective-C 2.0重讀筆記---1

來源:互聯網
上載者:User

標籤:des   style   color   java   使用   os   io   strong   

上次看這本書的時候匆匆走了一遍,最近不太忙,重溫了一遍,把筆記寫出來~..

有興趣的可以去買一本,感覺這本書還是挺不錯的

由於大部分是在坐車的時候用手機寫的,所以代碼很少,圖也很少

 

1. 盡量使用向前聲明,延後引入標頭檔的時間,這樣可以減少編譯時間
2. 使用arraywithobjects:....如果遇到為空白的變數,就會自動終止,後面的變數便會添加不上,切不會報錯,會造成資料不一致問題,所以盡量使用字面量文法建立相關對象,減少出錯且減少代碼量
3. 使用字面量建立的對象都是不可變的,如果要獲得可變對象,需要調用mutableCopy方法
4. static聲明的變數(imple塊和interf塊中間)只會在本編譯單元內可見。如果不加static,編譯器便會為其建立一個外部符號,此時如果另一個編譯單元裡也聲明了同名變數則會拋出異常資訊
5. 定義常量時應該少用預先處理指令,多用static const,這樣的常量帶有類型資訊,可以減少出錯機會。另外如果一個變數聲明為static const編譯器不會為其建立符號,而是直接替換
6 .如果要讓外部使用你定義的常值變數,就要用在標頭檔聲明extern nsstring *const variable          在實現檔案裡寫nsstring const variable = @"kkkkk",這樣外部就能訪問這個變數,另外這種供全域訪問的變數命名時最好使用該類類名開頭,避免有重複聲明的衝突
7. 使用枚舉時,如果枚舉不需要組合,應該是用NS_ENUM來定義枚舉,這種形式的枚舉編譯時間會自動添加相關的判斷宏定義,使其在新環境中使用新文法,舊環境使用舊文法
8 .如果要使用組合枚舉,最好使用NS_OPTIONS來定義。因為C++中,枚舉運算後所得變數應為其底層資料,不應該是枚舉型變數,所以要轉換,使用上述方法可以自動判斷是否需要實現顯示轉換 
 9. C++訪問成員變數是是利用在編譯期確定的位移量去訪問的,如果增加了一個變數,位於其後的變數將會訪問出錯,因為位移量已經確定了,所以增加後會出錯。OC的做法是將位移量存在類對象裡面,訪問的時候回去累對象裡查詢,所以此時如果再新增加變數總是能正確訪問。見書22頁(ABI和API)
10. @synthesize方法命 = 變數名 在編譯期間就會自動產生get和set方法@dynamic聲明的變數系統不會為其建立執行個體變數和存取方法,而且在編譯器訪問屬性代碼時編譯器即使沒找到存入方法也不會報錯,因為他相信在運行期可以找到,CoreData.就用到了這個技術,因為他的屬性是來自資料庫的不是執行個體變數
11 .屬性後面的小括弧裡寫的是屬性的特質,屬性的特質分為四類①原子性②讀寫權限③記憶體管理語義④方法名
12. 記憶體管理語義weak  strong  assign   copy    unsafe_unretained(該語義不會在其指向的對象銷毀時會自動清空,weak是會的)
13. 在寫帶參數的初始化方法時,如果直接給屬性賦值要注意賦值時記憶體管理語義,定義為copy的,記得要cooy 。
14. 在類對象內部直接存取執行個體變數的速度比用點文法訪問快,因為直接存取不經過方法派發。直接存取不會調用存取方法,如果在存取方法裡做了處理就要注意了並且直接存取不會調用記憶體管理語義。直接存取變數不會觸發KVO,通過屬性訪問有助於排查錯誤。
兩種訪問機制各有其優缺點,折中的方法是擷取時直接存取,設定時調用方法(保證記憶體管理語義)
15. 如果擷取方法中寫了懶載入,那麼訪問變數時應該用點文法訪問,在初始化方法中應該總是用變數直接存取。因為,,這個不解
16. 拋出異常NSException raise: format:
17. 判斷同等性如果是同一個類可以調用isEqual:如果是比較字串同等性應該用起特定的方法以減少效能消耗。數組和字典也有自己的等同性比較方法
18. 如果要給自己定義的類寫等同性判斷方法,也應該複寫isEqual方法,判斷是否為本類,如果是本類則調用自己實現的判斷方法,如果不是則交由父類判斷
如果對象有自己的特定id則直接判斷id即可。
19 .類族可以隱藏抽象基類背後的實現細節,UIButton就是其中之一。類族定義時一般需要提供枚舉的子類和建立子類的類方法
20. 判斷類族時應該用isKindOfClass:如果比較的是確定類執行個體應該用isMemberOfClass
21. oc可以讓一個對象動態關聯其他對象。objc_setAssociatedObject()函數可以給對象設定關聯對象。objc_getAssociatedObjects()函數可以根據給定的鍵將之前存入的關鍵對象取出來。objc_removeAssociatedObject()此方法可以移除指定的關聯對象
但是關聯對象不宜濫用,因為很有可能引入保留環,而保留環不易調試解決問題
22. OC中的方法最終都會轉換為底層的objc_msgSend()方法實現。選擇子和參數合起來稱為訊息,objc_msgSend()能接受兩個或以上的參數。objc_msgSend() 會依據接受者與選擇子的類型調用適當的方法,該方法會在接收者所屬的類中搜尋其方法列表,如果找到就執行,如果沒找到就沿著類的繼承體系向上尋找,等找到這個方法之後再跳轉,如果沒有一直找到,就會執行訊息轉寄操作。執行一個方法似乎要很多步驟,但是首次執行完一個方法後objc_msgSend()會將匹配結果緩衝在快速映射表裡,但是儘管如此還是不是靜態函數綁定操作那樣迅速
23. objc_msgSend_stret()用於處理返回量為結構體的方法,這個函數只能處理能被cpu寄存器容納的結構體,如果結構體太大則會用另一個函數調用
objc_msgSend_fpret()處理返回資料為浮點型的方法。objc_msgSendSuper()用於給超類發送訊息項
尾調用最佳化技術可以對訊息機制進最佳化,若且唯若一個訊息的最後一行調用了另一個函數切不將其傳回值另做他用。
24 .訊息轉寄分為兩個階段,首先會徵詢接受者看其是否能動態添加方法來處理這個未知的選擇子。第一階段響應完畢,此時運行期系統會請求接受者看看有沒有其他對象可以處理這條訊息,如果可以則交由那個對象處理,如果不行則會啟動完整的訊息轉寄機制,這時運行期系統會把訊息相關細節封裝到NSInvocation對象中,再次給接受者一個機會令其設法解決當前這條訊息

 

 

25. 對象收到無法解讀的訊息後,首先調用下面的方法
-(BOOL)resolveInstanceMethod:(SEL)selector
如果尋找的是一個類方法,找不到會調用-(BOOL)resolveClassMethod:(SEL)selector
如果上面的方法返回假,則代表類不會新增方法來處理上面的訊息則會進去下一步
如果上面的方法返回NO則代表本類不會新增方法來處理這個訊息,下一步調用
-(id)forwardingTargetForSelector:(SEL)selector
這個傳回值代表將會把訊息派送給其進行處理,利用這個特性可以類比多重繼承的一些特徵,在類內部指定執行的對象,外界看來好像是該對象親自處理其實是他的內部對象處理
如果上面返回nil代表該對象內部沒有可以處理改訊息的對象,運行時系統此時就會調用完整的訊息轉寄執行
-(void)forwardInvocation:(NSInvocation *)invocation
這個也是指定接受者來執行這個方法,實現此方法時若發現叫用作業不應由本類處理,則需要調用超類的同名方法如果追溯到NSObject類還是處理不了此方法,則會調用doesNotRecognizeSelector:拋出異常
p49有例子
class_addMethod()可以動態添加方法

26. 當執行方法時,系統會到該類的方法列表上去尋找該方法,這些方法均以IMP指標形式來表示例如

修改方法名和函數的映射表即可實現方法的互換
method_exchangeImplementations()這個方法可以互換兩個函數
Method m1 = class_getInstanceMethod(即可擷取相應的方法。
將自定方法跟系統定義的方法互換以後即可增加黑盒日誌功能,在自訂的方法裡做一些事情,然後在調用自訂的方法,看似會產生死迴圈,但需要注意的是運行期他們指向互換,調用的時候其實是調用原方法

 26. 每一個類都有一個isa指標,Class類也是也是OC類的對象,類對象的isa指標指向一個元類用來表述類對象本身所具備的中繼資料,類方法就定義與此



 

27.因為OC沒有命名空間,所以給類命名時容易發生衝突,一般都用首碼解決這個問題,Apple宣稱其保留兩字母首碼的使用權利,一般其提供的架構都是兩個字母的首碼,所以如果你的項目中使用了兩個字母首碼的命名方式,有可能會跟架構產生衝突此外C語言函數和全域變數那些頂級符號都要寫上首碼以免衝突28. 一般對象的初始化都要求外界傳入一部分資料,(UITableViewCell).所以一般我們會提供一個全能初始化方法,讓其他的初始化方法來調用這個初始化方法.資料相關的儲存在全能化初始化方法中執行,這樣的話如果要改變儲存機制,只需要改變該方法即可.這樣便於程式的控制,如果要強制使用自己定義的全能初始化方法可以再init等不可控制的方法中寫上拋異常@throw [NSException exceptionWithName:reason:userInfo:]也可以在init方法中寫上預設值另外如果有繼承體系,一定要維繫全能初始化方法的調用鏈條,這樣才能保持程式的健壯性.此外如果父類的全能初始化方法不適用於子類(矩形和正方形),應該複寫該方法,並在其中拋出異常29. 如果需要經常查看一個類的相關資訊,我們需要複寫該類的description方法[NSString stringWithFormat:@“%@:%p…..”,[]]如果經常要在調試框中查看對象的相關資訊,則需要覆寫debugDescription方法此外NSArray和NSDictionary等類列印的時候不是調用description方法而是調用-(NSString *)descriptionWithLocale:(id)locale方法 30. 盡量建立不可變的對象. 若某屬性僅可用於內部修改,則在類擴充中將其readonly屬性改為readWrite. 不要把可變的collection作為屬性公開,而應該提供相關方法,以此修改對象中可變的collection 31.給類內部使用的私人方法加上首碼字母+_,不要直接使用底線,容易跟蘋果提供的衝突. 給私人方法加首碼可以很明顯看到區別並且察覺共外界使用的方法一般不要輕易改動. 32.OC預設不是異常安全的,如果想使用異常安全的程式碼需要開啟編譯器標誌-fobjc-arc-exceptions即使不使用.....OC現在的做法是只有在極其罕見的情況下才拋出異常,拋出異常後無需考慮恢複問題,而應用程式此時也應該退出,異常屬於極其嚴重的錯誤,比如編寫了一個抽象類別,它的正確使用方法應該是從中整合一個子類,然後使用這個子類,對於默寫必須覆寫的方法,可以再父類裡拋出異常,這樣子類就必須覆寫,否則不能執行對於非致命錯誤,可以通過返回nil/0來處理或者使用NSError,如果初始化方法無法根據傳入的參數來初始化當前執行個體,那麼就可以令其返回nil33. 若想讓自訂的對象具有拷貝功能,則需要實現NSCoying協議. 如果自定的對象分為可變版本和不可變版本,需要同時實現NSCopying和NSMutableCopying協議,一般情況下執行的都是淺拷貝,深拷貝需要一些設定或者自己定義(也可以用Runtime實現深拷貝) 34. OC不支援多繼承,但是支援協議(類似於JAVA中的介面),對象之間經常需要通訊,OC中對象之間通訊模式之一就是委託模式,資料來源(dataSource)和代理(delegate)可以分別處理資料和事件處理,這樣可以達到很好的解耦效果.,代理協議的命名方法通常是類名+Delegate,其方法名一般是去除首碼的類名+相關操作命名.本類的代理成員變數一般聲明為weak,否則容易產生保留環.(例如tableView,如果tableView的delegate是Strong,他就會保留擁有它的控制器,而控制器也擁有tableview的強引用,這樣就產生了保留環) 35.代理協議中的方法有時不會要求代理者全部實現,只實現一部分即可,在方法聲明上加上@optional關鍵字即可標註為可選實現,加上@required標註為必須實現,一般在調用可選方法時我們需要查詢一下代理對象是否已經實現了要調用的方法(用responseToSelector). 36. 如果一個可選協議方法需要經常調用,可用”位段(bitfield)"來標識代理者是否已經是實現了相關方法,因為查詢方法是否實現是個比較費時的操作,如果執行較為頻繁,用標誌位來儲存可以節省效能開銷.struct data{unsigned int filedA : 8;unsigned int filedB : 4;
unsigned int filedC : 2;
unsigned int filedD : 1;
}結構體中fieldA佔8個二進位位,可以表示0~255之間的值,而fieldD可表示0-1Struct{unsigned int didReceiveData : 1;unsigned int didFailedWithEorror : 1;
unsigned int didUpdateProgressTo : 1;
} _delegateFlags;然後覆寫setDelegate:方法,設定代理的時候就去查詢一遍看看代理對象有沒有實現可選方法,同時設定標誌位,後續直接按照標誌位來看是否可以調用相關方法 37. 使用分類機制可以把類的實現代碼劃分成易於管理的小塊,並且調試的時候會列印出分類資訊,易於調試.如果一些方法不需要公開,可以寫在Private分類裡面,然後不再標頭檔中公開(靜態庫) 38. 如果兩個分類中出現了同名方法,分類是不會報錯的,會根據載入順序以最後一個載入的為準,覆蓋掉之前載入的方法.所以寫方法時最好也加上首碼,這樣會大大減少分類方法的覆蓋,…此外分類方法的優先順序比直接寫在類裡面的優先順序高,如果你在分類裡面寫了跟類裡面同樣的方法,分類裡的方法會覆蓋類裡面的方法 39.一般不要再分類中添加屬性,雖然可以使用關聯對象的方法實現分類中屬性的存取,但是這種事不安全的,有可能丟失記憶體管理語義.有時候如果分類中的屬性是獨立的,不跟本類裡面的資料互動是可以定義的.但是即使是這樣頁最好不要使用分類屬性. 40.class-continuation是一種特殊的分類,也叫類擴充,這種分類沒有自己的名字,直接寫在類的實現檔案裡面,寫在這裡的屬性等是不會對外公開的,可以用來聲明或儲存私人的變數方法等(其實OC裡面沒有真正的私人方法,任何變數和方法都可以用runtime那一套東西來訪問) 41.如果使用OC類裡面使用OC++代碼,如果引入了這種標頭檔,則該檔案也要按C++方式編譯(尾碼為.mm)所以使用分類可以不再.h中引入c++的標頭檔,這樣的話暴漏給外界的就是一套OC的介面,隱藏了實現細節.WebKit和CoreAnimation等架構均用到了此種方法類擴充的另一種用法就是將標頭檔中聲明為readonly的屬性改為readWrite,這樣在類的內部,這種屬性是可讀寫的,類的外部是唯讀想遵循私人或者不需要給外界知道的協議可以再類擴充中遵守 42.使用ARC,系統可以自動檢測產生代碼時成對的retain的release操作,如果有多餘的會自動取消.使用ARC調用的是objc_autoreleaseReturnValue方法,該方法可以檢測返回對象後即將要執行的一段代碼,如果跟剛才執行的有成對的操作,則會設定一個全域資料結構的一個標誌位(資料結構根處理器有關)而不執行release操作,objc_retainAutoreleasedReturnValue,檢測標誌位要比執行retain和release快很多,所以最好使用ARC 43. 如果以alloc ,new ,copy ,mutableCopy開頭返回對象的,系統不會做什麼操作,若是以其他形式開頭的,系統會自動在返回的對象上調用autoRelease 44.dealloc方法裡應該做的事情就是釋放指向其他對象的引用,並取消原來訂閱的索引值觀測和通知,一般不要做其他事情,因為此時對象的生命已經接近尾聲如果對象持有的是檔案描述符等系統稀缺資源,那麼應該編寫一個專門的方法來釋放這種資源,這樣的類要與其使用者約定,用完資源後一定要調用close方法.可以給類添加一個標誌位屬性,如果已經執行了close方法則將標誌位置成1,然後再dealloc裡面判斷標誌位是否為1,如果不為1應該跑出異常執行非同步任務不應該在dealloc裡面調用,只能在正常狀態下執行的哪些方法也不應該在dealloc裡面調用 45.捕獲異常時一定要把try裡面所建立的對象清理乾淨.預設情況下ARC不產生異常處理所需要的清理代碼,開啟編譯器標誌以後可以,但是這會導致應用程式變大,降低運行效率 46.合理利用自動釋放池可以降低程式的記憶體峰值 47.編寫程式時最好開啟殭屍對象調試功能,這樣尅幫主們快速定位崩潰所在,如果系統發現有_NSZombie——開頭的執行個體發送訊息,就會中斷 48. Class cla = object_getClass(p); object_getClass()可以擷取類的相關資訊

    Class superCla = class_getSuperclass(cla); class_getSuperclass()可以擷取一個類的父類資訊;

    const char *name  = class_getName(cla);    class_getName(cla)可以獲得一個類的類名

    Class baseZombi = objc_lookUpClass([@"_NSZombie_" UTF8String]); objc_lookUpClass可以尋找

當前系統有沒有指定的類

    zob = objc_duplicateClass(baseZombi, [@"_Zombie_Person"  UTF8String], 0);

objc_duplicateClass可以動態添加類,以指定的模板添加類

    objc_destructInstance(p); objc_destructInstance可以銷毀一個執行個體;

 

abort();可以中斷應用程式的執行

49. 應該盡最大的努力避免使用retainCount,因為你不知道他外面還有多少個自動釋放池,以及系統還會為這個對象執行多少次release操作 49 block塊會保留塊內使用的對象(不管是方法還是變數),如果塊實在對象內部,並且塊內使用了類的成員變數不管有沒有使用self。塊都會保留self、,因為就算使用_XX這樣直接存取成員變數其實是通過self->XX訪問的 50. Block其實也是OC類,只不過他是一種特殊的類,Block類分為三種,分為堆Block棧Block和全域BlockBlock建立的時候預設是棧上的,通過copy函數等會拷貝到堆上,MRC下Block對象也是需要手動管理的

 Block_copy(<#...#>) Block_release(<#...#>)分別執行拷貝和釋放操作;

全域塊是不訪問外界變數的塊,這樣的塊執行copy操作是空操作,因為他一成不變,沒必要拷貝(可以優惠程式執行)

 

51. 如果必須產生保留環則應該在功能執行完畢時通過XX = nil等方法將某一方的引用置為空白,解除保留環。很多網路架構都是先保留後解除

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.