再次整理,學習;
一、熟悉Objective-C
第1條:熟悉Objective-C 語言的起源
1)Objective-C有Smaltalk演化而來,后者是消息型語言的鼻祖;
1、消息機制和函數調用的關鍵區別:使用消息結構的語言,其運行時所執行的代碼由運行環境倆決定;而函數調用的語言,由編譯器決定;
2、Objective-C是C的超集
3、Objective-C使用動態綁定的消息結構,在運行時才會檢查對象類型;接收一條消息后,究竟應執行何種代碼,由運行環境而非編譯器決定;
第2條:在類的頭文件中盡量少引入其他頭文件
1、在編譯一個要使用的類的文件時,不需要知道這個類的全部細節,只需要知道有這個類就行了;
前置聲明:@class類名(不過需要調用這個類的方法時,必須用#import引入頭文件)
***要點:
1)除非確有必要,否則不要進入頭文件。一般來說,應在某個類的頭文件中使用前置聲明來提及別的類,并在實現文件中引入哪些類的頭文件。這樣做可以盡量降低類之間的耦合;
2)有時無法使用前置聲明,比如要聲明某個類遵循一項協議。這種情況下,盡量把“該類遵循某協議”的這條聲明移至“class-continuation分類”中。如果不行的話,就把協議單獨 放在一個頭文件中,然后將其引入;
第3條:多用字面量語法,少用與之等價的方法
1)NSString、NSNumber、NSArray、NSDictionary
NSString *str = @“my name is ruby”;
NSNumber *intNumber = @1;
NSArray *array = @[@1,@2,@3];
NSDictionary *dict = @{@“name”:@“ruby”,@“sex”:@“man”,@“age”:@24};
***要點:
1、應該使用字面量語法來創建字符串、數值、數組、字典。更簡潔;
2、應該通過取下標操作來訪問數組下標或字典中的鍵所對應的元素;
3、用字面量語法創建數組或字典是,若值中有nil,則會拋出異常。因此,務必確保值不為nil;
第4條:多用類型常量,少用#define預處理指令
1)static const NSTimeInterval kAnimationDuration = 0.5;
常用命名方法:若常量局限于某“編譯單元”內,則在前面加k;若常量在類之外可見,則通常以類名為前綴;
2)聲明其他類可用的全局常量
// In the header file
extern NSString *const LoginViewStr;
// In the implementation file
NSString *const LoginViewStr = @“1234567”;
***要點:
1、不要用預處理指令定義常量。這樣定義出來的常量不含類型信息,編譯器只是會在編譯前據此查找與替換操作。即使有人重新定義了常量值,編譯器也不會產生警告信息,這將導致應用程序中的常量不一致;
2、在實現文件中使用static const 來定義“只在編譯單元內可見的常量”。由于此類常量不在全局符號表中,所以無須為其名稱加前綴;
3、在頭文件中使用extern來聲明全局變量,并在相應實現文件中定義其值。這種常量要出現在全局符號表中,所以其名稱應加以區隔,通常用與之相應的類名做前綴;
第5條:用枚舉表示狀態、選項、狀態碼
1)用枚舉來表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值;
2)如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那么久將各選項值定義為2的冪,以便通過按位或操作將其組合起來;
3)用NS_ENUM與NS_OPTIONS宏來定義枚舉類型,并指明其底層數據類型。這樣做可以確保枚舉是用開發者所選的底層數據類型實現出來的,而不會采用編譯器所選的類型;
4)在處理枚舉類型的switch語句中不要實現default分支。這樣的話,加入新枚舉之后,編譯器就會提示開發者:switch語句并未處理所有枚舉;
第2章 對象、消息、運行期
當應用程序運行起來以后,為其提供相關支持的代碼叫做“Object-C運行期環境”(Objective-C runtime),它提供了一些使得對象之間能夠傳遞消息的重要函數,并且包含創建類實例所有的全部邏輯。
第6條:理解“屬性”這一概念
1)可以用@property語法來定義對象中所封裝的數據;
2)通過“特質”來指定存儲數據所需的正確語義;
3)在設置屬性所對應的實例變量時,一定要遵從該屬性所聲明的語義;
4)開發iOS程序時應該使用nonatomic屬性,因為atomic屬性會嚴重影響性能;
第7條:在對象內部盡量直接訪問實例變量
1)在對象內部讀取數據時,應該直接通過實例變量來讀,而寫入數據時,則應通過屬性來寫;
2)在初始化方法及dealloc方法中,總是應該直接通過實例變量來讀寫數據;
3)有時會使用惰性初始化技術配置某份數據,這種情況下,必須通過“獲取方法”來訪問屬性(_myname 通過下劃線取屬性值);
第8條:理解“對象等同性”這一概念
1)若想檢測對象的等同性,請提供“isEqual:”與hash方法;
2)相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象卻未必相同;
3)不要盲目地逐個檢測每條屬性,而是應該依照具體需求來指定檢測方案;
4)編寫hash方法時,應該使用計算速度快而且哈希碼碰撞幾率低的算法;
第9條:以“類族模式”隱藏實現細節
1)類族模式可以把實現細節隱藏在一套簡單的公共接口后面;
2)系統框架中經常使用類族
3)從類族的公共抽象基類中繼承子類時要當心,若有開發文檔,則應首先閱讀;
判斷某對象是否位于類族中:
if([classisKindOfClass:[SuperClassclass]]){
}
第10條:在即有類中使用關聯對象存放自定義數據
1)可以通過“關聯對象”機制來把兩個對象連起來;
2)定義關聯對象時可指定內存管理語義,用以模仿定義屬性時所采用的“擁有關系”與“非擁有關系”;
3)只有在其他做法不可行時才應選用關聯對象,因為這種做法通常會引入難于查找的bug;
第11條:理解objc_msgSend的作用
1)消息由接受者、選擇子以及參數構成。給某個對象“發送消息”也就相當于在該對象上“調用方法”;
2)發給某對象的全部消息都要由“動態消息派發系統(dynamic message dispatch system)來處理,該系統會查出對應的方法,并執行其代碼”
OC代碼:
id returnValue = [someObject messageName:parameter];
轉換成C代碼:
方法void objc_msgSend(id self, SEL cmd,…..)
id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);
該方法只描述了部分消息的調用過程,其他“邊界情況”則需要交由Object-C運行環境中的另一些函數處理:
objc_msgSend_setet:如果待發送的消息要返回結構體,
objc_msgSend_fpert:如果消息返回的是浮點數,
objc_msgSendSuper:如果要給超類發消息,
第12條:理解消息轉發機制
1)若對象無法響應某個選擇子,則進入消息轉發流程;
2)通過運行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中;
3)對象可以把其無法解讀的某些選擇子轉交給其它對象處理;
4)經過上述兩步之后,如果還是沒辦法處理選擇子,那就啟動完整的消息轉發機制;
將屬性聲明為@dynamic,這樣的話,編譯器就不會為其自動生成實例變量及存取方法了;
@dynamic string,number,date;
第13條:用“方法調配技術”調式“黑盒方法”
1)在運行期,可以向類中新增或替換選擇子所對應的方法實現;
2)使用另一份實現來替換原有的方法實現,這道工序叫“方法調配”,開發者常用此技術向原有實現中添加新功能;
3)一般來說,只有調式程序的時候才需要在運行期修改方法實現,這種做法不宜濫用;
類的方法列表會把選擇子的名稱映射到相關的方法實現之上,使得“動態消息派發系統”,能夠據此知找到應該調用的方法。這些方法均以函數指針的形式來表示,這種指針叫做IMP,
id (*IMP)(id,SEL,….)
Objective-C運行期系統提供的幾個方法能夠用來操作選擇子映射表;
第14條:理解“類對象”的用意
1)每個實例都有一個指向Class對象的指針,用以表明其類型,而這些Class對象則構成了類的繼承體系;
2)如果對象類型無法再編譯器確定,那么就應該使用類型信息查詢方法來探知;
3)盡量使用類型信息查詢方法來確定對象類型,而不要直接比較類對象,因為某些對象可能實現了消息轉發功能;
第3章接口與API設計
第15條:用前綴避免命名空間沖突
1)選擇與你的公司,應用程序或二者皆有關聯之名稱作為類名的前綴,并在所有代碼中均使用這一前綴;
2)若自己所開發的程序庫中用到了第三方庫,則應為其中的名稱加上前綴;
******蘋果保留了兩個字母作為前綴的權利(最好用三個以上的字母作為前綴);
第16條:提供“全能初始化方法”
1)在類中提供一個全能初始化方法,并于文檔里指明。其它初始化方法均應調用此方法;
2)若全能初始化方法與超類不同,則需覆寫超類中的對應方法;
3)如果超類的初始化方法不適用于子類,那么應該覆寫這個超類的方法,并在其中拋出異常;
第17條:實現description方法
1)實現description方法返回一個有意義的字符串,用以描述該實例;
2)若想在調式時打印出更詳細的對象描述信息,則應實現debugDescription方法;用po 打印內容,是調用description方法;
第18條:盡量使用不可變對象
1)盡量創建不可變的對象;
2)若某屬性僅可于對象內部修改,則在“class-continuation分類”中將其由readonly屬性擴展為readwrite屬性;
3)不要把可變的collection作為屬性公開,則應提供相關方法,以此修改對象中的可變collection;
第19條:使用清晰而協調的命名方式
1)起名時應遵從標準的Objective-C命名規范,這樣創建出來的接口更容易為開發者所理解;
2)方法名要言簡意賅,從左至右讀起來要像個日常用語中的句子才好;
3)方法名里不要使用縮略后的類型名稱;
4)給方法起名時的第一要務就是確保其風格與你自己的代碼或所要集成的框架相符;
第20條:為私有方法名加前綴
1)給私有方法的名稱加上前綴,這樣可以很容易地將其同公共方法分開;
2)不要單用一個下劃線做私有方法的前綴,因為這樣做法是預留給蘋果公司用的;
第21條:理解Objective-C錯誤模型
1)只有發生了可使整個應用程序崩潰的嚴重錯誤時,才應使用異常;
2)在錯誤不那么嚴重的情況下,可以指派“委托方法”來處理錯誤,也可以把錯誤信息方法NSError對象里,經由“輸出參數”返回給調用者;
第22條:理解NSCopying協議
1)若想令自己所寫的對象具有拷貝功能,則需要事項NSCopying協議;
2)如果自定義的對象分為可變版本與不可變版本,那么就要同時實現NSCopying與NSMutableCopying協議;
3)復制對象是需決定采用淺拷貝還是深拷貝,一般情況下應該盡量執行淺拷貝;
4)如果你所寫的對象需要深拷貝,那么可考慮新增一個專門執行深拷貝的方法;
第4章協議與分類
第23條:通過委托與數據源協議進行對象間通信
1)委托模式為對象提供了一套接口,使其可由此將相關事件告知其他對象;
2)將委托對象應該支持的接口定義成協議,在協議中把可能需要處理的事件定義成方法;
3)當某對象需要從另外一個對象中獲取數據時,可以使用委托模式。這種情境下,該模式稱“數據源協議(data source protocal)”;
4)若有必要,可實現含有位段的結構體,將委托對象是否能響應相關協議方法這一信息緩存至其中;
第24條:將類的實現代碼分散到便于管理的數個分類之中
1)使用分類機制把類的實現代碼劃分成易于管理的小塊;
2)將應該視為“私有”的方法歸入名叫Private的分類中,以隱藏實現細節;
第25條:總是為第三方類的分類名稱加前綴
1)向第三方類中添加分類時,總應給其名稱加上你專用的前綴;
2)向第三方類中添加分類時,總應給其中的方法名加上你專用的前綴;
第26條:勿在分類中聲明屬性
1)把封裝數據所用的全部屬性都定義在主接口里;
2)在“class-continuation分類”之外的其他分類中,可以定義存取方法,但盡量不要定義屬性;
第27條:使用“class-continuation分類”隱藏實現細節
1)通過“class-continuation分類”向類中新增實例變量;
2)如果某屬性在主接口中聲明為“只讀”,而類的內部又要用設置方法修改此屬性,那么就在“class-continuation分類”中將其擴展為“可讀寫”;
3)把私有方法的原型聲明在“class-continuation分類”里面;
4)若想使類所遵循的協議不為人所知,則可于“class-continuation分類”中聲明;
第28條:通過協議提供匿名對象
1)協議可在某種程度上提供匿名類型。具體的對象類型可以淡化成遵從某協議的id類型,協議里規定了對象所應實現的方法;
2)使用匿名對象來隱藏類型名稱(或類名);
3)如果具體類型不重要,重要的是對象能夠響應(定義在協議里的)特定方法,那么可使用匿名對象來表示;
第5章內存管理
第29條:理解引用計數
1)引用計數機制通過可以遞增遞減的計數器來管理內存。對象創建好之后,其保留計數至少為1.若保留計數為正,則對對象繼續存留。當保留計數降為0時,對象就被銷毀了;
2)在對象生命期中,其余對象通過引用來保留或釋放此對象。保留與釋放操作分別會遞增及遞減保留計數;
第30條:以ARC簡化引用計數
1)ARC只負責管理Objective-C對象的內存。尤其要注意:CoreFoundation對象不歸ARC管理,開發者必須適時調用CFRetain/CFRelease;
-(void)dealloc{
CFRelease(_coreFoundationObject);
}
第31條:在dealloc方法中釋放引用并解除監聽
1)在dealloc方法里,應該做的事情就是釋放指向其它對象的引用,并取消原來訂閱的“鍵值觀測”(KVO)或NSNotificationCenter等通知,不要做其它事情;
2)如果對象持有文件描述等系統資源,那么應該專門編寫一個方法來釋放此種資源。這樣的類要和其使用中約定:用完資源后必須調用close方法;
3)執行異步任務的方法不應在dealloc里調用;只能在正常狀態下執行的那些方法在不應在dealloc里調用,因為此時對象已處在回收的狀態了;
第32條:編寫“異常安全代碼”時留意內存管理問題
1)捕獲異常時,一定要注意將try快內所創立的對象清理干凈;
2)在默認情況下,ARC不生成安全處理異常所需的清理代碼。開啟編譯器標志后,可生成這種代碼,不過會導致應用程序變大,而且會降低運行效率;
ARC環境下基本不會使用異常安全代碼,極大影響效率,OC代碼中,只有當應用程序必須因異常狀態而終止才應拋出異常(拋出異常沒有意義);
開啟-fobjc-arc-exceptions,ARC也能生成處理異常所用的附加代碼;
第33條:以弱引用避免保留環
1)將某些引用設為weak,可避免出現“保留環”;
2)weak引用可以自動清空,也可以不自動清空。自動清空(autonilling)是隨著ARC而引入的新特性,由運行期系統來實現。在具備自動清空功能的弱引用上,可以隨意讀取其數據,因為這種引用不會指向已經回收過的對象;
第34條:以“自動釋放池”降低內存峰值
1)自動釋放池排布在棧中,對象收到autorelease消息后,系統將其放入最頂端的池里;
2)合理運用自動釋放池,可降低應用程序的內存峰值;
3)@autoreleasepool這種新式寫法能創建出更為輕便的自動釋放池;
第35條:用“僵尸對象”調用內存管理問題(***有點蒙)
1)系統在回收對象時,可以不將其真的收回,而是把它轉化為僵尸對象。通過環境變量NSZombieEnabled可開啟此功能;
2)系統會修改對象的isa指針,令其指向特殊的僵尸類,從而使改對象變為僵尸對象。僵尸類能夠響應所有的選擇子,響應方式為:打印一條包含消息內容及其接受者的消息,然后終止應用程序;
第36條:不要使用retainCount
1)對象的保留計數看似有用,實則不然,因為任何給定時間點上的“絕對保留計數”(absolute retain count)都無法反映對象生命的全貌;
2)引入ARC之后,retainCount方法就正式廢止了,在ARC下調用該方法會導致編譯器報錯;
第6章:塊與大中樞派發
第37條:理解“塊”這一概念
1)塊是C、C++、Objective-C中的語法閉包;
2)塊可接受參數、也可返回值;
3)塊可以分配在棧或堆上,也可以是全局的。分配在棧上的塊可拷貝到堆里,這樣的話,就和標準的Objective-C對象一樣,具備引用計數了;
self也是個對象,因而塊在捕獲它時也會將其保留。如果self所指代的按個對象同時也保留了塊,那么這種情況通常會導致“保留環”;
理解全局塊、棧塊、堆塊;
全局塊:
void (^block)() = ^{
NSLog(@“this is a block”);
}
第38條:為常用的塊類型創建typedef
1)以typedef重新定義塊類型,可令塊變量用起來更加簡單;
2)定義新類型時應遵從現有的命名習慣,勿使用名稱與別的類型相沖突;
3)不妨為同一個塊簽名定義多個類型別名。如果要重構的代碼使用了塊類型的某個別名,那么直需要修改相應typedef中的塊簽名即可,無須改動其它typedef;
用typedef重命名:
typedef int (^EOCSomeBlock)(BOOL flag,int value);
回調:
-(void)startWithCompletionHandler:(void(^)(NSData *data,NSError *error))completion;
第39條:用handler塊降低代碼分散程度
1)在創建對象時,可以使用內聯的handler塊將相關業務邏輯一并聲明;
2)在有多個實例需要監控時,如果采用委托模式,那么經常需要根據傳入的對象來切換,而若用handler塊來實現,則可直接將快與相關對象放在一起;
3)設計API時如果用到了handler塊,那么可以增加一個參數,使調用者可通過此參數來決定應該把塊安排在哪個隊列上執行;
第40條:用塊引用其所屬對象時不要出現保留環
1)如果塊所捕獲的對象直接或間接地保留了塊本身,那么就得當心保留環問題;
2)一定要找個適當的時機解除保留環,而不能把責任推給API的調用者;
如果在block代碼塊里面改變實例變量,必須獲取self,這樣就會導致“保留環”;用__weak修飾;
第41條:多用派發隊列,少用同步鎖
1)派發隊列可用來表述同步語義(synchronizetion semantic),這種做法要比使用@synchronized塊或NSLock對象更簡單;
2)將同步與異步派發結合起來,可以實現與普通加鎖機制一樣的同步行為,而這么做卻不會阻塞執行異步派發的線程;
3)使用同步隊列及柵欄塊,可以令同步行為更加高效;
理論上:運用線程鎖可以保證線程安全,但是無法保證線程的絕對安全(單個線程中多次訪問某個屬性值時,其它線程可能會寫入新值);
第42條:多用GCD,要用performSelector系列方法
1)performSelector系列方法在內存管理方面容易有疏失。它無法確定要執行的選擇子具體是什么,因而ARC編譯器也就無法插入適當的內存管理方法;
2)performSelector系列方法所能處理的選擇子太過局限了,選擇子的返回值類型及發送給方法的參數個數都收到限制;
3)如果想把任務放在另一個線程上執行,那么最好不要用performSelector系列方法,而是應該把任務封裝到塊里,然后調用大中樞派發機制的相關方法來實現;
第43條:掌握GCD及操作隊列的使用時機
1)在解決多線程與任務管理問題時,派發隊列并非唯一方案;
2)操作隊列提供了一套高層的Objective-C API,能實現純GCD所具備的絕大部分功能,而且還能完成一些更為復雜的操作,哪些操作若改用GCD來實現,則需另外編寫代碼;
第44條:通過Dispatch Group機制,根據系統資源狀況來執行任務
1)一系列任務可歸入一個dispatch group之中。開發者可以在這組任務執行完畢時獲得通知;
2)通過dispatch group,可以在并發式隊列里同時執行多項任務。此時GCD會根據系統資源狀況來調度這些并發執行的任務。開發者若自己來實現此功能,則需編寫大量代碼
第45條:使用dispatch_once 來執行只需運行一次的線程安全代碼
1)dispatch_get_current_queue 函數的行為常常與開發者所預期的不同。此函數已經廢棄,只應做調試只用;
2)由于派發隊列是按層級來組織的,所以無法單用某個隊列對象來描述“當前隊列”這一概念;
3)dispatch_get_current_queue函數用于解決由不可重入的代碼所引發的死鎖,然而能用此函數解決的問題,通常也能改用“隊列特定數據”來解決;
第46條:不要使用dispatch_get_current_queue
1)dispatch_get_current_queue函數的行為常常與開發者所預期的不同。此函數已經廢棄,只應做調試之用;
2)由于派發隊列是按層級來組織的,所以無法單用某個隊列對象來描述“當前隊列”這一概念;
3)dispatch_get_current_queue函數由于解決由不可重入的代碼所引發的死鎖,然而能用此函數解決的問題,通常也能改用“隊列特定數據”來解決;
- (void)viewDidLoad {
[superviewDidLoad];
//1、創建NSInvocationOperation對象
NSInvocationOperation*operation = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(operationDaYin)object:nil];
//[operation start];
//2、創建NSBlockOperation對象
NSBlockOperation*operation1 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"operation1打印內容:111111111111");
}];
//該方式創建可以添加額外任務,任務執行沒有先后順序;
[operation1addExecutionBlock:^{
NSLog(@"operation1添加的額外任務:1111111*****");
//4、子線程給主線程傳值的兩種方法:該方法會在主線程執行完之后再調用;優先選擇第二種方法
//[self performSelectorOnMainThread:@selector(log:) withObject:@"1111111111" waitUntilDone:NO];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"從子線程回到主線程打印的內容:*******************");
});
}];
//[operation1 start];
NSBlockOperation*operation2 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"operation2打印內容:222222222");
}];
//[operation2 start];
NSBlockOperation*operation3 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"operation3打印內容:3333333");
}];
//[operation3 start];
//3、創建任務隊列
NSOperationQueue*q = [[NSOperationQueuealloc]init];
//給添加入隊列中的線程排序:即隊列中添加依賴關系
[operation1addDependency:operation];
[operation2addDependency:operation1];
[operation3addDependency:operation2];
[qaddOperation:operation];
[qaddOperation:operation1];
[qaddOperation:operation2];
[qaddOperation:operation3];
[qsetMaxConcurrentOperationCount:1];//設置隊列的最大并行數;為1為串行隊列;
NSLog(@"主線程中打印的內容");
//5、延后一段時間執行某個任務,兩種方法,優選第二種方法:
//[self performSelector:@selector(yanHouTime) withObject:nil afterDelay:1];
dispatch_time_ttime =dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 *NSEC_PER_SEC));
dispatch_after(time,dispatch_get_main_queue(), ^{
NSLog(@"用GCD延后時間執行某個任務");
});
}
-(void)operationDaYin
{
NSLog(@"operation的打印內容:00000000000");
}
-(void)log:(NSString*)str
{
NSLog(@"子線程傳給主線程的值:%@",str);
}
-(void)yanHouTime
{
NSLog(@"用perform方法延后時間再執行某個任務");
}
第7章系統框架
第47條:熟悉系統框架
1)許多系統框架都可以直接使用。其中最重要的是Foundation與CoreFoundation,這兩個框架提供了構建應用程序所需要的許多核心功能;
2)很多常見任務都能用框架來做,例如音頻與視頻處理、網絡通信、數據管理等;
3)請記住:用純C寫成的框架與用Objective-C寫成的一樣重要,若想成為優秀的Objective-C開發者,應該掌握C語言的核心概念;
第48條:多用塊枚舉,少用for循環
1)遍歷collection有四種方式。最基本的辦法是for循環,其次是NSEnumerator遍歷法及快速遍歷法,最新、最先進的方式是“塊枚舉法”;
2)“塊枚舉法”本身就能通過GCD來并發執行遍歷操作,無須另行編寫代碼。而采用其他遍歷方式則無法輕易實現這一點;
3)若提前知道待遍歷的collection含有何種對象,則應修改塊簽名,指出對象的具體類型;
第49條:對自定義其內存管理語義的collection使用無縫橋接
1)通過無縫橋接技術,可以再Foundation框架中的Objective-C對象與CoreFoundation框架中的C語言數據結構之間來回轉換;
2)在CoreFoundation層面創建collection時,可以指定許多回調函數,這些函數表示此collection應如何處理其元素。然后,可運用無縫橋接技術,將其轉換成具備特殊內存管理語義的Objective-C collection;
第50條:構建緩存時選用NSCache而非NSDictionary
1)實現緩存時應選用NSCache而非NSDictionary對象。因為NSCache可以提供優雅的自動刪除功能,而且是“線程安全的”,此外,它與字典不同,并不會拷貝鍵;
2)可以給NSCache對象設置上限,用以限制緩存中的對象總個數及“總成本”,而這些尺度則定義了緩存刪減其中對象的時機。但是絕對不要把這些尺度當成可靠的“硬限制”(hard limit),它們僅對NSCache起指導作用;
3)將NSPurgeableData與NSCache搭配使用,可實現自動清除數據的功能,也就是說,當NSPurgeableData對象所占內存為系統丟棄時,該對象自身也會從緩存中移除;
4)如果緩存使用得當,那么應用程序的響應速度就能提高。只有那種“重新計算起來很費事的”數據,才值得放入緩存,比如那些需要從網絡獲取或從磁盤讀取的數據;
第51條:精簡initialize與load的實現代碼(很少用這兩個類方法)
1)在加載階段,如果類實現了load方法,那么系統就會調用它。分類里也可以定義此方法,類的load方法要比分類中的先調用。與其他方法不同,load方法不參與覆寫機制。
2)首次使用某個類之前,系統會向其發送initialize消息。由于此方法遵從普通的覆寫規則,所以通常應該在里面判斷當前要初始化的是哪個類;
3)load與initialize方法都應該實現得精簡一些,這有助于保持應用程序的響應能力,也能減少引入“依賴環(interdependency cycle)”的幾率;
4)無法在編譯期設定的全局變量,可以放在initialize方法里初始化;
第52條:別忘了NSTimer會保留其目標對象
1)NSTimer對象會保留其目標,直到計時器本身失效為止,調用invalidate方法可令計時器失效,另外,一次性的計時器在觸發完任務之后也會失效;
2)反復執行任務的計時器(repeating timer),很容易引入保留環,如果這種計時器的目標對象又保留了計時器本身,那肯定會導致保留環。這種環狀保留關系,可能是直接發生的,也可能是通過對象里的其他對象間接發生的;
3)可以擴充NSTimer的功能,用“塊”來打破保留環。不過,除非NSTimer將來在公共接口里提供此功能,否則必須創建分類,將相關實現代碼加入其中;
//防止反復執行的計時器引入“保留環”方法
__weakViewController*weakSelf =self;
NSTimer*timer = [selflzw_scheduledTimerWithTimeInterval:1block:^{
NSLog(@"運用block塊進行計時器循環************");
[weakSelfdoSomeThing];
}repeats:YES];
-(void)doSomeThing
{
NSLog(@"111111111111111111");
}
-(NSTimer*)lzw_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats
{
return[NSTimerscheduledTimerWithTimeInterval:intervaltarget:selfselector:@selector(lzw_blockInvoke:)userInfo:[blockcopy]repeats:YES];
}
-(void)lzw_blockInvoke:(NSTimer*)timer
{
void(^block)() = timer.userInfo;
if(block) {
block();
}
}