《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》--第五章 第35條
(ps:此乃讀書筆記漂问,加深記憶,僅供大家參考)
第35條 用“僵尸對(duì)象”調(diào)試內(nèi)存管理問題
大家都知道,向業(yè)已回收的對(duì)象發(fā)送消息是不安全的。這么做有時(shí)可以捉撮,有時(shí)不行。具體可行與否妇垢,完全取決于對(duì)象所占內(nèi)存有沒有為其他內(nèi)容所覆寫巾遭。
所幸Cocoa提供了“僵尸對(duì)象”(Zoombie Object)這個(gè)非常方便的功能肉康。啟用這項(xiàng)調(diào)試功能之后,運(yùn)行期系統(tǒng)會(huì)把所有已回收的實(shí)例轉(zhuǎn)化成特殊的“僵尸對(duì)象”灼舍,而不會(huì)真正回收它們迎罗。這種對(duì)象所在的核心內(nèi)存無法重用,因此不可能遭到覆寫片仿。僵尸對(duì)象收到消息后纹安,會(huì)拋出異常,其中準(zhǔn)確說明了發(fā)送過來的消息砂豌,并描述了回收之前的那個(gè)對(duì)象厢岂。僵尸對(duì)象是調(diào)試內(nèi)存管理問題的最佳方式。
僵尸對(duì)象的工作原理是什么阳距?它的實(shí)現(xiàn)代碼深植于Objective-C的運(yùn)行期程序庫塔粒、Foundation框架及CoreFoundation框架中。系統(tǒng)在即將回收對(duì)象時(shí)筐摘,如果發(fā)現(xiàn)通過環(huán)境變量啟用了僵尸對(duì)象功能卒茬,那么還將執(zhí)行一個(gè)附加步驟。這一步驟就是把對(duì)象轉(zhuǎn)化為僵尸對(duì)象咖熟,而不徹底回收圃酵。
void PrintClassInfo(id obj){
Class cls = object_getClass(obj);
Class superCls = class_getSuperclass(cls);
NSLog(@"=== %s : %s ===", class_getName(cls), class_getName(superCls));
}
int main(int argc, char *argv[])
{
EOCClass *obj = [[EOCClass alloc] init];
NSLog(@"Before release:");
PrintClassInfo(obj);
[obj release];
NSLog(@"After release:");
PrintClassInfo(obj);
}
范例代碼將輸出下面這種消息:
Before release:
=== EOCClass : NSObject ===
After release:
=== _NSZombie_EOCClaxx : nil ===
_NSZombie_EOCClaxx實(shí)際上是在運(yùn)行期生成的,當(dāng)首次碰到EOCClass類的對(duì)象要變成僵尸對(duì)象時(shí)馍管,就會(huì)創(chuàng)建這么一個(gè)類郭赐。創(chuàng)建過程中用到了運(yùn)行期程序庫里的函數(shù),他們的功能很強(qiáng)大确沸,可以操作類列表(class list)捌锭。
僵尸類(zoombie class)是從名為NSZombie的模板類里復(fù)制出來的。這些僵尸類沒有多少事情可做罗捎,只是充當(dāng)一個(gè)標(biāo)記观谦。
僵尸類的作用會(huì)在消息轉(zhuǎn)發(fā)例程(參見12條)中體現(xiàn)出來。NSZombie類(以及所有從該類拷貝出來的類)并未實(shí)現(xiàn)任何方法桨菜。此類沒有超類豁状,因此和NSObject一樣,也是個(gè)“根類”雷激,該類只有一個(gè)實(shí)例變量替蔬,叫做isa,所有NSObjective-C的根類都必須由此變量屎暇。由于這個(gè)輕量級(jí)的類沒有實(shí)現(xiàn)任何方法,所以發(fā)給它的全部消息都要經(jīng)過“完整的消息轉(zhuǎn)發(fā)機(jī)制”(full forwarding mechanism驻粟, 參見第12條)根悼。
在完整的消息轉(zhuǎn)發(fā)機(jī)制中凶异,forwarding是核心,調(diào)試程序時(shí)挤巡,大家可能在検1颍回溯消息里看見過這個(gè)函數(shù)。它首先要做的事情就包括檢查接收消息的對(duì)象所屬的類名矿卑。若名稱前綴為NSZombie喉恋,則表明消息接收者是僵尸對(duì)象,需要特殊處理母廷。此時(shí)會(huì)打印一條消息轻黑,其中指明了僵尸對(duì)象所受到的消息及原來所屬的類,然后應(yīng)用程序就終止了琴昆。
* * * -[CFString respondsToSelector:]: message sent to deallocated instance 0x7ff9e9c080e0
把本節(jié)開頭那個(gè)范例擴(kuò)充一下氓鄙,試著給變成僵尸的EOCClass對(duì)象發(fā)送description消息:
EOCClass *obj = [[EOCClass alloc] init];
NSLog(@"Before release:");
PrintClassInfo(obj);
[obj release];
NSLog(@"After release:");
PrintClassInfo(obj);
[obj description];
若是開啟了僵尸對(duì)象功能,那么控制條會(huì)輸出下列消息:
Before release:
=== EOCClass : NSObject ===
After release:
=== _NSZombie_EOCClass : nil ===
*** -[EOCClass description]: message sent to deallocated instance 0x7fb81bdce9b0
要點(diǎn)
- 系統(tǒng)在回收對(duì)象時(shí)业舍,可以不將其真的回收抖拦,而是把它轉(zhuǎn)化為僵尸對(duì)象。通過環(huán)境變量NSZombieEnable可開啟此功能
- 系統(tǒng)會(huì)修改對(duì)象的isa指針舷暮,令其指向特殊的僵尸類态罪,從而使該對(duì)象變?yōu)榻┦瑢?duì)象。僵尸類能夠響應(yīng)所有的選擇子下面,響應(yīng)方式為:打印一條包含消息內(nèi)容及其接受者的消息向臀,然后終止應(yīng)用程序。