1. 程式人生 > >《iOS開發進階》閱讀總結

《iOS開發進階》閱讀總結

1.引用計數的作用 物件A向物件B傳遞引數物件M ,物件M可能成為物件B的成員變數,也可能只是臨時使用,如果不用引用計數控制,則無法在正確的時間釋放物件M。
案例一:物件A將物件M傳遞給物件B後,物件B複製一份物件M的拷貝M1,然後物件A釋放物件M,這樣由物件B繼續維護物件M1的生命週期。 缺點:會帶來更多的記憶體申請、複製、釋放的工作,影響效能。
案例二:物件A將物件M傳遞給物件B後,物件A不再維護物件M的生命週期,由物件B來負責維護物件M的生命週期。 缺點:物件A與物件B的耦合度很高,需要程式碼維護者明確記住這種程式設計約定,而且由於物件M在物件A中申請,在物件B中釋放,則物件M的記憶體管理程式碼分散在不同物件中,管理困難,如果此時物件B要向物件C傳遞物件M,則複雜度進一步提高,影響程式碼結構。
而使用引用計數就能規避這個問題,所有物件都遵循這個原則,則物件的生命週期管理可以完全交給引用計數。
引用計數存在一個瑕疵,就是可能出現迴圈引用,所以需要引入弱引用概念。

