編寫高質量的代碼

·語法糖:計算機語言與另外一套語法等效但是開發者用起來更加方便的語法。

NSNumer

NSNumber *number = @3.14;
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);

NSArray

NSArray *animals = @[@"cat", @"dog"];
NSString *dog = animals[1];

屬性:

優勢:

編譯器就會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”。
這個過程由編譯器在編譯期執行,所以編譯器里看不到這些“合成方法”的源代碼。編譯器還自動向類中添加適當類型的實例變量。
@dynamic:
告訴編譯器,不要自動創建實現屬性所用的實例變量也不要為其創建存取方法。
屬性都是nonatomic的:
歷史原因是:在iOS中使用同步鎖的開銷較大,這會帶來性能問題。而且原子性,并不能保證“線程安全”。

對象等同性:

簡介:

當且僅當“指針值”完全相同時,這兩個對象才相等。NSObject協議中有兩個用于判斷等同性的關鍵方法:

-  (BOOL)isEqual:(id)object;
-  (NSInteger)hash;

要點:

  • 若想檢測對象的等同性,請提供“isEqual:”與hash方法。
  • 相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象卻未必相同。
  • 不要盲目地逐個檢查每條屬性,而是應該依據具體需求來制定檢測方案。
  • 編寫hash方法時,應該使用計算速度快而且哈希碼碰撞幾率低的算法。

類族

要點:

  • 類族模式可以把實現細節隱藏在一套簡單的公共接口后面。
  • 系統框架經常使用類族
  • 從類族的公共抽象基類中繼承子類時要當心,若有開發文檔,則應首先閱讀。

關聯對象

設置關聯對象值:

void objc_setAssociatedObject(id object, void *key,id value, objc_AssociationPolicy policy)

根據給定的鍵從某對象中獲取相應的關聯對象值:

id objc_getAssociatedObject(id object, void *key)

移除指定對象的全部關聯對象:

void objc_removeAssociatedObjects(id object)

應用場景:

給系統 UIAlertView添加block塊的回調,但是沒有子類話UIAlertView然后在里面封裝的好,所以所以控件最好都自己封裝一下它的內部事件。

objc_msgSend

動態綁定:

調用的函數直到運行期才能確定。待調用的函數地址無法硬編碼到指令之中,而是要在運行期讀取出來。

給對象發消息:

id returnValue = [someObject messageName:parameter];
會轉換成一條標準的c語言函數調用,調用的函數是消息傳遞機制中的核心函數,叫做objc_msgSend,其“原型”如下:
void objc_msgSend(id self, SEL cmd, ...)
id returnValue = objc_msgSend(someObject,
                             @selector(messageName:),
                             parameter);

實現步驟:

在someObject中搜尋其方法列表,如果能找到“messageName”這個方法,就跳至其實現代碼。如果找不到就沿著繼承體系繼續向上查找,等找到合適的方法之后再跳轉。如果最終還是找不到相符的方法,那就執行“消息轉發”操作。

弊端:

調用一個方法需要很多步驟。
objc_msgSend會將匹配結果緩存在“快速映射表(fast map)里面”,每個類都有這樣一塊緩存,這種“快速執行路徑”還是不如“靜態綁定的函數調用操作”那樣迅速。

實現流程:

//是否在此類中添加這個方法
+ (BOOL)resolveInstanceMethod:(SEL)selector;
//能不能把這條消息轉給其他接收者來處理。
- (id)forwardingTargetForSelector:(SEL)selector;
//如果來到這步,只能啟用完整的消息轉發機制。首先創建NSInvocation對象,把尚未處理的那條消息有關的全部細節都封裝其中。
- (void)forwardInvocation:(NSInvocation *)invocation;
消息轉發.png

方法互換:

void  method_exchangeImplementations(Method m1, Method m2);

應用場景:

可以通過這一手段來為既有的方法實現增添新功能。

  1. 比方說在調用lowercaseString的時記錄某些信息,這時就可以通過方法交換來達成目的。
  2. NSArray里面添加數組的時候,加nil會崩潰,所以就可以在add方法里面加上空判斷。
    這個方法最好是不用,最好是在調試的時候使用,很容易出問題。

獲取方法實現:

Method class_getInstanceMethod(Class aClass, SEL aSelector);

理解“類對象”的用意

簡介:

對象類型并非在編譯器就綁定好了,而是要在運行期查找。
“在運行期檢視對象類型”這一操作叫做“類型信息查詢”。

