当开启 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";

打印结果:

-- ::43.303 Zombies[:] ObjZomies :----ObjZomies---ObjZomies---0x7fcb71f047c0
-- ::43.304 Zombies[:] ObjZomies :-------ObjZomies----_NSZombie_ObjZomies----0x7fcb71f047c0
-- ::43.304 Zombies[:] *** -[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], );
class_addMethod(zombieClass, @selector(methodSignatureForSelector:), (IMP)ZombieMethodSignatureForSelector, "@@::");
//这里很容易理解,+initialize 存在于MetaClass 中,实例方法则存在于Class 中。
class_addMethod(object_getClass(zombieClass), @selector(initialize), (IMP)EmpthIMP, "v@:");
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

再次运行:

-- ::55.433 Zombies[:] ObjZomies :----ObjZomies---ObjZomies---0x7f825ae00790
-- ::55.435 Zombies[:] ObjZomies :-------ObjZomies----CMZombie_ObjZomies----0x7f825ae00790
-- ::55.435 Zombies[:] 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

构建自己的NSZombie的更多相关文章

  1. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  2. 快速构建H5单页面切换骨架

    在Web App和Hybrid App横行的时代,为了拥有更好的用户体验,单页面应用顺势而生,单页面应用简称`SPA`,即Single Page Application,就是只有一个HTML页面的应用 ...

  3. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  4. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  5. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  6. .Net中的AOP系列之构建一个汽车租赁应用

    返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...

  7. 使用ServiceStack构建Web服务

    提到构建WebService服务,大家肯定第一个想到的是使用WCF,因为简单快捷嘛.首先要说明的是,本人对WCF不太了解,但是想快速建立一个WebService,于是看到了MSDN上的这一篇文章 Bu ...

  8. Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境

    首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...

  9. 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&引用jquery来实现alert

    使用webstorm+webpack构建简单入门级"HelloWorld"的应用&&构建使用jquery来实现 1.首先你自己把webstorm安装完成. 请参考这 ...

随机推荐

  1. ios php RSA 非对称加密解密 der 和pem生成

    ios 使用public_key.der加密 php 使用 private_key.pem解密 openssl req -x509 -out public_key.der -outform der - ...

  2. C# 基本算法

    1.冒泡排序 排序 ,,,,,, }; ; i < ArrayList.Count(); i++) { for (int j = i; j < ArrayList.Count(); j++ ...

  3. 无向图最小生成树(prim算法)

    普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小.该算法于1930年由捷 ...

  4. Python缩进

    今天练习代码的时候发现一个问题,练习类,我在notepad++上写的代码运行后,复制到pycharm上运行然后报错,看代码 #---coding:utf-8--- #定义一个Person类然后实例化 ...

  5. SpringMVC中使用DWR

    SpringMVC中使用DWR重点在其配置当中. 1.  web.xml文件的配置 在DispatcherServlet中增加dwr的拦截来取代DwrServlet. 更改配置如下: <serv ...

  6. WPF RichTextBox,关键字搜索,样式改变,超链接替换,图文混排

    RichTextBox 只是一个控件,表示对 FlowDocument 对象执行操作的丰富编辑控件.它所承载的内容由其 Document 属性来呈现. Document 是一个 FlowDocumen ...

  7. oracle dblink调用函数

    select  用户名.函数名@DBLINK名称(参数) from dual; e.g. select newbosid@TEST('1234ECMA') from dual; -- 成功执行 sel ...

  8. 软件工程结组开发软件特色——NABC模型

    特点:通过学生提前点餐,可以让摊主在准备食材的时候有个参照,当准备的食材比较少的时候可以及时回家取来. N(Need):每当放学的时候,学校外边的卖饭摊位总是挤满了人,好多同学都要排好长的队等比较长的 ...

  9. 制作DIP Package及DIP焊盘制作,不规则焊盘制作

    DIP的焊盘制作: 1.启动Pad Designer. 2.New一个焊盘,取一个名字.圆形通孔长方形pad4_0r2_3cir1_5md,圆形通孔正方形pad4_0sq2_4md,圆形通孔圆形pad ...

  10. iOS图片加载新框架 - FlyImage

    FlyImage 整合了SDWebImage,FastImageCache,AFNetworking的优点,是一个新的性能高效.接口简单的图片加载框架. 特点 高效 可将多张小图解码后存储到同一张大图 ...