1.blcok相關(guān)知識(shí)?
在ARC環(huán)境下辖试,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的block進(jìn)行一次copy操作,將block復(fù)制到堆上。
//block拷貝到堆上的幾種情況:
????1.調(diào)用block的copy實(shí)例方法
????2.Block作為函數(shù)返回值
????3.將block賦值給有__strong修飾符的id類型的類或block類型成員變量時(shí)
????4.在方法名中含有usingBlock的cocoa框架方法或GCD的API傳遞blokc時(shí)<code>
我們使用block有兩種方式姚淆,逃逸和非逃逸(借用swift中的說(shuō)法)。
非逃逸:聲明的block的生命周期就是聲明所在的函數(shù)體的生命周期屡律。我們?cè)诤瘮?shù)體中聲明一個(gè)block腌逢,這個(gè)block會(huì)在函數(shù)體結(jié)束時(shí)釋放。
逃逸:聲明的block生命周期和聲明所在的函數(shù)體無(wú)關(guān)了超埋。我們?cè)诤瘮?shù)A中聲明的block搏讶,在B中也可以調(diào)用。
1.NSGlobalBlock (不捕獲自動(dòng)變量的類型或者捕獲的是靜態(tài)局部變量)
此處指的不捕獲自動(dòng)變量霍殴,變量不包含全局變量媒惕,因?yàn)槿肿兞康奶厥馍芷冢恍枰东@来庭,也可以在block中訪問(wèn)妒蔚。
1.值捕獲:捕獲的變量為其指針指向的值,或基礎(chǔ)數(shù)據(jù)類型的值
指針指向的值:
2.地址捕獲:捕獲的變量為其指針本身月弛,或指向基礎(chǔ)數(shù)據(jù)類型的指針
4.解決循環(huán)引用肴盏,打破block對(duì)對(duì)象的強(qiáng)引用即可,兩種方式:__weak對(duì)象帽衙,__block對(duì)象(需在block內(nèi)將變量主動(dòng)置空)
對(duì)于 MRC 環(huán)境菜皂,使用 Copy 修飾 Block,會(huì)將棧區(qū)的 Block 拷貝到堆區(qū)佛寿。
對(duì)于 ARC 環(huán)境幌墓,使用 Strong、Copy 修飾 Block冀泻,都會(huì)將棧區(qū)的 Block 拷貝到堆區(qū)。
所以蜡饵,Block 不是一定要用 Copy 來(lái)修飾的弹渔,在 ARC 環(huán)境下面 Strong 和 Copy 修飾效果是一樣的。
補(bǔ)充:一個(gè)block要使用self溯祸,會(huì)處理成在外部聲明一個(gè)weak變量指向self肢专,然而為何有時(shí)會(huì)出現(xiàn)在block里又聲明一個(gè)strong變量指向weakSelf?
原因:block會(huì)把寫(xiě)在block里的變量copy一份焦辅,如果直接在block里使用self博杖,(self對(duì)變量默認(rèn)是強(qiáng)引用)self對(duì)block持有,block對(duì)self持有筷登,導(dǎo)致循環(huán)引用剃根,所以這里需要聲明一個(gè)弱引用weakSelf,讓block引用weakSelf前方,打破循環(huán)引用狈醉。
而這樣會(huì)導(dǎo)致另外一個(gè)問(wèn)題廉油,因?yàn)閣eakSelf是對(duì)self的弱引用,如果這個(gè)時(shí)候控制器pop或者其他的方式引用計(jì)數(shù)為0苗傅,就會(huì)釋放抒线,如果這個(gè)block是異步調(diào)用而且調(diào)用的時(shí)候self已經(jīng)釋放了,這個(gè)時(shí)候weakSelf已就變成了nil渣慕。
當(dāng)控制器(也可以是其他的控件)pop回來(lái)之后(或者一些其他的原因?qū)е箩尫牛┧惶浚W(wǎng)絡(luò)請(qǐng)求完成,如果這個(gè)時(shí)候需要控制器做出反映逊桦,需要strongSelf再對(duì)weakSelf強(qiáng)引用一下旱物。
但是,你可能會(huì)疑問(wèn)卫袒,strongSelf對(duì)weakSelf強(qiáng)引用宵呛,weakSelf對(duì)self弱引用,最終不也是對(duì)self進(jìn)行了強(qiáng)引用夕凝,會(huì)導(dǎo)致循環(huán)引用嗎宝穗。不會(huì)的,因?yàn)閟trongSelf是在block里面聲明的一個(gè)指針码秉,當(dāng)block執(zhí)行完畢后逮矛,strongSelf會(huì)釋放,這個(gè)時(shí)候?qū)⒉辉購(gòu)?qiáng)引用weakSelf转砖,所以self會(huì)正確的釋放须鼎。
2.數(shù)組的深拷貝和淺拷貝?
@property (nonatomic, strong) NSArray *array0;
@property (nonatomic, copy) NSArray *array1;
@property (nonatomic, strong) NSMutableArray *array2;
@property (nonatomic, copy) NSMutableArray *array3;
第一種寫(xiě)法不推薦使用府蔗,是對(duì)傳遞對(duì)象的強(qiáng)引用晋控,不管是傳遞 NSArray 還是 NSMutableArray 對(duì)象都是多了一個(gè)強(qiáng)引用的指針而已。當(dāng)外面?zhèn)鬟f的是 NSMutableArray 對(duì)象姓赤,在該類中使用該屬性時(shí)就要注意外面也可能隨時(shí)修改該對(duì)象赡译。
第二種寫(xiě)法為推薦寫(xiě)法,如果傳遞的是 NSArray 對(duì)象不铆,則只是對(duì)原先對(duì)象的一份強(qiáng)引用(應(yīng)該是編譯器優(yōu)化的)蝌焚,但是如果傳遞的是 NSMutableArray 對(duì)象,則是對(duì)原先對(duì)象的一次“單層深拷貝”誓斥,生成的 NSArray 對(duì)象是一份新內(nèi)存地址的對(duì)象只洒,但是其中的元素還是原先的。
第三種寫(xiě)法為推薦寫(xiě)法劳坑,是對(duì)傳遞 NSMutableArray 對(duì)象的一個(gè)強(qiáng)引用毕谴。該類中使用該屬性時(shí)要注意外面也可能隨時(shí)修改該對(duì)象。
第四種寫(xiě)法為錯(cuò)誤寫(xiě)法,是對(duì)傳遞 NSMutableArray 對(duì)象的一個(gè)“單層深拷貝”析珊,而且生成的對(duì)象是 NSArray 類型而不是 NSMutableArray 類型羡鸥,在該類中對(duì)該屬性做增刪操作就會(huì)出現(xiàn)unrecognized method send to … 引發(fā)crash。
NSString 與 NSMutableString 和上面的結(jié)論是一樣的忠寻,只是沒(méi)有單層深拷貝的概念惧浴。
3. 淺拷貝、單層深拷貝奕剃、深拷貝
淺拷貝
所謂的淺拷貝衷旅,就是指只是將對(duì)象內(nèi)存地址多了一個(gè)引用,也就是說(shuō)纵朋,拷貝結(jié)束之后柿顶,兩個(gè)對(duì)象的值不僅相同,而且對(duì)象所指的內(nèi)存地址都是一樣的操软。
單層深拷貝
對(duì)于不可變的容器類對(duì)象(如NSArray嘁锯、NSSet、NSDictionary)進(jìn) mutableCopy 操作聂薪,內(nèi)存地址發(fā)生了變化家乘,但是其中的元素內(nèi)存地址并沒(méi)有發(fā)生變化,屬于單層深拷貝藏澳。
對(duì)于可變集合類對(duì)象(如NSMutableArray仁锯、NSMutableSet、NSMutableDictionary)翔悠,不管是進(jìn)行 copy 操作還是 mutableCopy 操作业崖,其內(nèi)存地址都發(fā)生了變化,但是其中的元素內(nèi)存地址都沒(méi)有發(fā)生變化蓄愁,屬于單層深拷貝双炕。
深拷貝
所謂深拷貝,就是指拷貝一個(gè)對(duì)象的具體內(nèi)容涝登,拷貝結(jié)束之后雄家,兩個(gè)對(duì)象的值雖然是相同的,但是指向的內(nèi)存地址是不同的胀滚。兩個(gè)對(duì)象之間也互不影響,互不干擾乱投。
對(duì) NSArray 進(jìn)行 copy 操作的時(shí)候咽笼,數(shù)組的內(nèi)存地址沒(méi)有發(fā)生變化,但是進(jìn)行 mutableCopy 操作時(shí)戚炫,其內(nèi)存地址發(fā)生了變化剑刑,結(jié)論跟非集合類的差不多。
但是,這里的深拷貝和非集合類的深拷貝還是不太一樣的施掏,上面我們打印出了數(shù)組的第一個(gè)元素的內(nèi)存地址钮惠,可以發(fā)現(xiàn),進(jìn)行 mutableCopy 操作時(shí)七芭,雖然數(shù)組內(nèi)存地址發(fā)生了變化素挽,但是數(shù)組元素的內(nèi)存地址并沒(méi)有發(fā)生變化。
這個(gè)屬于一個(gè)特例狸驳,我們稱它為單層深復(fù)制预明。并不是理論上的完全深復(fù)制。
對(duì) NSMutableArray 進(jìn)行 copy 和 mutableCopy 操作耙箍,其內(nèi)存地址都發(fā)生了變化撰糠,但是,對(duì)于數(shù)組中的元素辩昆,不管是進(jìn)行的哪種操作阅酪,內(nèi)存地址始終都沒(méi)有發(fā)生變化,所以屬于單層深拷貝汁针。
所以术辐,我們可以得出,對(duì)于不可變的集合類對(duì)象進(jìn)行 copy 操作扇丛,只是改變了指針术吗,其內(nèi)存地址并沒(méi)有發(fā)生變化;進(jìn)行 mutableCopy 操作帆精,內(nèi)存地址發(fā)生了變化较屿,但是其中的元素內(nèi)存地址并沒(méi)有發(fā)生變化。
對(duì)于可變集合類對(duì)象卓练,不管是進(jìn)行 copy 操作還是 mutableCopy 操作隘蝎,其內(nèi)存地址都發(fā)生了變化,但是其中的元素內(nèi)存地址都沒(méi)有發(fā)生變化襟企,屬于單層深拷貝嘱么。
?深拷貝就是內(nèi)容拷貝,淺拷貝就是指針拷貝顽悼。本質(zhì)區(qū)別在于:
是否開(kāi)啟新的內(nèi)存地址
是否影響內(nèi)存地址的引用計(jì)數(shù)
?特別注意的是:對(duì)于集合類的可變對(duì)象來(lái)說(shuō)曼振,深拷貝并非嚴(yán)格意義上的深復(fù)制,只能算是單層深復(fù)制蔚龙,即雖然新開(kāi)辟了內(nèi)存地址冰评,但是存放在內(nèi)存上的值(也就是數(shù)組里的元素仍然之鄉(xiāng)員數(shù)組元素值,并沒(méi)有另外復(fù)制一份)木羹,這就叫做單層深復(fù)制甲雅。
?No1:可變對(duì)象的copy和mutableCopy方法都是深拷貝(區(qū)別完全深拷貝與單層深拷貝) 解孙。
?No2:不可變對(duì)象的copy方法是淺拷貝,mutableCopy方法是深拷貝抛人。
?No3:copy方法返回的對(duì)象都是不可變對(duì)象弛姜。
在修改原值之前,marry1妖枚、marry2廷臼、marr3 地址都不一樣,很明顯copy和mutableCopy都是深拷貝盅惜,但是從修改原值后的打印結(jié)果來(lái)看中剩,這里的深拷貝只是單層深拷貝:新開(kāi)辟了內(nèi)存地址,但是數(shù)組中的值還是指向原數(shù)組的抒寂,這樣才能在修改原值后结啼,marry2 marr3中的值都修改了。另外屈芜,從打印的數(shù)組元素地址可以很明顯的看出來(lái)郊愧,修改前后marry1、marry井佑、marr3的數(shù)組元素地址都是一模一樣的属铁,更加佐證了這一點(diǎn)。
但是修改數(shù)組的元素的個(gè)數(shù)躬翁,只有當(dāng)前數(shù)組的個(gè)數(shù)會(huì)改變焦蘑,數(shù)組之間不會(huì)互相影響,修改元素的值會(huì)互相影響 盒发,所有的數(shù)組的元素的值都會(huì)改變
[mstr1 appendFormat:@"aaa"];這樣修改元素的值例嘱,所有數(shù)組的元素都會(huì)修改。
[marry3 replaceObjectAtIndex:0 withObject:@"value1---"];這樣修改元素的值宁舰,只有當(dāng)前數(shù)組的元素會(huì)修改拼卵。
3.隱式動(dòng)畫(huà)和顯式動(dòng)畫(huà)的區(qū)別?
顯式動(dòng)畫(huà)是指用戶自己通過(guò)beginAnimations:context:和commitAnimations創(chuàng)建的動(dòng)畫(huà)。
隱式動(dòng)畫(huà)是指通過(guò)UIView的animateWithDuration:animations:方法創(chuàng)建的動(dòng)畫(huà)蛮艰。
動(dòng)畫(huà)事務(wù)--CATransaction
隱式動(dòng)畫(huà)一直存在 如需關(guān)閉需設(shè)置腋腮;顯式動(dòng)畫(huà)是不存在,如需顯式 要開(kāi)啟(創(chuàng)建)壤蚜。
顯式動(dòng)畫(huà)是指用戶自己通過(guò)beginAnimations:context:和commitAnimations創(chuàng)建的動(dòng)畫(huà)即寡。隱式動(dòng)畫(huà)是指通過(guò)UIView的animateWithDuration:animations:方法創(chuàng)建的動(dòng)畫(huà)。
隱式動(dòng)畫(huà)是系統(tǒng)框架自動(dòng)完成的袜刷。Core Animation在每個(gè)runloop周期中自動(dòng)開(kāi)始一次新的事務(wù)嘿悬,即使你不顯式的用[CATransaction begin]開(kāi)始一次事務(wù),任何在一次runloop循環(huán)中屬性的改變都會(huì)被集中起來(lái)水泉,然后做一次0.25秒的動(dòng)畫(huà)。在iOS4中,蘋(píng)果對(duì)UIView添加了一種基于block的動(dòng)畫(huà)方法:+animateWithDuration:animations:草则。這樣寫(xiě)對(duì)做一堆的屬性動(dòng)畫(huà)在語(yǔ)法上會(huì)更加簡(jiǎn)單钢拧,但實(shí)質(zhì)上它們都是在做同樣的事情。CATransaction的+begin和+commit方法在+animateWithDuration:animations:內(nèi)部自動(dòng)調(diào)用炕横,這樣block中所有屬性的改變都會(huì)被事務(wù)所包含
4.給對(duì)象賦值nil是做了什么操作源内?
nil在字典,數(shù)組中有特殊含義–元素結(jié)束標(biāo)記
5.自動(dòng)釋放池的相關(guān)知識(shí)份殿?
每一個(gè)自動(dòng)釋放池都是由一系列的?AutoreleasePoolPage?組成的膜钓,并且每一個(gè)?AutoreleasePoolPage?的大小都是?4096?字節(jié)(16 進(jìn)制 0x1000)自動(dòng)釋放池中的?AutoreleasePoolPage?是以雙向鏈表的形式連接起來(lái)的:
autorelease 方法
NSAutoreleasePool*pool = [[NSAutoreleasePoolalloc]init ];//創(chuàng)建一個(gè)自動(dòng)釋放池
Person *person = [[Person alloc]init];
//調(diào)autorelease方法將對(duì)象加入到自動(dòng)釋放池//注意使用該方法,對(duì)象不會(huì)自己加入到自動(dòng)釋放池卿嘲,需要人為調(diào)用autorelease方法加入
[person autorelease];
//,手動(dòng)釋放自動(dòng)釋放池執(zhí)行完這行代碼是颂斜,自動(dòng)釋放池會(huì)對(duì)加入他中的對(duì)象做一次release操作
[pool release];
自動(dòng)釋放池銷毀時(shí)機(jī):[pool release]代碼執(zhí)行完后
每一個(gè)自動(dòng)釋放池沒(méi)有單獨(dú)的結(jié)構(gòu),每一個(gè)autorealeasePool對(duì)象都是由若干個(gè)個(gè)autoreleasePoolPage通過(guò)雙向鏈表連接而成拾枣,當(dāng)一個(gè)對(duì)象調(diào)用了autorelease方法沃疮,這個(gè)對(duì)象就會(huì)被加入到當(dāng)前自動(dòng)釋放池的最新的autoreleasePoolPage中,關(guān)于autoreleasePoolPage/梅肤,請(qǐng)看下面
當(dāng)我們向自動(dòng)釋放池 pool 發(fā)送 release 消息司蔬,將會(huì)向池中臨時(shí)對(duì)象發(fā)送一條 release 消息,并且自身也會(huì)被銷毀姨蝴。
一俊啼、autorelease 對(duì)象會(huì)在什么時(shí)候釋放?
分兩種情況:
使用?@autoreleasepool左医,會(huì)在大括號(hào)結(jié)束時(shí)釋放
不使用?@autoreleasepool授帕,這個(gè)會(huì)由系統(tǒng)自動(dòng)釋放,釋放時(shí)機(jī)是在當(dāng)前?runloop?結(jié)束時(shí)釋放炒辉,因?yàn)橄到y(tǒng)會(huì)自動(dòng)為每個(gè)?runloop?執(zhí)行自動(dòng)釋放池的?push?和?pop?操作
Autorelease對(duì)象什么時(shí)候釋放豪墅?
這個(gè)問(wèn)題拿來(lái)做面試題,問(wèn)過(guò)很多人黔寇,沒(méi)有幾個(gè)能答對(duì)的偶器。很多答案都是“當(dāng)前作用域大括號(hào)結(jié)束時(shí)釋放”,顯然木有正確理解Autorelease機(jī)制缝裤。
在沒(méi)有手加Autorelease Pool的情況下屏轰,Autorelease對(duì)象是在當(dāng)前的runloop迭代結(jié)束時(shí)釋放的,而它能夠釋放的原因是系統(tǒng)在每個(gè)runloop迭代中都加入了自動(dòng)釋放池Push和Pop
ARC下憋飞,我們使用@autoreleasepool{}來(lái)使用一個(gè)AutoreleasePool霎苗,隨后編譯器將其改寫(xiě)成下面的樣子:
void *context = objc_autoreleasePoolPush();
// {}中的代碼
objc_autoreleasePoolPop(context);
而這兩個(gè)函數(shù)都是對(duì)AutoreleasePoolPage的簡(jiǎn)單封裝,所以自動(dòng)釋放機(jī)制的核心就在于這個(gè)類榛做。