构建自己的NSZombie

当开启 xcode zombie 选项,发送消息到一个被  "释放了的对象"  时

    ObjZomies *oz = [[ObjZomies  alloc] init];
    oz.name = @"obz";

    NSLog(@"ObjZomies :----%@---%s---%p",[ObjZomies class],object_getClassName(oz),oz);
    [oz release];

    NSLog(@"ObjZomies :-------%@----%s----%p",[ObjZomies class],object_getClassName(oz),oz);

    oz.name = @"z----o";

打印结果:

2015-10-27 17:32:43.303 Zombies[68811:1009530] ObjZomies :----ObjZomies---ObjZomies---0x7fcb71f047c0
2015-10-27 17:32:43.304 Zombies[68811:1009530] ObjZomies :-------ObjZomies----_NSZombie_ObjZomies----0x7fcb71f047c0
2015-10-27 17:32:43.304 Zombies[68811:1009530] *** -[ObjZomies setName:]: message sent to deallocated instance 0x7fcb71f047c0

很神奇....   分析log 可以得出以下信息:

1,我们向一个 "已经释放了的对象" ObjZomies 发送了 setName: 方法。

2,在oz 被release 前后  object_getClassName(oz) 打印的结果不同,即oz 的isa 指向的class 变啦 0.0

ObjZomies 和 _NSZombie_ObjZomies  

3,有没有注意到 那三个%p  都是一样的  0x7fcb71f047c0, 说明oz 这货release 后并没有别释放。

所以 "已经释放了的对象"  并没有释放  而是变成了 "僵尸",在程序运行期间一直在内存驻留。 当然在启用xcode zombie 选项后,所有类都不能 "幸免"  变成  "僵尸"

_NSZombie_ObjZomies  当然是运行时添加进去的类,像KVO 一样,根据目标类假如是A  那么运行时就会生成一个特殊功能的类 xxx_A  来行使某些功能。

_NSZombie_ 前缀 + ObjZomies  就构成了运行时创建的  用于捕捉 "僵尸" 的功能类。

 

_NSZombie_MyClass 也就是 捕捉僵尸类 ,

作用是   捕捉 给一个    "已经释放的对象"   发消息时 能够触发异常的机制,并提醒开发者具体是发到了哪个已经被释放的类,以及 哪个消息。

这依赖于oc 强大的动态性,以及消息传递机制。

简单概括原理:

当一个类A  的实例a 的引用计数为0 时,调用delloc 进行内存清理工作,当然它的基类是NSobject  最终会调到 NSObject 的dealloc 中。这时候如果让它走完的话,a 也就释放啦。我们也永远不知道它是谁。

所以不能让a 释放,让a 成为一个"僵尸" 也就是a 的内存永远驻留在程序运行时,创建一个 捕捉僵尸的类 B,在消息发相a 时转发到B 中抛出异常,并提示相关信息。 即 a--isa-->B  把a的isa指针指向B ,(这里不理解的需要好好做做功课啦)那么a 就会用isa 到B 中去找对应实现。

实现如下:

注意main.m 环境为非ARC,并开启xcode zombie 相关选项

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
#import "ObjZomies.h"

void EmpthIMP(id obj,SEL _cmd){}

NSMethodSignature * ZombieMethodSignatureForSelector(id obj,SEL _cmd,SEL selector){
    Class class = object_getClass(obj);
    NSString *className = NSStringFromClass(class);
    className = [className substringFromIndex:[@"CMZombie_" length]];

    NSLog(@"Selector %@ sent to deallocated instance %p of class %@", NSStringFromSelector(selector), obj, className);
    abort();

    return nil;
}