2.ARC的使用
Core Foundation 物件不在ARC管理下,需要自己維護物件的引用計數。 與CF物件互動是,需要用到CFRetain(obj),CFRelease(obj),可以直觀的認為與OC的retain、release等價 在ARC環境下,將一個CF物件轉換成OC物件,需要用到bridge相關關鍵字: __bridge:只做型別轉換,不修改相關物件的引用計數,原有的CF物件在不用時,需要呼叫CFRelease方法。 __bridge_retained:型別轉換後,將相關物件的引用計數加一,原來的CF物件不用時,需要呼叫CFRelease方法。 __bridge_transfer:型別轉換後,將該物件的引用計數交給ARC管理,CF物件不用時,不需要呼叫CFRelease方法。
3.GCD(Grand Central Dispatch)的使用
dispatch_queue_t 需要手動管理釋放 dispatch_queue_t urls_queue = dispatch_queue_create("com.ginhoor.url",NULL); dispatch_release(urls_queue); UIApplication BackgroundTask可以使應用在進入後臺後,最多10分鐘的時間在後臺長久執行。
Block的特點 1.程式塊可以在程式碼中以內聯的方式定義。 2.程式塊可以訪問建立它的範圍內的可用變數。
Block的特性
預設情況下,block中外部變數是複製進去的,即寫操作不對原變數生效,如果是引用型別,block會將其引用計數+1,加上__block可以使其寫操作對原變數生效。
UIWindow的主要作用 從繼承關係上,UIWindow繼承自UIView,所以UIWindos除了具有UIView的所有功能外,還增加了一些特有的屬性和方法。常用的就是UIWindow的makeKeyAndVisible方法使整個程式介面可見。 1.UIWindow是最頂層的介面容器,預設不包含任何內容,包含應用顯示所需要的所有UIView。 2.傳遞觸控訊息和鍵盤事件給UIView。
UIWindow的特性 1.與建立UIview不同,UIWindow一旦被建立,它就自動被新增到整個介面上。 2.如果建立的UIWindow需要處理鍵盤事件,那就需要合理的將其設定為keyWindow。我們可以通過makeKeyWindow和resignKeyWindow的方法將自己建立的UIWindow例項設定成keyWindow。
UIWebView 可以使用模版檔案渲染HTML,例如MGTemplateEngine、GRMustache OC與JavaScript相互呼叫: UIWebView呼叫原生介面為非同步呼叫,原生介面使用stringByEvaluatingJavaScriptFromString為同步呼叫。同步呼叫會阻塞原有javaScript程式碼的執行,所以用JavaScript端用iFrame的插入延後之行。另外stringByEvaluatingJavaScriptFromString必須在主執行緒上執行,而主執行緒的執行時間過長會阻塞UI的更新,所以要儘量縮短執行時間。 無法通過[obj becomeFirstResponder]讓游標輸入焦點轉移到UIWebView上。
CoreText CoreText是用於處理文字和字型的底層計數,它直接和Core Graphics(又稱為Quartz)打交道,具有高效的排版功能。 Quartz是一個2D圖形渲染引擎,能夠處理OSX和iOS中的圖形顯示問題。 CoreText處於非常底層,上層的UI控制元件(包括UILabel、UITextFiled、UITextView)和UIWebView都是基於CoreText來實現的。
CoreText與UIWebView對富文字的處理 1.CoreText佔用的記憶體更少,渲染速度更快,UIWebView佔用記憶體多,渲染速度慢。 2.CoreText在渲染介面前就可以精確地獲得顯示內容的高度(只要有了CTFrame即可),而UIWebView 只有在渲染出內容後,才能獲得控制元件高度(而且還需要用JavaScript程式碼來獲取)。 3.CoreText的CTFrame可以在後臺執行緒渲染,UIWebView的內容只能在主執行緒渲染。 4.基於CoreText可以做更好的原生互動效果,而UIWebView的互動效果都是用JavaScript來實現的,在互動效果上會有一些卡頓存在。
CoreText的劣勢 1.CoreText渲染出來的內容不能像UIWebView那樣方便的支援內容複製。 2.基於CoreText的排版需要自己處理很多複雜邏輯,例如需要自己處理圖片和文字的混排相關邏輯,也需要自己實現連結點選操作的支援。
開發技巧 用CoreGraphics可以實現截圖
為什麼ViewDidUnload被廢棄 在iOS4、5中,記憶體不足,收到MemoryWarning時,系統會呼叫沒在當前介面上的ViewController的viewDidUnload方法。 在iOS6中UIView有一個CALayer的成員變數,CALayer是具體用於將自己畫到螢幕上。CALayer是一個bitmap影象的容器,當UIView呼叫自身的drawRect時,CALayer才建立這個bitmap影象類。具體佔用記憶體的其實是一個bitmap影象類,CALayer只佔48Bytes,UIView只佔96Bytes。而一個iPad的全屏UIView的bitmap類會佔到12MB的大小。在iOS6中,當系統發出MemoryWarning時,系統會自動回收bitmap類,但不回收UIView和CALayer類。這樣既能回收大部分記憶體,又能在需要bitmap類的時候,通過呼叫UIVIew的drawRect:方法重建。
isa 物件指標 物件的方法都儲存在類的可變區域內,在OC2.0中並未在標頭檔案中將實現暴露出來,在OC1.0中,方法的定義列表是一個methodLists的指標的指標,通過修改該指標的指向指標的值,就可以動態的胃某一個類增加成員方法,也就是category的實現原理。同時也說明了category只能為物件增加方法,卻不能增加成員變數(通過objc_setAssociatedObject 和objc_getAssociatedObject 可以變相新增成員變數,但由於實現機制不同,並不是真正改變了記憶體結構)。
Method Swizzling API 說明 class_replaceMethod 替換類方法的定義。 有兩種不同的行為:當類沒有想替換的方法時,該方法會呼叫class_addMethod 來為該類增加一個新方法。如果存在想替換的方法,則直接替換,所以需要傳入types引數。
method_exchangeImplementations 交換兩個方法的實現。 內部實現其實就是呼叫了兩次method_setImplementation
method_setImplementation 設定一個方法的實現。
Tagged Pointer 如果儲存一個NSNumber物件,正常情況下就是一個NSInteger的普通變數,那麼32位CPU下佔4個位元組,64位CPU下佔8個位元組,而一個指標在32位CPU下為32/8 = 4個位元組 ,在64位CPU下為8個位元組。 一個普通的iOS程式,如果沒有Tagged Pointer物件,從32位機器遷移到64位機器中,雖然邏輯沒有變化,但是NSNumber、NSDate一類的物件佔用記憶體會翻倍。 效率上,為了儲存和訪問一個NSNumber物件,我們需要在堆上為其分配記憶體,另外還要維護它的引用計數,管理它的生命週期。這些都給程式增加了額外的邏輯,造成執行效率上的損失。
所以在64位CPU下,NSNumber的指標被拆成兩個部分,一部分直接儲存資料,另一部分作為特殊標記,表示這是一個指標,不指向任何一個地址。當8個位元組可以承載用於表示的數值時,系統就會以Tagged Pointer的方式生成指標,將值直接儲存到了指標裡,如果8位元組承載不了時,又會用以前的方式來生成普通的指標。
特點: 1.Tagged Pointer 專門用於儲存小的物件,例如NSNumber和NSDate。 2.Tagged Pointer 指標的值不再是地址了,而是真正的值。所以,實際上它不再是一個物件了,它只是一個披著物件“皮”的普通變數而已。所以它的記憶體不儲存在堆中,也不需要malloc和free。 3.記憶體讀取上有著以前3倍的效率,建立比以前快106倍
注意事項: Tagged Pointer並不是真正的物件,它並沒有isa指標,不能直接訪問Tagged Pointer的isa成員(obj->isa),需用isKindOfClass和object_getClass代替。
64位下的isa指標優化 在32位環境下,物件的引用計數都儲存在一個外部的hash表中,每一個物件的Retain操作,實際上包括瞭如下5個步驟: 1.獲得全域性的記錄引用計數的hash表。 2.為了執行緒安全,給該hash表加鎖。 3.查詢到目標物件的引用計數值。 4.將該引用計數值+1,寫回hash表。 5.給該hash表解鎖。
在64位環境下,isa指標也是64位,實際作為指標部分只用到了其中33位,剩餘31位蘋果使用了類似Tagged Pointer 的概念,其中19位 將儲存物件的引用計數,這樣引用計數的操作只需要修改這個指標就可以了。只有當引用計數超過19位,才會將引用計數存到外部表,而這個情況很少,所以這樣引用計數的效率會更高。 與前面5個步驟對應,在64位環境下,新的Retain操作包括如下5個步驟: 1.檢查isa指標上面的標記為,看引用計數是否儲存在isa變數中,如果不是,則是用以前的步驟,否則,執行第2步。 2.檢查當前物件是否正在釋放,如果是,則不做任何事情。 3.增加該物件的引用計數,但時並不是馬上寫會isa變數中。 4.檢查增加後的引用計數的值是否能夠被19位表示,如果不是,則切換成以前的辦法,否則執行第5步。 5.進行一個原子的寫操作,將isa的值寫會。
雖然步驟都是5步,但是由於沒有了全域性加鎖的操作,所以引用計數的更改更快了。