id類型本身定義:

typedef struct objc_object {
  Class isa;
} *id;

isa指針描述了實例所屬的類。

isMemberOfClass:能夠判斷出對象是否為某個特定類的實例。
isKindOfClass:能夠判斷出對象是否為某類或其派生類的實例。

前綴

寫類的時候要加上命名前綴:

Apple宣稱其保留使用所有“兩字母前綴”的權利,所以你自己選用的前綴應該是三個字母的。
需要加前綴的地方:
1)類命
2)分類及分類中的方法
3)純c函數
4)全局變量

如果應用程序自身和其所用的程序庫都引入了同名的第三方庫:

第三方庫應該加上前綴以避免命名沖突。

要點:

1.選擇與你的公司、應用程序或二者皆有聯系之名稱作為類名的前綴,并在所有代碼中均使用這一前綴。
2.若自己所開發的程序庫中用到了第三方庫,則應為其中的名稱加上前綴。

全能初始化方法

介紹:令其他初始化方法都來調用它。只有在這個方法內部才會存儲內部數據。這樣的話,當底層數據存儲機制改變時,只需修改次方法的代碼就好,無須改動其他初始化方法。

要點:

1)在類中提供一個全能初始化方法,并于文檔里指明。其它初始化方法均應調用此方法。
2)若全能初始化方法與超類不同,則需覆寫超類中的對應方法。
3)如果超類的初始化方法不適用于子類,那么應該覆寫這個超類方法,并在其中拋出異常。

description

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

為私有方法名加前綴

原因:

類的公共API不變隨意改動,私有的API卻可以修改。
方式:

- (void)p_privateMethod {
/* ... */
}

要點:

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

理解oc的錯誤模型
簡介:

拋出異常,那么本應再作用域末尾釋放的對象現在卻不會自動釋放了。即使用ARC,也很難寫出在拋出異常時不會導致內存泄漏的代碼。
用處:
異常拋出以后,無需考慮恢復問題,應用程序在此時應該退出。
異常只用于極其嚴重的錯誤。

第26條 勿在分類中聲明屬性
要點:

把封裝數據所用的全部屬性都定義在主接口里。
在“class-continuation分類”之外的其他分類中,可以定義存取方法,但盡量不要定義屬性。

原因:
1)分類是一種手段,目標在于擴展類的功能,而非封裝數據。
2)而且重寫set和get方法需要把相似的代碼寫很多遍,內存管理容易出問題。
3)通過屬性特質修改了某個屬性的內存管理語義,而此時還要記得要設置方法中也得修改設置關聯對象時所用的內存管理語義。

第五章 引用計數

簡介:
一個對象所占的內存在“解除分配”之后,只能放回“可用內存池”。內存不一定被復寫了。

autorelease

在方法返回的時候調用autorelease。它會在稍后釋放對象,從而給調用者留下了足夠長的時間。保證對象跨越“方法調用邊界”后一定存活。

autorelease能延長對象生命期,使其跨越方法調用邊界后依然存活一段時間。

ARC

優點:

1)增加程序效率,比如一對release和retain會被抵消。
2)arc以后不用考慮set方法里面的“邊界情況”
3)arc會優化處理返回aotorelease的情況。

要點:

  • ARC之后,可省去類中許多“樣板代碼”。
  • ARC機制:在合適的地方插入“保留”及“釋放”操作。
    變量的內存管理語義可以通過修飾符指明。
  • ARC只負責管理OC對象的內存。coreFoundation對象不歸ARC管理,開發者應調用CFRetain/CFRelease.
  • 由方法返回的對象,其內存管理語義總是通過方法名來體現。這是開發者必須遵守的規則。

塊會自動保留其捕獲的全部對象。

dealloc

注意:

1)雖說dealloc中釋放引用,但是開銷較大或系統內稀缺的資源則不在此則。像是文件描述符、套接字、大塊內存等。不能指望dealloc方法必定在某個特定的時機調用。
2)不要隨意在delloc里面調用方法。

要點:

1)delloc里面應該做的事情就是釋放對象、KVO、通知等。不要做其他事情。
2)如果對象持有大量資源,那么應該專門創建一個方法來釋放此資源,這樣的類要和使用者約定,用完資源后必須調用close方法。
3)異步執行的方法不應在delloc里調用,delloc最好不要調用方法,因為此時對象已處于正在回收的狀態。

