上面就是這本書的目錄,能夠 點擊這裏下載PDF版,原版英文版PDF我也有存~最近在重溫這本OC經典之做《Effective Objective-C 2.0編寫高質量iOS與OS X代碼的52個有效方法》,這篇文章算是重溫以後的產物吧,讀完這篇文章你將快速讀完這本書,因爲我的能力有限,不免有一些遺漏或者錯誤,請各位看官不吝賜教!謝謝!同時若是有任何問題也能夠在下方留言,歡迎一塊兒交流進步!另外因爲篇幅緣由,書中一些基礎知識的介紹文中就省略掉了。php
Objective-C
從Smalltalk語言是從Smalltalk
語言演化而來, Smalltalk
是消息語言的鼻祖。Objective-C
是C語言
的超集,在C語言
基礎上添加了面向對象等特性,可能一開始接觸時你會以爲語法有點奇怪,那是由於Objective-C
使用了動態綁定的消息結構
,而Java
,C++
等等語言使用的是函數調用。消息結構
與函數調用
的關鍵區別在於:函數調用的語言,在編譯階段由編譯器
生成一些虛方法表
,在運行時從這個表找到所要執行的方法去執行。而使用了動態綁定的消息結構
在運行時接到一條消息,接下來要執行什麼代碼是運行期決定的,而不是編譯器。@class xx.h
,這樣作的好處會減小必定的編譯時間。若是是用的#import
所有導入的話,會出現a.h
import了b.h
,當c.h
又import a.h
時,把b.h
也都導入了,若是隻是用到類名,真的比較浪費,也不夠優雅@class
向前聲明,好比某個類要遵循一項協議,這個協議在另一個類中聲明的,能夠將協議這部分單獨放在一個頭文件,或者放在分類當中,以下降引用成本。1.多使用字面量語法來建立字符串,數組,字典等。 傳統建立數組方法:git
NSArray *languages = [NSArray arrayWithObjects:@"PHP", @"Objective-C", someObject, @"Swift", @"Python", nil];
NSString *Swift = [languages objectAtIndex:2];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"key", @"value", nil];
NSString *value = [languages objectForKey:@"key"];
複製代碼
字面量:github
NSArray *languages = @[@"PHP", @"Objective-C", someObject, @"Swift", @"Python"];
NSString *Swift = languages[2];
NSDictionary *dict = @{@"key" : @"value"};
NSString *value = languages[@"key"];
複製代碼
這樣作的好處:使代碼更簡潔,易讀,也會避免nil問題。好比languages數據中 someObject 若是爲nil時,字面量語法就會拋出異常,而使用傳統方法建立的languages數組值確是@[@"PHP", @"Objective-C"];
由於字面量語法實際上是一種語法糖,效果是先建立了一個數組,而後再把括號中的對象都加到數組中來。 不過字面量語法有一個小缺點就是建立的數組,字符串等等對象都是不可變的,若是想要可變的對象須要本身多執行一步mutableCopy
,例如數據庫
NSMutableArray *languages = [@[@"PHP", @"Objective-C", @"Swift", @"Python"] mutableCopy];
複製代碼
這一條講的是屬性的基本概念,以及屬性的各類修飾符,這些就很少囉嗦了,這裏強調一下:緩存
readonly
。atomic
並不能保證多線程安全,例如一個線程連續屢次讀取某個屬性的值,而同時還有別的線程在修改這個屬性值得時候,也仍是同樣會讀到不一樣的值。atomic 的原理只是在 setter and getter 方法中加了一個@synchronized(self)
,因此iOS開發中屬性都要聲明爲nonatomic
,由於atomic嚴重影響了性能,可是在Mac OSX上開發卻一般不存在這個性能問題@property (nonatomic, strong) NSArray *arrayOfStrong;
@property (nonatomic, copy) NSArray *arrayOfCopy;
@property (nonatomic, strong) NSMutableArray *mutableArrayOfStrong;
@property (nonatomic, copy) NSMutableArray *mutableArrayOfCopy;
複製代碼
具體運行示例點擊查看 答案是正常應該這樣聲明安全
@property (nonatomic, copy) NSArray *arrayOfCopy;
@property (nonatomic, strong) NSMutableArray *mutableArrayOfStrong;
複製代碼
思考下面輸出什麼?bash
NSString *aString = @"iphone 8";
NSString *bString = [NSString stringWithFormat:@"iphone %i", 8];
NSLog(@"%d", [aString isEqual:bString]);
NSLog(@"%d", [aString isEqualToString:bString]);
NSLog(@"%d", aString == bString);
複製代碼
答案是110 ==
操做符只是比較了兩個指針,而不是指針所指的對象服務器
爲何下面這段if 永遠爲false
id maybeAnArray = @[];
if ([maybeAnArray class] == [NSArray class]) {
//Code will never be executed
}
複製代碼
由於[maybeAnArray class] 的返回永遠不會是NSArray,NSArray是一個類族,返回的值一直都是NSArray的實體子類。大部分collection類都是某個類族中的’抽象基類’ 因此上面的if想要有機會執行的話要改爲
id maybeAnArray = @[];
if ([maybeAnArray isKindOfClass [NSArray class]) {
//Code probably be executed
}
複製代碼
這樣判斷的意思是,maybeAnArray這個對象是不是NSArray類族中的一員 ** 使用類族的好處:能夠把實現細節隱藏再一套簡單的公共接口後面 **
這條講的是objc_setAssociatedObject
和objc_getAssociatedObject
,如何使用在這裏就很少說了。值得強調的一點是,用關聯對象可能會引入難於查找的bug,畢竟是在runtime階段,因此可能要看狀況謹慎選擇
以前在瞭解Objective-C語言的起源
有提到過,Objective-C是用的消息結構
。這條就是讓你理解一下怎麼傳遞的消息。
objc_msgSend
,該函數定義以下:void objc_msgSend(id self, SEL cmd, ...)
複製代碼
這是一個參數個數可變的函數,第一參數表明接收者
,第二個參數表明選擇子
(OC函數名),後續的參數就是消息(OC函數調用)中的那些參數 2. 舉例來講:
id return = [git commit:parameter];
複製代碼
上面的Objective-C方法在運行時會轉換成以下函數:
id return = objc_msgSend(git, @selector(commit), parameter);
複製代碼
objc_msgSend函數會在接收者所屬的類中搜尋其方法列表
,若是能找到這個跟選擇子名稱相同的方法,就跳轉到其實現代碼,往下執行。如果當前類沒找到,那就沿着繼承體系繼續向上查找,等找到合適方法以後再跳轉 ,若是最終仍是找不到,那就進入消息轉發
的流程去進行處理了。 3. 說過了OC的函數調用實現,你會以爲消息轉發要處理不少,尤爲是在搜索上,幸運的是objc_msgSend在搜索這塊是有作緩存的,每一個OC的類都有一塊這樣的緩存,objc_msgSend會將匹配結果緩存在快速映射表
(fast map)中,這樣以來這個類一些頻繁調用的方法會出如今fast map 中,不用再去一遍一遍的在方法列表
中搜索了。 4. 還有一個有趣的點,就是在底層處理髮送消息的時候,有用到尾調用優化
,大概原理就是在函數末尾調用某個不含返回值函數時,編譯器會自動的不在棧空間上從新進行分配內存,而是直接釋放全部調用函數內部的局部變量,而後直接進入被調用函數的地址。
關於這條這看看這篇文章:iOS理解Objective-C中消息轉發機制附Demo
這條講的主要內容就是 Method Swizzling,經過運行時的一些操做能夠用另一份實現來替換掉原有的方法實現,每每被應用在向原有實現中添加新功能,好比擴展UIViewController,在viewDidLoad裏面增長打印信息等。具體例子能夠點擊我查看
Objective-C類是由Class類型來表示的,它其實是一個指向objc_class結構體的指針。它的定義以下:
typedef struct objc_class *Class;
複製代碼
在<objc/runtime.h>中能看到他的實現:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; ///< 指向metaClass(元類)
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; ///< 父類
const char *name OBJC2_UNAVAILABLE; ///< 類名
long version OBJC2_UNAVAILABLE; ///< 類的版本信息,默認爲0
long info OBJC2_UNAVAILABLE; ///< 類信息,供運行期使用的一些位標識
long instance_size OBJC2_UNAVAILABLE; ///< 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; ///< 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; ///< 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; ///< 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; ///< 協議鏈表
#endif
} OBJC2_UNAVAILABLE;
複製代碼
此結構體存放的是類的「元數據」(metadata),例如類的實例實現了幾個方法,具有多少實例變量等信息。 這裏的isa指針指向的是另一個類叫作元類(metaClass)。那什麼是元類呢?元類是類對象的類。也能夠換一種容易理解的說法:
咱們來看一個很經典的圖來加深理解:
能夠總結爲下:Class
都有一個isa指針
指向一個惟一的Meta Class
Meta Class
的isa指針
都指向最上層的Meta Class
,這個Meta Class
是NSObject的Meta Class
。(包括NSObject的Meta Class
的isa指針
也是指向的NSObject的Meta Class
,也就是本身,這裏造成了個閉環)Meta Class
的super class
指針指向它本來Class
的 Super Class的Meta Class
(這裏最上層的NSObject的Meta Class
的super class
指針仍是指向本身)NSObject Class的super class
指向 nilObjective-C沒有相似其餘語言那樣的命名空間機制(namespace),好比說PHP中的
<?php
namespace Root\Sub\subnamespace;
複製代碼
這就會致使當你不當心實現了兩個相同名字的類,或者把兩個相對獨立的庫導入項目時而他們又剛好有重名的類的時候該類所對應的符號和Meta Class符號定義了兩次。因此很容易產生這種命名衝突,讓程序的連接過程當中出現出現重複的符號形成報錯。 爲了不這種狀況,咱們要儘可能在類名,以及分類和分類方法上增長前綴,還有一些宏定義等等根據本身項目來定吧
若是建立類的實例的方式不止一種,那麼這個類就會有多個初始化方法,這樣作很好,不過仍是要在其中選定一個方法做爲全能初始化方法,剩下的其他的初始化方法都要調用它,這樣作的好處是之後若是初始化的邏輯更改了只需更改一處便可,或者是交給子類覆寫的時候也只覆寫這一個方法便可~ 舉個例子來講:能夠看一下NSDate的實如今NSDate.h中NSDate類中定義了一個全能初始化方法:
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
複製代碼
其他的相似初始化方式定義在NSDate (NSDateCreation) 分類中
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
複製代碼
在NSDate文檔中有一條:If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, you must do these things:
而後其中要作的有一步就是
Override [initWithTimeIntervalSinceReferenceDate:
](apple-reference-documentation://hcslylvSCo), one of the designated initializer methods`
複製代碼
這個是咱們組織代碼過程當中應該學習的地方!
這條講的是能夠經過覆寫description方法或者debugDescription方法來在NSLog打印時或者LLDB打印時輸出更多的自定義信息。(數據和字典的能夠經過覆寫descriptionWithLocale:
方法) 友情提示:不要在description中使用 NSLog("%@",self);
,否則會掉進無底深淵啊 這裏我有一個有趣的想法,不過還沒徹底實現,就是想經過覆寫description能把任何一個對象的屬性值名稱,屬性值都一一完整的記錄下來,能夠點擊查看
這條主要講儘可能使用不可變的對象,也就是在對外屬性聲明的時候要儘可能加上readonly修飾,默認是readwrite,這樣一來,在外部就只能讀取該數據,而不能修改它,使得這個類的實例所持有的數據更加安全。若是外部想要修改,能夠提供方法來進行修改。 不要把可變的collection做爲屬性公開,而應提供相關方法,以此修改對象中的可變collection(這條我的感受通常在經常使用、重要的類纔有必要,畢竟也增長了很多代碼量) 好比例子:
//Language.h
@property (nonatomic, strong) NSSet *set;
複製代碼
應該改成
//Language.h
@property (nonatomic, strong, readonly) NSSet *languages;
- (void)addLanguage:(NSString *)language;
- (void)removeLanguage:(NSString *)language;
//**.m
@implementation Language {
NSMutableSet *mutableLanguages;
}
- (NSSet *)languages {
return [_mutableLanguages copy];
}
- (void)addLanguage:(NSString *)language {
[_mutableLanguages addObject:language];
}
- (void)removeLanguage:(NSString *)language {
[_mutableLanguages removeObject:language];
}
複製代碼
這條不用太強調了,具體也能夠參照一下我以前擬的Objective-C編程規範及建議,後續可能會不斷補充更新
這條講的是應該爲類內的私有方法增長前綴,以便區分,這個感受因人而異吧,感受只要你不隨便把私有方法暴露在.h文件都能接受,曾遇到過這樣的同事,感受其不太適合寫程序吧。
不少語言都有異常處理機制,Objective-C也不例外,Objective-C也有相似的@throw,不過在OC中使用@throw可能會致使內存泄漏,多是它被設計的使用場景的問題。建議@throw只用來處理嚴重錯誤,也能夠理解爲致命錯誤(fatal error),那麼處理通常錯誤的時候(nonfatal error)時可使用NSError。
在OC開發中,使用對象時常常須要拷貝它,咱們會經過copy/mutbleCopy
來完成。若是想讓本身的類支持拷貝,那必需要實現NSCopying
協議,只須要實現一個方法:
- (id)copyWithZone:(NSZone*)zone
複製代碼
固然若是要求返回對象是可變的類型就要用到NSMutableCopying
協議,相應方法
- (id)mutableCopyWithZone:(NSZone *)zone
複製代碼
在拷貝對象時,須要注意拷貝執行的是淺拷貝仍是深拷貝。深拷貝在拷貝對象時,會將對象的底層數據也進行了拷貝。淺拷貝是建立了一個新的對象指向要拷貝的內容。通常狀況應該儘可能執行淺拷貝。
這條講的也比較基礎,就是基本的delegate,protocal使用。 有一點稍微說一下:當某對象須要從另一個對象中獲取數據時,可使用委託模式,這種用法常常被稱爲「數據源協議」(Data source Protocal)相似 UITableview
的UITableViewDataSource
另外在Swift中有一個很重要的思想就是面向協議編程。固然OC中也能夠用協議來下降代碼耦合性,必要的時候也能夠替代繼承,由於遵循同一個協議的類能夠是任何,沒必要是同一個繼承體系下。
這條主要說的是經過分類機制,能夠把類分紅不少歌易於管理的小塊。也是有一些前提的吧,多是這個類業務比較複雜,須要瘦身,須要解耦等等。做者還推薦把私有方法統一放在Private分類中,以隱藏實現細節。這個我的以爲視狀況而定吧。
向第三方類的分類名稱加上你專用的前綴,這點沒必要多說,😜
不要在分類中聲明屬性,除了「class-continuation」分類中。那什麼是「class-continuation」分類呢,其實就是咱們常常在.m文件中用到的,例如:
//Swift.m
@interface Swift ()
//這個就是「class-continuation」分類
@end
@implementation Swift
@end
複製代碼
這條跟以前的也有點重複,最終目的仍是要儘可能在公共接口中向外暴露的內容最小化,隱藏實現細節,只告訴怎麼調用,怎麼使用便可。具體實現以及屬性的可修改權限盡量的隱藏掉。
id<someProtocal> object
。object對象的類型不限,只要能聽從這個協議便可,在這個協議裏面定義了這個對象所應該實現的方法。對照明設備所作的工做 | 對OC對象所作的動做 |
---|---|
開燈 | 生成對象 |
須要照明 | 持有 |
不須要照明 | 釋放 |
關燈 | 廢棄 |
內存管理的思考方式 | 對應OC方法 |
---|---|
本身生成的對象,本身所持有 | alloc/new/copy/mutableCopy等 |
非本身生成的對象(好比[NSArray array]),本身也能持有 | retain |
再也不須要本身持有的對象時釋放 | release |
當對象不被任何其餘對象持有時廢棄 | dealloc |
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([HSAppDelegate class]));
}
}
複製代碼
autoreleasepool能夠延長對象的生命期,使其在跨越方法調用邊界後依然能夠存活一段時間,一般是在下一次「時間循環」(event loop)時釋放,不過也可能會執行的早一點。 3. 保留環: 也稱retain cycle,就是循環引用。造成緣由就是對象之間相互用強引用指向對方,會使得所有都沒法得以釋放。解決方案一般是使用弱引用(weak reference)
使用ARC,能夠省略對於引用計數的操做,因此在ARC下調用對象的retain,release,autorelease,dealloc方法時系統會報錯。 這裏要注意CoreFoundation 對象不歸ARC管理,開發中若是有用到仍是要誰建立誰釋放,適時調用CFRetain/CFRelease。
不要在delloc方法中調用其餘方法,尤爲是須要異步執行某些任務又要回調的方法,這樣的很危險的行爲,極可能異步執行完回調的時候該對象已經被銷燬了,這樣就沒得玩了,crash了。 在delloc方法裏應該製做一些釋放相關的事情,包括不限於一些KVO取消訂閱,remove 通知等。
這條有點重複,以前已經說過了,OC中拋出異常的時候可能會引發內存泄漏,注意一下使用的時機,或者注意在@try捕獲異常中清理乾淨。
這條比較簡單,內容主旨就是標題:以弱引用避免保留環(Retain Cycle)
在遍歷處理一些大數組或者大字典的時候,可使用自動釋放池來下降內存峯值,例如:
NSArray *people = /*一個很大的數組*/
NSMutableArray *employeesArray = [NSMutableArray new];
for (NSStirng *name in people) {
@autoreleasepool {
MLEmployee *employee = [MLEmployee alloc] initWithName:name];
[employeesArray addObject:employee];
}
}
複製代碼
在蘋果引入ARC以後retainCount已經正式廢棄,任什麼時候候都不要調用這個retainCount方法來查看引用計數了,由於這個值實際上已經沒有準確性了。可是在MRC下仍是能夠正常使用
根據block在內存中的位置,block被分紅三種類型:
void (^block)() = ^{
NSLog(@"I am a NSGlobalBlock");
}
複製代碼
NSString *name = @"PHP";
void (^block)() = ^{
NSLog(@"世界上最好的編程語言是%@", name);
};
NSLog(@"%@", block);
複製代碼
運行下你會發現控制檯打印的是:
<__NSStackBlock__: 0x7fff5480fa18>
複製代碼
什麼,你說什麼,你打印出來的是__ NSMallocBlock __
? 那是由於你在ARC下編譯的,ARC下編譯器編譯時會幫你優化自動幫你加上了copy操做,你能夠用-fno-objc-arc
關閉ARC再看一下 3. NSMallocBlock 堆塊: NSMallocBlock心裏獨白:我已經被暴露了,爲何要最後才介紹我!! 堆block內存保存於堆區,在變量做用域結束時不受影響。經過以前在ARC下的輸出已經看到了__ NSMallocBlock __.因此咱們在定義block類型的屬性時經常加上copy修飾,這個修飾實際上是多餘的,系統在ARC的時候已經幫咱們作了copy,可是仍是建議寫上copy。
這條主要是爲了代碼更易讀,也比較重要。
- (void)getDataWithHost:(NSString *)host success:(void (^)(id responseDic))success;
//以上要改爲下面這種
typedef void (^SuccessBlock)(id responseDic);
- (void)getDataWithHost:(NSString *)host success:(SuccessBlock)success;
複製代碼
在iOS開發中,咱們常常須要異步執行一些任務,而後等待任務執行結束以後通知相關方法。實現此需求的作法不少,好比說有些人可能會選擇用委託協議。那麼在這種異步執行一些任務,而後等待執行結束以後調用代理的時候,可能代碼就會比較分散。當多個任務都須要異步,等等就顯得比較不那麼合理了。 因此咱們能夠考慮使用block的方式設計,這樣業務相關的代碼會比較緊湊,不會顯得那麼凌亂。
這點比較基礎了,可是要稍微說一下,不是必定得在block中使用weakself,好比下面:
[YTKNetwork requestBlock:^(id responsObject) {
NSLog(@"%@",self.name);
}];
複製代碼
block 不是被self所持有的,在block中就可使用self
在iOS開發中,若是有多個線程要執行同一份代碼,咱們可能須要加鎖來實現某種同步機制。有人可能第一印象想到的就是@synchronized(self)
,例如:
- (NSString*)someString {
@synchronized(self) {
return _someString;
}
}
- (void)setSomeString:(NSString*)someString {
@synchronized(self) {
_someString = someString;
}
}
複製代碼
這樣寫法效率很低,並且也不能保證線程中以爲的安全。若是有不少屬性,那麼每一個屬性的同步塊都要等其餘同步塊執行完畢才能執行。 應該用GCD來替換:
_syncQueue = dispatch_queue_create("syncQueue", DISPATCH_QUEUE_CONCURRENT);
//讀取字符串
- (NSString*)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString*)someString {
dispatch_barrier_async(_syncQueue, ^{
_someString = someString;
});
}
複製代碼
Objective-C本質上是一門分廠動態的語言,開發者在開發中能夠指定任何一個方法去調用,也能夠延遲調用一些方法,或者指定運行方法的線程。通常咱們會想到performSelector
,可是在GCD出來以後基本就沒那麼須要performSelector
了,performSelector
也有不少缺點:
performSelector
咱們常常會看到編譯器發出以下警告:warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]
performSelector
的返回值只能是void或對象類型。performSelector
沒法處理帶有多個參數的選擇子,最多隻能處理兩個參數。 爲了改變這些,咱們能夠用下面這種方式dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
複製代碼
替換掉
[self performSelectorOnMainThread:@selector(doSomething)
withObject:nil
waitUntilDone:NO];
複製代碼
而後還能夠用
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
[self doSomething];
});
複製代碼
替換
[self performSelector:@selector(doSomething)
withObject:nil
afterDelay:5.0];
複製代碼
GCD技術確實很棒,可是也有一些侷限性,或者說有一些場景並不適合。好比過想取消隊列中的某個操做,或者須要後臺執行任務。還有一種技術叫NSOperationQueue
,其實NSOperationQueue
跟GCD有不少相像之處。NSOperationQueue
在GCD以前就已經有了,GCD就是在其某些原理上構建的。GCD是C層次的API,而NSOperation
是重量級的Objective-C對象。 使用NSOperation
和NSOperationQueue
的優勢:
cancel方法
,用以代表此任務不須要執行。不過已經啓動的任務沒法取消。GCD隊列是沒法取消的,GCD是「安排好以後就無論了(fire and forget)」。下載清單文件操做
執行完畢以後開始同時執行。這條主要是介紹dispatch group,任務分組的功能。他能夠把任務分組,而後等待這組任務執行完畢時會有通知,開發者能夠拿到結果真後繼續下一步操做。 另外經過dispatch group在併發隊列上同時執行多項任務的時候,GCD會根據系統資源狀態來幫忙調度這些併發執行的任務。
這條講的是經常使用的dispatch_once
+ (id)sharedInstance {
static EOCClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
複製代碼
dispatch_once比較高效,沒有重量級的同步機制。
在Objective-C中除了Foundation 與CoreFoundation以外還有不少系統庫,其中包括但不限於下面列出的這些:
經過無縫橋接技術,能夠在定義於Foundation框架中的類和CoreFoundation框架中的C語言數據結構之間來回轉換。 下面代碼展現了簡單的無縫橋接:
NSArray *anNSArray = @[@1, @2, @3, @4, @5];
CFArrayRef aCFArray = (__bridge CFArrayRef)anNSArray;
NSLog(@"Size of array = %li", CFArrayGetCount(aCFArray));
//Output: Size of array = 5
複製代碼
轉換操做中的__bridge
告訴ARC如何傳力轉換所涉及的OC對象,也就是ARC仍然具有這個OC對象的全部權。__bridge_retained
與之相反。這裏要注意用完了數組要本身釋放,使用CFRelease(aCFArray)
前面有提到過的。
在構建緩存時應該儘可能選用NSCache而非NSDictionary,NSCache會在系統資源將要耗盡時自動刪減緩存,而使用NSDictionary只能經過系統低內存警告方法去手動處理。此外NSCache還會看狀況刪減最久未使用的對象,並且是線程安全的。
在iOS開發中常常會用到定時器:NSTimer,因爲NSTimer會生成指向其使用者的引用,而其使用者若是也引用了NSTimer,那就造成了該死的循環引用,好比下面這個例子:
#import <Foundation/Foundation.h>
@interface EOCClass : NSObject
- (void)startPolling;
- (void)stopPolling;
@end
@implementation EOCClass {
NSTimer *_pollTimer;
}
- (id)init {
return [super init];
}
- (void)dealloc {
[_pollTimer invalidate];
}
- (void)stopPolling {
[_pollTimer invalidate];
_pollTimer = nil;
}
- (void)startPolling {
_pollTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(p_doPoll)
userInfo:nil
repeats:YES];
}
- (void)p_doPoll {
// Poll the resource
}
@end
複製代碼
若是建立了本類的實例,並調用其startPolling方法開始定時器,因爲目標對象是self,因此要保留此實例,由於定時器是用成員變量存放的,因此self也保留了計時器,因此此時存在保留環。此時要麼調用stopPolling
,要麼令系統將此實例回收,只有這樣才能打破保留環。 這是一個很常見的內存泄漏,那麼怎麼解決呢?這個問題能夠經過block來解決。能夠添加這樣的一個分類:
#import <Foundation/Foundation.h>
//.h
@interface NSTimer (EOCBlocksSupport)
+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
//.m
@implementation NSTimer (EOCBlocksSupport)
+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(eoc_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)eoc_blockInvoke:(NSTimer*)timer {
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
@end
複製代碼
EOF : 因爲我的能力有限,不免有一些遺漏或者錯誤,請各位看官不吝賜教!謝謝!同時若是有任何問題也能夠在下方留言,歡迎一塊兒交流進步~最後感謝做者Matt Galloway以及譯者!更多細節仍是請翻閱圖書,能夠點擊這裏下載PDF版,原版英文版PDF我也有存~本文已經同步到我的博客