Class ZombifyClass(Class class){

    NSString * className = NSStringFromClass(class);
    NSString *zombieClassName = [@"CMZombie_" stringByAppendingString:className];

    Class zombieClass = NSClassFromString(zombieClassName);
    if (zombieClass) {
        return zombieClass;
    }
    //构建自己的NSZombie
    zombieClass = objc_allocateClassPair(nil, [zombieClassName UTF8String], 0);
    class_addMethod(zombieClass, @selector(methodSignatureForSelector:), (IMP)ZombieMethodSignatureForSelector, "@@::");    //这里很容易理解,+initialize 存在于MetaClass 中,实例方法则存在于Class 中。
    class_addMethod(object_getClass(zombieClass), @selector(initialize), (IMP)EmpthIMP, "[email protected]:");
    objc_registerClassPair(zombieClass);

    return zombieClass;
}

void ZombieDealloc(id obj,SEL _cmd){
    Class c = ZombifyClass(object_getClass(obj));    // 把obj 的isa 指向 我们自定义的 "捕捉僵尸类" _NSZombie_MyClass
    object_setClass(obj, c);
}

void EnableZombies(void){
    Method m = class_getInstanceMethod([NSObject class],@selector(dealloc));
    method_setImplementation(m , (IMP)ZombieDealloc);
}