異常處理

簡介:

@finally塊,無論是否拋出異常,其中的代碼都會保證運行,而且只運行一次。
要點:
1)捕獲異常時,一定要注意將try塊內所創立的對象清理干凈。
2)在默認情況下,ARC不生成安全處理異常所需的清理代碼。開始編譯器標志后,可生成這種代碼,不過會導致程序變大,而且會降低運行效率。關鍵字是-fobjc-arc-exceptions,在拋出異常的文件加上這個標識。

保留環

weak修飾屬性特質

自動釋放池

main里面的自動釋放池:理解成為最外圍捕獲全部自動釋放對象所用的池。

自動釋放池嵌套用的好處是:可以借此控制應用程序的內存峰值,使其不致過高。

應用場景:

監控內存用量,判斷其中有沒有需要解決的問題,如果沒完成這一步,就別急著優化。雖然自動釋放池塊的開銷不太大,但畢竟還是有的,所以盡量不要建立額外的自動釋放池。

要點:

  • 自動釋放池排布在棧中,對象收到autorelease消息后,系統將其放入最頂端的池里。
  • 合理運用自動釋放池,可降低應用程序的內存峰值。
  • @autoreleasepool這種新式寫法能創建出更為輕便的自動釋放池。

僵尸對象

簡介:

僵尸對象是調試內存管理問題的最佳方式。

NSString和NSNumber的引用計數

NSString:OC對NSString有特殊照顧。所有的NSString的引用計數默認初始值都會非常非常大。是一個常量。

NSNumber也類似,它使用了一種叫做“標簽指針”的概念來標注特定類型的數值。

內存區域

分為5個區域:

1)棧區:由編譯器自動分配并釋放,存放函數的參數值,局部變量等。棧是系統數據結構,對應線程/進程是唯一的。
優點:是快速高效
確定:數據不靈活
特點:棧無需釋放,也就沒有釋放函數。
2)堆區:需要程序員自己釋放
3)全局區(靜態區):全局變量和靜態變量的存儲是放在一起的,結束的時候由系統釋放。
全局區又分為:初始化的和未初始化的。
4)文字常量區:存放常量字符串,程序結束后由系統釋放。
5)程序代碼區:存放函數的二進制代碼。

強大之處:
在聲明它的范圍里,所有變量都可以為其所捕獲。
但是捕獲的變量是不可以修改的,除非變量加上__block標識。

捕獲:

如果塊所捕獲的變量是對象類型,那么就會自動保留它。
如果通過讀取或寫入操作捕獲了實例變量,那么就會自動把self變量一起捕獲了,因為實例變量與self所指代的實例關聯一起的。
塊拷貝的并不是對象本身,而是指向這些對象的指針變量。

全局塊、棧塊及堆塊

全局塊:這種塊不會捕捉任何狀態(比如外圍的變量等),這種塊就相當于是一個單例。
關鍵點:
如果塊所捕獲的對象直接或者間接地保留了塊本身,那么就得當心保留環問題。
一定要找個適當的時機解除保留環,而不能把責任推給API的調用者。

框架

用C語言來實現API的好處是:可以繞過OC運行期系統,從而執行速度。

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

原因:

若是在self對象上頻繁加鎖,那么程序可能要等另一段與此無關的代碼執行完畢,才能繼續執行當前代碼,這么做其實并沒有必要。

多個獲取方法可以并發執行,而獲取方法和設置方法之間不能并發執行。

柵欄:在隊列中,柵欄塊必須單獨執行,不能與其他塊并行。并發隊列如果接下來處理的塊是個柵欄塊,那么就一直等當前所有冰法快都執行完畢,才會單獨執行這個柵欄快。待柵欄塊執行完畢以后,再按正常方法繼續向下處理。

要點:

派發隊列可用來表述同步語義,這種做法要比使用@synchronized塊或NSLock對象更簡單。
2)將同步和異步派發結合起來,可以實現與普通加鎖機制一樣的同步操作,而這么做卻不會阻塞執行異步派發的線程。
3)使用同步隊列及柵欄塊,可以令同步行為更加高效。

NSCache勝過NSDictionary

原因:

1)NSCache,當系統資源將要耗盡時,它可以自動刪減緩存。還會先行刪減“最永久未使用的對象。”
2)NSCache是線程安全的。開發者在不編寫枷鎖代碼的前提下,多線程便可同時訪問NSCache。
3)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容