isa的bit位含義 從低位到高位 bit位:1bit,變數名:indexed,意義:0表示普通isa,1表示Tagged Pointer。 bit位:1bit,變數名:has_assoc,意義:表示該物件是否有過associated物件,如果沒有,在析構釋放記憶體可以更快。 bit位:1bit,變數名:has_cxx_dtor,意義:表示該物件是否有C++或ARC的解構函式,如果沒有,在析構釋放記憶體時可以更快。 bit位:30bits,變數名:shiftcls,意義:類指標。 bit位:9bits,變數名:magic,意義:其值固定位0xd2,用於除錯時分辨物件是否未完成初始化。 bit位:1bit,變數名:wealky_referenced,意義:表示該物件是否指向過weak物件,如果沒有,在析構釋放記憶體時後可以更快。 bit位:1bit,變數名:deallocating,意義:表示該物件是否正在析構。 bit位:1bit,變數名:has_sidetable_rc,意義:表示該物件的引用計數是否達到無法直接在isa中儲存。 bit位:19bits,變數名:extra_rc,意義:表示該物件超過1的引用計數值,例如該物件的引用計數是6,則extra_rc的值為5。
Block的內部資料結構定義 1.isa指標,所有物件都有該指標,用於實現物件相關功能。 2.flags,用於按bit位表示一些block的附加資訊。 3.reserved,保留變數。 4.invoke,函式指標,指向具體的block實現的函式呼叫地址。 5.descriptor,表示該block的附加描述資訊,主要是size大小,以及copy和dispose函式的指標。 6.variables,capture過來的變數,block能夠訪問它外部的區域性變數,就是因為將這些變數(或變數地址)複製到了結構體重。
在Objective-C語言中,一共有3種類型的block: 1._NSConcreteGlobalBlock,全域性的靜態block,不會訪問任何外部變數。 當block沒有傳入外部引數的時候就會標記為 NSGlobalBlock; 2._NSConcreteStackBlock,儲存在棧中的block,當函式返回時會被銷燬。 在MRC環境下,預設位StackBlock,在ARC環境中被 NSMallocBlock替代。 3._NSConcreteMallocBlock,儲存在堆中的block,當引用計數為0是會被銷燬。 所以結論是,在ARC環境下,Block都好使,因為使用的是NSMallocBlock,在MRC環境先愛,最好在任何時候都使用 block = [[block copy] autorelease]。 So, what's the point of all this? The point is  always use ARC . With ARC, blocks pretty much always work correctly. If you're not using ARC, you better defensively  block = [[block copy] autorelease]  any time a block outlives the stack frame where it is declared. That will force it to be copied to the heap as an  NSMallocBlock