聲明:這個(gè)筆記的系列是我每天早上打開電腦第一件做的事情瞳步,當(dāng)然使用的時(shí)間也不是很多因?yàn)檫€有其他的事情去做蟀给,雖然吧自己買了紙質(zhì)的書但是做筆記和看的時(shí)候基本都是看的電子版本冰评,一共52個(gè)Tip每一個(gè)Tip的要點(diǎn)我是完全謄寫下來(lái)的纫普,害怕自己說(shuō)的不明白所以就謄寫也算是加強(qiáng)記憶峭弟,我會(huì)持續(xù)修改把自己未來(lái)遇到的所有相關(guān)的點(diǎn)都加進(jìn)去攀芯,最后希望讀者尊重原著屯断,購(gòu)買正版書籍。PS:不要打賞要喜歡~
GitHub代碼網(wǎng)址侣诺,大大們給個(gè)鼓勵(lì)Star啊殖演。
整個(gè)系列筆記目錄
《Effective Objective-C 2.0》第一份讀書筆記
《Effective Objective-C 2.0》第二份讀書筆記
《Effective Objective-C 2.0》第三份讀書筆記
第四章 協(xié)議與分類
23.通過(guò)委托與數(shù)據(jù)源協(xié)議進(jìn)行對(duì)象間通信
我舉一個(gè)~ 獲取網(wǎng)絡(luò)數(shù)據(jù)的類含有一個(gè)“委托對(duì)象”,在獲取完數(shù)據(jù)之后年鸳,它會(huì)回調(diào)這個(gè)委托對(duì)象趴久。
EOCDataModel對(duì)象就是EOCNetworkFetcher的委托對(duì)象。EOCDataModel請(qǐng)求EOCNetworkFetcher“以異步方式執(zhí)行一項(xiàng)任務(wù)”搔确,而EOCNetworkFetcher在執(zhí)行完這項(xiàng)任務(wù)之后彼棍,就會(huì)通知其委托對(duì)象,也就是EOCDataModel膳算。
這里面代理要用weak修飾座硕。通常情況下,因?yàn)榇韉elegate要對(duì)比TableView做相應(yīng)的操作涕蜂,所以代理delegate要持有TableView這個(gè)對(duì)象华匾,而如果我們用Strong修飾TableView的delegate屬性,就會(huì)引入保留環(huán)(retain cycle)机隙。
如果要在委托對(duì)象上調(diào)用可選方法蜘拉,那么必須提前使用類型信息查詢方法判斷這個(gè)委托對(duì)象是否響應(yīng)相關(guān)選擇子。
NSData * data ;
if([_delegate respondsToSelector:@selector(networkFetcher: didReceiveData:)]){
[_delegate networkFetcher: didReceiveData:];
}
委托模式: 對(duì)象把應(yīng)該對(duì)某個(gè)行為的責(zé)任委托給另一個(gè)類有鹿。
以TableView為例子
委托模式是信息從類流向受委托者也就是讓這個(gè)責(zé)任流向了受委托者旭旭。
數(shù)據(jù)源模式(Data Source Pattern)是數(shù)據(jù)流向TableView,決定TableView的布局印颤。
寫一個(gè)現(xiàn)實(shí)中的例子吧您机。(通過(guò)協(xié)議代理哈,我知道這個(gè)方案更好的方案,只不過(guò)想寫出來(lái)下協(xié)議代理的步驟)际看。(詳細(xì)見第二十三條Demo)咸产。
加入我想通過(guò)一個(gè)類開啟定時(shí)器,然后另一個(gè)類來(lái)監(jiān)控這個(gè)類的定時(shí)器仲闽,當(dāng)這個(gè)定時(shí)器開啟5S之后脑溢,相應(yīng)的做一些事情,首先是被監(jiān)視定時(shí)器的那個(gè)類:
//.h
#import <Foundation/Foundation.h>
@class Thirtyeight;
//首先是協(xié)議代理方式
typedef void(^iSuCompletionHandle)(int five);
@protocol iSuNetworkFetcherDelegate <NSObject>
- (void)netwrokFecher:(Thirtyeight *)networkFetcher didFinishWithData:(int)five;
@end
@interface Thirtyeight : NSObject
@property (nonatomic,weak) id<iSuNetworkFetcherDelegate> delegate;
@property (nonatomic,strong) NSTimer * iSuTimer;
@property (nonatomic,assign) int iSuNumber;
@property (nonatomic,copy) iSuCompletionHandle iSuCompletion;
- (void)TimerTest;
@end
//.m
@implementation Thirtyeight
- (void)TimerTest{
self.iSuNumber = 0;
//這個(gè)Block我寫著玩的赖欣,實(shí)際作用不大屑彻,只不過(guò)下面有一個(gè)關(guān)于block的問(wèn)題。
__weak typeof(self) weakSelf = self;
self.iSuCompletion = ^(int five) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.iSuNumber = five;
NSLog(@"賦值給self.iSuNumber為%i",strongSelf.iSuNumber);
};
__block int num = 0;
_iSuTimer =[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
__strong typeof(weakSelf) strongSelf = weakSelf;
num++;
NSLog(@"定時(shí)器里面的變化%d",num);
/*
這邊就是最重要的代碼了顶吮,也就是傳遞給監(jiān)聽對(duì)象數(shù)據(jù)社牲。
*/
[strongSelf.delegate netwrokFecher:strongSelf didFinishWithData:num];
if (num == 5) {
strongSelf.iSuNumber = num;
NSLog(@"現(xiàn)在的數(shù)值為:%d",strongSelf.iSuNumber);
//這邊調(diào)用block的時(shí)候,是不允許我們使用strong.iSuCompletion這樣的操作悴了。
_iSuCompletion(num);
[_iSuTimer invalidate];
};
}];
}
主動(dòng)監(jiān)聽類:
//.m
@implementation ThirtyeightViewController
interface ThirtyeightViewController ()<iSuNetworkFetcherDelegate>
- (void)netwrokFecher:(Thirtyeight *)networkFetcher didFinishWithData:(int)five{
//這樣就可以監(jiān)聽了搏恤。
NSLog(@"現(xiàn)在行走的時(shí)間是多少:%d",five);
}
@end
要點(diǎn):
- 委托模式為對(duì)象提供一套接口,使其可由此將相關(guān)事件告知其他對(duì)象湃交。
- 將委托對(duì)象應(yīng)該支持的接口定義為協(xié)議熟空,在協(xié)議中把可能需要處理的事件定義成方法。
- 當(dāng)某對(duì)象需要從另外一個(gè)對(duì)象中獲取數(shù)據(jù)時(shí)搞莺,可以使用委托模式息罗。這種情況下,該模式也成為“數(shù)據(jù)源協(xié)議(data source protocal)”
- 若有必要才沧,可實(shí)現(xiàn)含有位段的結(jié)構(gòu)體迈喉,將委托對(duì)象是否響應(yīng)相關(guān)協(xié)議方法這一信息緩存到其中。
24.將類的實(shí)現(xiàn)代碼分散到便于管理的數(shù)個(gè)分類之中
分類功能是對(duì)類對(duì)相應(yīng)功能的整理糜工,使得整個(gè)類的條理更加清晰弊添,防止更多不需要的方法在頭文件中導(dǎo)入,影響系統(tǒng)性能捌木。
例如:
//.h
#import <Foundation/Foundation.h>
@interface EOCBengi : NSObject
@property (nonatomic, copy, readonly) NSString * firstName;
@property (nonatomic, copy, readonly) NSString * lastName;
@property (nonatomic, strong, readonly) NSArray * friends;
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
@end
@interface EOCBengi(Friendship)
- (void)addFriend:(EOCBengi *)person;
- (void)removeFirend:(EOCBengi *)person;
- (BOOL)isFriendWith:(EOCBengi *)person;
@end
@interface EOCBengi(Work)
- (void)performDaysWork;
- (void)takeVacationFromWork;
@end
@interface EOCBengi(Play)
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
//.m
#import "EOCBengi.h"
@implementation EOCBengi
@end
@implementation EOCBengi(Friendship)
@end
@implementation EOCBengi(Work)
@end
@implementation EOCBengi(Play)
@end
要點(diǎn):
- 使用分類機(jī)制把類的實(shí)現(xiàn)代碼劃為易于管理的小塊。
- 將應(yīng)該視為“私有”的方法歸為Private的分類中嫉戚,來(lái)隱藏實(shí)現(xiàn)細(xì)節(jié)刨裆。
25.總是為第三方類的分類名稱加前綴
分類機(jī)制通常用于向無(wú)源碼的既有類中新增功能。
要點(diǎn):
- 向第三方類中添加分類時(shí)彬檀,總應(yīng)給其名稱加上你專用的前綴帆啃。
- 向第三方磊中添加分類時(shí),總應(yīng)給其中的方法加上你專用的前綴窍帝。
26.勿在分類中聲明屬性
盡管在技術(shù)上講努潘,分類中也是可以聲明屬性的,但這種做法還是要盡量避免的。原因在于疯坤,除了"class-continuation分類"之外报慕,其他分類都無(wú)法向類中新增實(shí)例變量,因此压怠,它們無(wú)法把實(shí)現(xiàn)屬性所需的實(shí)例變量合成出來(lái)眠冈。
如果在分類中聲明了屬性,我們可以通過(guò)運(yùn)行期關(guān)聯(lián)對(duì)象的方式get set數(shù)值菌瘫,但是這樣容易引起內(nèi)存問(wèn)題蜗顽。因?yàn)槲覀冊(cè)跒閷傩詫?shí)現(xiàn)存取方法時(shí),經(jīng)常會(huì)忘記遵守從內(nèi)存管理語(yǔ)義雨让,那么有可能在不經(jīng)意之間就造成了內(nèi)存出現(xiàn)錯(cuò)誤雇盖。(當(dāng)然這個(gè)錯(cuò)誤是自己可以解決的)。
#import <objc/runtime.h>
static const char * kFriendsPropertyKey = “kFriendPropertyKey”
@implemetation EOCPerson(Friendship)
(NSArray*)friends(
return objc_getAssociatedObject(self, kFriendsPropertyKey);
)
(void)setFriends:(NSArray*)friends{
objc_setAssociatedObject(self , kFriendsPropertyKey , friends,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
要點(diǎn)
- 把封裝數(shù)據(jù)所用的全部屬性都定義在主接口里栖忠。
- 在”class - continuation分類” 之外的其他分類中崔挖,可以定義存取方法,但盡量不要定義屬性娃闲。
27.使用“class- continuation分類“ 隱藏實(shí)現(xiàn)細(xì)節(jié)
編寫的準(zhǔn)則是
@interface EOCPerson ()
//Methods here
@end
這個(gè)就是正常的我們創(chuàng)建的.m上面的東西虚汛,就是class- continuation
類的延續(xù)。
要點(diǎn):
- 通過(guò)“ class-continuation分類”向類中新增實(shí)例變量皇帮。
- 如果某屬性在主接口聲明為“只讀”卷哩,而類的內(nèi)部又要設(shè)置方法修改此屬性,那么就在“calss-continuation分類’中將其擴(kuò)展為“可讀寫”属拾。
- 把私有方法的原型聲明在“calss-continuation分類”里面。
- 若要使類所遵循的協(xié)議不被人知道渐白,則可用“class-continuation分類”中聲明尊浓。
28.通過(guò)協(xié)議提供匿名對(duì)象
匿名對(duì)象:以內(nèi)聯(lián)形式所創(chuàng)建出來(lái)的無(wú)名類。
這個(gè)匿名對(duì)象具體表象就是:
@property (nonatomic ,weak) id <EOCDelegate> delegate;
由于該屬性類型是id<EOCDelegate>纯衍,所以實(shí)際上任何類的對(duì)象都能充當(dāng)這一屬性栋齿,即使這個(gè)類不集成與NSObject也是可以的,只要遵守協(xié)議<EOCDelegate>就好了襟诸。相同例子還有字典的存儲(chǔ)瓦堵,我們知道字典對(duì)于key是copy而對(duì)于值為保留的:
- (void)setObject:(id)object forKey:(id<NSCopying>)key;
要點(diǎn):
- 協(xié)議可在某個(gè)程度上提供匿名類型。具體的對(duì)象類型可以淡化成遵從某協(xié)議的id類型歌亲,協(xié)議里規(guī)定了對(duì)象所實(shí)現(xiàn)的方法菇用。
- 使用匿名對(duì)象來(lái)隱藏類型名稱(或類名)
- 如果具體類型不重要,重要的是對(duì)象能夠響應(yīng)(定義在協(xié)議里的)特定方法陷揪,那么可使用匿名對(duì)象來(lái)表示惋鸥。
第五章 內(nèi)存管理
29.理解引用計(jì)數(shù)
在引用計(jì)數(shù)的架構(gòu)下杂穷,每個(gè)對(duì)象都一個(gè)計(jì)數(shù)器,NSObject協(xié)議聲明了下面三個(gè)方法用于操作計(jì)數(shù)器卦绣,以遞增或遞減其值耐量。
Retain 遞增保留計(jì)數(shù)
release 遞減保留計(jì)數(shù)
autorelease 待稍后清理“自動(dòng)釋放池(autorelease pool)”時(shí),再遞減保留計(jì)數(shù)迎卤。
NSMutableArray * array = [NSMutableArray alloc] init];
NSNumber * number =[NSNumber alloc] initWithInt:1223];
[array addObject:number];
[number release];
[array release];
如果我們不走[array release]方法拴鸵,那么我們知道number對(duì)象還是會(huì)存在的,因?yàn)閿?shù)組還在持有他蜗搔,但是絕對(duì)不應(yīng)該假設(shè)對(duì)象一定存在劲藐,也就是說(shuō),不要這樣寫代碼:
NSNumber * number = [NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
NSLog(@“ number = %@”,number);
如果我們調(diào)用了release之后樟凄,基于某些原因聘芜,其保留計(jì)數(shù)可能降至0.
為了避免在不經(jīng)意間使用了無(wú)效對(duì)象,一般調(diào)用玩realse之后都會(huì)清空指針缝龄。這就保證了不會(huì)出現(xiàn)可能指向無(wú)效對(duì)象的指針汰现。這種指針通常稱為懸掛指針
。比如:
NSNumber * number = [NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
number = nil;
不管是數(shù)組叔壤,其他的對(duì)象也可以保留別的對(duì)象瞎饲,這一般都是用過(guò)“屬性”來(lái)實(shí)現(xiàn)。會(huì)用到相關(guān)實(shí)例變量的獲取方法及設(shè)置方法炼绘。若屬性為“strong關(guān)系(string relationship)”嗅战,那么設(shè)置的屬性就會(huì)保留,比方說(shuō)俺亮,有一個(gè)屬性為foo
- (void)setFoo:(id ) foo {
[foo retain];
[_foo release];
_foo = foo;
}
此方法將保留新值并釋放舊值驮捍,然后更新實(shí)例變量,令其指向新值脚曾。順序很重要东且。加入還未保留新值就先將舊值釋放了,而且兩個(gè)值又指向同一個(gè)對(duì)象本讥,那么珊泳,先執(zhí)行的release操作就可能導(dǎo)致喜用將對(duì)象永久回收。而后續(xù)的retain操作則無(wú)法令這個(gè)已經(jīng)徹底回收的對(duì)象復(fù)生拷沸,于是實(shí)例變量就變成了懸掛指針旨椒。
自動(dòng)釋放池
調(diào)用release會(huì)立刻遞減對(duì)象的保留計(jì)數(shù),然而有些時(shí)候不能可以不調(diào)用release堵漱,改調(diào)用autorelease,此方法會(huì)在稍后遞減計(jì)數(shù)涣仿,通常是在下一次“事件循環(huán)(event loop)時(shí)遞減勤庐,不過(guò)也可能執(zhí)行的更早些”示惊。
此特性很有用,尤其在方法中返回對(duì)象時(shí)更應(yīng)該用它愉镰,在這種情況下:
(NSString*)stringValue{
NSString * str =[NSString alloc] initWithFormat:@“I am this:%@,self”];
return str;
}
這個(gè)時(shí)候返回的str對(duì)象比期望的要多1 因?yàn)檎{(diào)用 alloc 會(huì)加1米罚,但是不會(huì)有對(duì)應(yīng)的釋放操作,但是不能在方法內(nèi)部釋放 str,否則沒等方法返回系統(tǒng)就把該方法回收了丈探。這里應(yīng)該用autorelease,它會(huì)稍后釋放對(duì)象录择,從而給調(diào)用者留下足夠長(zhǎng)的時(shí)間,使其可以在需要時(shí)先保留返回?cái)?shù)值碗降,換句話就是保證對(duì)象在跨越“方法調(diào)用邊界(method call boundary)”后一定存活隘竭。
(NSString *)stringValue{
NSString * str =[NSString alloc] initWithFormat:@“I am this %@”,self];
return [str autorelease];
}
保留環(huán)(retain cycle)
就是相互持有
要點(diǎn):
- 引用計(jì)數(shù)機(jī)制通過(guò)可以遞增遞減的計(jì)數(shù)器來(lái)管理內(nèi)存,對(duì)象創(chuàng)建好之后讼渊,其保留計(jì)數(shù)至少為1动看。若保留計(jì)數(shù)為正,則對(duì)象繼續(xù)存活爪幻。當(dāng)保留計(jì)數(shù)降為0的時(shí)菱皆,對(duì)象就被銷毀了。
- 在對(duì)象聲明周期中挨稿,其余對(duì)象通過(guò)引用來(lái)保留或釋放此對(duì)象仇轻,保留與釋放操作分別會(huì)遞增或者遞減保留計(jì)數(shù)。
30. 以ARC簡(jiǎn)化引用計(jì)數(shù)
Clang編譯器自帶一個(gè)“靜態(tài)分析器(static analyzer)” 奶甘。用于指明程序里引用計(jì)數(shù)出問(wèn)題的地方篷店。
由于ARC會(huì)自動(dòng)執(zhí)行retain,release甩十,autorelease等操作船庇,所以不能直接調(diào)用retain,release,autorelease,dealloc 。
將內(nèi)存管理語(yǔ)義在方法命中表示出來(lái)早就成了OC的慣例侣监,而ARC則將之確立為硬性規(guī)定鸭轮。這些規(guī)則簡(jiǎn)單的體現(xiàn)在方法名上。若方法名以下列語(yǔ)句開頭橄霉,則其返回的對(duì)象鬼調(diào)用者所有
alloc 窃爷,new ,copy 姓蜂,mutableCopy
對(duì)于ARC和MRC的轉(zhuǎn)換:
ARC:
_myPerson = [EOCPerson personWithName:@“Bob Smith”];
MRC:
EOCPerson * tmp = [EOCPerson personWithName:@“Bob Smith”];
_myPerson = [tmp retain];
ARC可以在運(yùn)行期檢測(cè)到這一對(duì)多余的操作按厘,也就是autorelease及緊跟其后的retain。為了優(yōu)化代碼钱慢,在方法中返回自動(dòng)釋放的對(duì)象時(shí)逮京,要執(zhí)行一個(gè)特殊函數(shù)。此時(shí)不直接調(diào)用對(duì)象的autorelease方法束莫,而是改用調(diào)用objc_autoreleaseReturnValue懒棉。此函數(shù)會(huì)檢視當(dāng)前方法之后即將要執(zhí)行的那段代碼草描。若發(fā)現(xiàn)那段代碼要在返回的對(duì)象上執(zhí)行retain操作,則設(shè)置全局?jǐn)?shù)據(jù)結(jié)構(gòu)中的一個(gè)標(biāo)志位策严,而不執(zhí)行autorelease操作穗慕。
如果返回一個(gè)自動(dòng)釋放的對(duì)象,而調(diào)用方法的代碼中保留此對(duì)象妻导,那么此時(shí)不執(zhí)行retain,而改成objc_retainAutoreleasedReturnValue函數(shù)逛绵。此函數(shù)要檢測(cè)剛才提到的那個(gè)標(biāo)志位,若已經(jīng)執(zhí)行retain操作倔韭。設(shè)置并檢測(cè)標(biāo)志位术浪。要比調(diào)用autorelease和retain更快。
要點(diǎn):
- 有ARC之后狐肢,程序員就無(wú)須擔(dān)心內(nèi)存管理問(wèn)題添吗。使用ARC來(lái)變成,可省去類中很多的“樣板代碼”份名。
- ARC管理對(duì)象生命期的辦法基本上就是:在合適的地方插入”保留“及”釋放“操作碟联。在ARC環(huán)境下,變量的內(nèi)存管理語(yǔ)義可以通過(guò)修飾符指明僵腺,而原來(lái)則需要手動(dòng)執(zhí)行“保留”及“釋放”操作鲤孵。
- 由方法所返回的對(duì)象,其內(nèi)存管理語(yǔ)義總是通過(guò)方法名來(lái)體現(xiàn)辰如。ARC將此確定為開發(fā)者必須遵守的規(guī)則普监。
- ARC只負(fù)責(zé)管理Object-C對(duì)象的內(nèi)存。尤其要注意:CoreFoundation對(duì)象不歸ARC管理琉兜,開發(fā)者必須適時(shí)調(diào)用CFRetain/CFRelease凯正。
31.在dealloc方法中釋放引用并解除監(jiān)聽
如果手動(dòng)管理引用計(jì)數(shù)的話 dealloc中需要調(diào)用 [super dealloc] 而ARC中則不需要調(diào)用[super dealloc]。
要點(diǎn):
- 在dealloc方法里豌蟋,應(yīng)該做的事情就是釋放指向其他對(duì)象的引用廊散,并取消原來(lái)訂閱的“鍵值觀察“(KVO)或NSNotificationCenter等通知 ,不要做其他的事情梧疲。
- 如果對(duì)象持有文件描述符等系統(tǒng)資源允睹,那么應(yīng)該專門編寫一個(gè)方法來(lái)釋放此種資源。這樣的類要和其使用者約定:用完資源后必須調(diào)用close方法幌氮。
- 執(zhí)行異步任務(wù)的方法不應(yīng)在dealloc里調(diào)用缭受;只能在正常狀態(tài)下執(zhí)行的那些方法也不應(yīng)在dealloc里調(diào)用,因此此時(shí)對(duì)象已處于正在回收的狀態(tài)下该互。
32.編寫“異常安全代碼”時(shí)留意內(nèi)存管理問(wèn)題
OC的錯(cuò)誤模型表示米者,異常只有在發(fā)生了嚴(yán)重錯(cuò)誤(21條詳解)的時(shí)候才會(huì)被拋出。
這個(gè)Tip里面我們研究MRC情況下怎么實(shí)現(xiàn)異常處理內(nèi)存宇智,就像C++那樣的塘雳。
我們使用try塊實(shí)現(xiàn)這個(gè)功能:
@finally的作用在于無(wú)論是否拋出異常都會(huì)走這一步陆盘,因?yàn)槲覀儾磺宄绦驎?huì)不會(huì)在dosomeThing中拋出異常,如果拋出異嘲苊鳎可能會(huì)影響下面的動(dòng)作。
@try {
ThirtyTwo * object =[[ThirtyTwo alloc]init];
[object dosomeThing];
// [object release];
} @catch (NSException *exception) {
NSLog(@"拋出異常");
} @finally {
// [object release];
}
要點(diǎn):
- 捕捉異常時(shí)太防,一定要注意try塊內(nèi)創(chuàng)立的對(duì)象清理干凈妻顶。
- 在默認(rèn)情況下,ARC不生成安全處理異常所需的清理代碼蜒车。開啟編譯器標(biāo)志后讳嘱,可生成這種代碼,不會(huì)導(dǎo)致應(yīng)用程序變大酿愧,而且降低運(yùn)行效率沥潭。
33.以弱引用避免保留環(huán)
強(qiáng)引用:
#import <Foundation/Foundation.h>
@class EOCClassA;
@class EOCClassB;
@interface EOCClassA :NSObject
@property (nonatomic, strong) EOCClassB * other;
@end
@interface EOCClassB :NSObject
@porperty (nonatomic, strong) EOCClassA * other;
@end
避免保留環(huán)的最佳方式就是弱引用,用weak或者是unsafe_unretained即可嬉挡。
要點(diǎn):
- 將某些引用設(shè)置為weak,可避免出現(xiàn)“保留環(huán)”钝鸽。
- weak引用可以自動(dòng)清空,也可以不自動(dòng)清空庞钢。自動(dòng)清空是隨著ARC而引入的新特性拔恰,由運(yùn)行期系統(tǒng)來(lái)實(shí)現(xiàn)。在具備自動(dòng)清空的弱引用上基括,可以隨意讀取其數(shù)據(jù)颜懊,因?yàn)檫@種引用不會(huì)指向已經(jīng)回收過(guò)的對(duì)象。
34.以“自動(dòng)釋放池塊”降低內(nèi)存峰值
創(chuàng)建自動(dòng)釋放池的方法如下:
@autoreleasepool {
}
當(dāng)數(shù)據(jù)量過(guò)大的時(shí)候我們可以通過(guò)嵌套自動(dòng)釋放池的方法來(lái)降低內(nèi)存峰值风皿。
NSArray * databaseRecorde = /******/;
NSMutableArray * people = [NSMutableArray new];
for (NSDictionary * record in databaseRecords){
@autoreleasepool {
EOCPerson * person = [[EOCPerson alloc] initWithRecord:record];
[person addObject: person];
}
}
自動(dòng)釋放池機(jī)制就像“棧(stack)”一樣河爹。系統(tǒng)創(chuàng)建好自動(dòng)釋放池之后,就將其推入棧中桐款,而清空自動(dòng)釋放池咸这,則相當(dāng)于將其從棧中彈出。在對(duì)象上執(zhí)行自動(dòng)釋放操作鲁僚,就等于將其放入棧頂?shù)哪莻€(gè)池里炊苫。
之前MRC的時(shí)候有一個(gè)創(chuàng)建釋放池的類NSAutoreleasePool 此對(duì)象更加重量級(jí)(heavyweight) 通常用來(lái)創(chuàng)建偶爾需要清空的池,比方說(shuō):
NSArray * databasRecode = /****/
NSMutableArray * people = [NSMutableArray new];
int i = 0 ;
NSAutoreleasePool * pool = [NSAutoreleasePool alloc] init ];
for (NSDictionary * record in databaseRecode ){
EOCPerson * person = [EOCPerson alloc] initWithRecord:record];
[people addobject:person];
if (++i == 10){
[pool drain];
i = 0;
}
}
[pool drain];
是否應(yīng)該用池來(lái)優(yōu)化效率冰沙,完全取決于具體的應(yīng)用程序侨艾。首先得監(jiān)控內(nèi)存用量,判斷其中有沒有需要解決的問(wèn)題拓挥,如果沒完成這一步唠梨,那就別急著優(yōu)化。盡管自動(dòng)釋放池塊的開銷不太大侥啤,但畢竟還是有的当叭,所以盡量不要建立額外的自動(dòng)釋放池茬故。
要點(diǎn):
- 自動(dòng)釋放池排布在棧中,對(duì)象受到autorelease消息后蚁鳖,系統(tǒng)將其放入最頂端的池里磺芭。
- 合理運(yùn)用自動(dòng)釋放池,可降低應(yīng)用程序的內(nèi)存峰值醉箕。
- @autoreleasepool 這種新式寫法能創(chuàng)建出更為輕便的自動(dòng)釋放池钾腺。
35.用“僵尸對(duì)象”調(diào)用內(nèi)存管理問(wèn)題
僵尸對(duì)象:?jiǎn)?dòng)這項(xiàng)調(diào)試成功之后,運(yùn)行期系統(tǒng)會(huì)把所有已經(jīng)回收的實(shí)例轉(zhuǎn)化為特殊的“僵尸對(duì)象(Zombie Object)”讥裤,而不會(huì)真正回收他們放棒。這樣就使得我們能夠及時(shí)定位到錯(cuò)誤位置。
打開全局?jǐn)帱c(diǎn)和僵尸對(duì)象調(diào)試功能有兩種方法:
第一種方法:
點(diǎn)擊"+"選擇Exception Breakpoint己英。
第二種方法:
點(diǎn)擊上方調(diào)試開關(guān)右邊的程序管理臺(tái)间螟,選擇Edit Scheme。
然后選擇Run - > Diagnostics - > Zombie Objects损肛。
要點(diǎn):
- 系統(tǒng)在回收對(duì)象時(shí)厢破,可以不將其真的回收,而是把它轉(zhuǎn)化為僵尸對(duì)象荧关。通過(guò)環(huán)境變量NSZombieEnabled可開啟此功能溉奕。
- 系統(tǒng)會(huì)修改對(duì)象的isa指針,令其指向特殊的僵尸類忍啤,從而使該對(duì)象變成僵尸對(duì)象加勤。僵尸類能夠響應(yīng)所有的選擇子,響應(yīng)方式為:打印一條包含消息內(nèi)容及其接收者的消息同波,然后終止應(yīng)用程序鳄梅。
36.不要使用retainCount
要點(diǎn):
- 對(duì)象的保留計(jì)數(shù)看似有用,實(shí)則不然未檩,因?yàn)槿魏谓o定時(shí)間點(diǎn)上的“絕對(duì)保留計(jì)數(shù)(absolute retain count)”都無(wú)法反應(yīng)對(duì)象生命期的全貌戴尸。
- 引入ARC之后,retainCount方法就正式廢止冤狡,在ARC下調(diào)用該方法會(huì)導(dǎo)致編譯器報(bào)錯(cuò)孙蒙。
結(jié)尾
自己寫的筆記首先是用Pages寫的,寫完之后放到簡(jiǎn)書里面以為也就剩下個(gè)排版了悲雳,結(jié)果發(fā)現(xiàn)基本上每一個(gè)點(diǎn)的總結(jié)都不讓自己滿意挎峦,但是又想早點(diǎn)放上去,總感覺自己被什么追趕著合瓢,哈哈坦胶,本來(lái)寫完筆記的時(shí)候是2W字的,結(jié)果到第二次發(fā)表的時(shí)候發(fā)現(xiàn)就成了2.5W了,需要改進(jìn)的東西還是太多顿苇,希望朋友們有什么改進(jìn)的提議都可以告訴我峭咒,我會(huì)一直補(bǔ)充這個(gè)筆記,然后抓緊改GitHub上的代碼~