int main(int argc, char * argv[]) {
    //启用
    EnableZombies();

    ObjZomies *oz = [[ObjZomies  alloc] init];

    oz.name = @"obz";

    NSLog(@"ObjZomies :----%@---%s---%p",[ObjZomies class],object_getClassName(oz),oz);
    [oz release];

    NSLog(@"ObjZomies :-------%@----%s----%p",[ObjZomies class],object_getClassName(oz),oz);

    oz.name = @"z----o";

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
#import <Foundation/Foundation.h>

@interface ObjZomies : NSObject
@property (nonatomic,copy) NSString *name;

@end

#import "ObjZomies.h"

@implementation ObjZomies
@end

再次运行:

2015-10-27 17:55:55.433 Zombies[68883:1030347] ObjZomies :----ObjZomies---ObjZomies---0x7f825ae00790
2015-10-27 17:55:55.435 Zombies[68883:1030347] ObjZomies :-------ObjZomies----CMZombie_ObjZomies----0x7f825ae00790
2015-10-27 17:55:55.435 Zombies[68883:1030347] Selector setName: sent to deallocated instance 0x7f825ae00790 of class ObjZomies

so cool 。

参考:https://www.mikeash.com/pyblog/friday-qa-2014-11-07-lets-build-nszombie.html
http://www.cocoachina.com/ios/20141204/10526.html

时间: 2024-07-29 05:09:32

构建自己的NSZombie的相关文章

Android程序能够构建和运行,但是报以下报错,为什么?

安卓程序写完之后能够构建和运行,但是会报以下的错误.不知道原因为何?求大神解答. 网上说的是混淆编译的原因,不过程序没有开启混淆编译. Error:warning: Ignoring InnerClasses attribute for an anonymous inner class Error:(com.alipay.android.phone.mrpc.core.c) that doesn't come with an Error:associated EnclosingMethod at

如何构建安全的网络连接机制

随着计算机网络与信息化的不断发展,DT时代数据资源的多样性.庞大性.分布广泛性,导致信息安全问题日趋复杂,计算机网络的开放框架所带来的威胁层出不穷.面对严峻的网络安全形势,传统的信息安全系统从架构和强度上已经难有大的突破.人们在信息安全的实践中逐渐认识到,大多数安全隐患来自于终端,如何解决这项问题,成为了各网络大咖们需要攻克的又一课题. 勤智数码产品方案部-秦杨凯给出了这个课题的解决方案-- 通过构建对等规格的网络安全协议和的信息资源管理体系的分布式网络,可快速提高数据资源自由流通.往来无碍.安

认知,构建个人的知识体系(上)

1.前言 本文将聊聊我对构建个人知识体系的一些想法,主要是为了提升自我认知.从个人经历开始,谈谈对知识的划分,也就是一个是什么,为什么的过程. 2.缘起 把时间回到一年前,那时候我工作快一年了,得益于前面的一些努力,工作比较顺利.特别是技术上,没有遇到太多过无法解决的问题.同时也开始迷茫,工作难道就是这个轻松的样子?三五年之后那不是很无趣,该怎么办? 想找到这个问题的答案,而最好的方式莫过于,亲自去了解那些三五年工作经验的人是怎么的样子. 因此从那时候起,关注了不少来公司面试的人的简历,也有过几

Busybox构建根文件系统和制作Ramdisk

定制根文件系统的方法很多,最常用的是使用BusyBox来构建定制根文件系统.它集成压缩了Linux的许多工具和命令,可以使用户迅速方便地建立一套相对完整.功能丰富的文件系统,其中包括大量常用的应用程序.下面详细介绍有关Busybox定制根文件系统. 一.系统环境: 1.操作系统:Ubuntu140.4 2.交叉编译工具:gcc version 6.1.1 20160711 (Linaro GCC 6.1-2016.08) 3.busybox源码包:busybox-1.26.2 二.构建rootf

构建器模式

*构建器模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.类图如下: *product产品类:表示被构建器构建的复杂对象,包含多个部件. 1 //产品接口 示意性产品,所以没有任何方法 2 public interface Product{ 3 } *builder构建器接口:定义创建一个product对象所需要的各个部件的操作. 1 //生成器接口 2 public interface Builder{ 3 public void builderPart1();

构建之法——读书笔记(8)

<构建之法>第十&十一章 主要讲述了在软件设计前期的需求分析问题上的方法和实践经验,分为"典型用户和场景"以及"软件设计与实现". 其中第十章大部分内容包含: 用户的分类(典型用户可以包括以下内容: 1. 名字(越自然越好) 2. 年龄(不同年龄和收入的用户有不同的需求) 3. 收入 4. 代表的用户在市场上的比例和重要性(比例大不等同于重要性高,如付费的用户比例较少,但是影响大,所以更重要 5. 使用这个软件的典型场景 6. 使用本软件/服务的

《构建之法》第二次

第二章讲的是个人技术和流程.绝大多数软件是由多人合作完成的.单元测试能够让自己负责的模块功能定义更加明确,模块内部的改变不会影响其他模块,而且模块的质量能得到稳定的.量化的保证. 创建一个单元测试函数的主要步骤是: 1.设置数据 2.使用被测试类型的功能 3.比较实际结果和预期的结果 在写技术模块规格说明书的时候,要越详细越好,最好各项要求都可以表示为一个单元测试用例.单元测试也并不是件容易的事,单元测试应该准确.快速地保证程序基本模块的正确性.首先单元测试应该在最基本的功能/参数上验证程序的正

SQL Server虚拟化系列(3)&mdash;&mdash;构建理想的基于VMware的SQL Server虚拟机

虚拟化变得越来越常见,并且在不了解虚拟化如何工作的情况下,DBA在尝试解决性能问题时会出现盲点,例如减少资源争用或改进备份和恢复操作等. 在本文中我们将主要讲述为您的SQL Server工作负载构建理想的基于VMware的虚拟机.我们的下一篇文章将介绍怎么样在Hyper-V上构建对应的SQL Server虚拟化环境. 现在,作为DBA,您可能没有访问权限来创建用于SQL Server的新虚拟机.这些操作可以交给您的VM管理员,他们将为您部署合适的VM环境. 以下详细信息适用于在Windows S

构建CTC语音识别解码网络

本文介绍 kaldi-ctc 构建 CTC[1, 2, 3, 4] 语音识别加权有限状态机(WFST)解码网络的方式. 示例相关资源 lifeiteng/codingmath/CTC-decoding-graph 构建语言模型 以 单句 "how are you are" 作为文本语料,训练 bi-gram(order=2)语言模型 生成 G.fst [data/lang_2/G.pdf],如下图 准备"发音" 词典 不同单元 phone[1, 2] / chara