iOS中的僵尸对象的实现

僵尸对象对于我们调试程序来说很有用,在XCode中打开僵尸对象的方法是设置NSZombieEnabled环境变量为YES,这导致所有的对象都不会被释放,程序跑起来会时间长了内存占用量很大,这次我们就要来写一些代码,模仿XCode中的实现,这样我们也能够大体上了解了XCode中实现僵尸对象的原理了。


Mike Ash在他的博客中已经解释了僵尸对象实现的细节,我在这里就算是翻译一下吧...  (这些链接可能需要翻墙...)


OC中的对象都是结构体,结构体中第一个字段是一个isa,指向对象的类对象,类对象也是一个对象,也有一个isa指针,指向了Meta类对象,这里就不细说了,具体看CocoaWithLove中的解释。


在一个普通对象的retainCount变成0的时候,会调用dealloc,我们的代码要勾住dealloc,然后在这里做以下操作:

1、创建一个新的僵尸类;

2、将此对象的isa指向僵尸类(这个对象就变成了僵尸类的对象了)

这样,将来所有发向这个对象的消息现在都会去僵尸类中找实现的方法,正如上面提到的,僵尸类中没有自己的方法,所以会forwardInvocate:(NSInvocation*),在这之前,系统还会根据消息的selector,调用methodSignatureForSelector:方法生成NSInvocation对象,所以第一时间发现给僵尸对象的消息的时机是在methodSignatureForSelector:中。


从上面的讨论中,可以得到僵尸类的实现需要满足以下细节:

1、不可以像实现其他类一样继承自NSObject,否则我们就继承了很多NSObject中的方法,也就不能够在methodSignatureForSelector:中截获这个消息;

2、需要实现methodSignatureForSelector:方法,在这里打印出相关的信息;

3、需要实现+initialize方法,这个方法是所有类被发送第一个方法前会调用的一个方法,如果我们的僵尸类没有实现这个方法,那么就会forwardInvocate:了。


现在有一个问题,虽然我们可以在methodSignatureForSelector:中截获这个消息,但是这个对象的isa指针已经指向了僵尸类,我们怎样得到原始类的名字呢?有一个巧妙的办法就是创建的这个新的僵尸类的名字要用一下规则命名:NSZombie+原始类的名字,在methodSignatureForSelector:中就可以将前缀NSZombie前缀去掉,得到原始类名了。


Mike Ash还有一篇文章是研究XCode中是如何实现僵尸对象的。


首先我先给出一段代码

@implementation NSZombie

- (id)init
{
    self = [super init] ;
    if (self) {
        NSIndexSet *obj = [[NSIndexSet alloc] init];
        [NSZombie dump:obj] ;
        [obj release];
        [NSZombie dump:obj] ;
    }
    return self ;
}

+ (void)dump:(id)obj
{
    size_t size = malloc_size(obj) ;
    NSLog(@"size:%zu, className:%s", size, object_getClassName(obj)) ;
}

@end


没有开启NSZombieEnabled的情况下,我们的到的是,虽然对象已经被释放,但是这块内存并没有被复写,所以还是能够通过isa指针找对应类的信息的。

size:16, className:NSIndexSet

size:0, className:NSIndexSet


开启了NSZombieEnabled之后,得到了,可以看到对象引用计数变成0之后,对象对应的类已经被改变,变成了一个僵尸对象。

size:16, className:NSIndexSet

size:16, className:_NSZombie_NSIndexSet


整理的代码 in github:Zombie

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。