刨根问底Objective-C Runtime(2)- Object & Class & Meta Class

Chun Tips

专注iOS开发

刨根问底Objective-C Runtime(2)- Object & Class & Meta Class

上一篇笔记讲述了objc runtime中Self 和 Super的细节,本篇笔记主要是讲述objc runtime中关于Object & Class & Meta Class的细节。

习题内容

下面代码的运行结果是?

@interface Sark : NSObject
@end@implementation Sark
@endint main(int argc, const char * argv[]) {@autoreleasepool {BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];NSLog(@"%d %d %d %d", res1, res2, res3, res4);}return 0;
}

运行结果为:

2014-11-05 14:45:08.474 Test[9412:721945] 1 0 0 0

这里先看几个概念

什么是 id

id 在 objc.h 中定义如下:

/// A pointer to an instance of a class.
typedef struct objc_object *id;

就像注释中所说的这样 id 是指向一个 objc_object 结构体的指针。

id 这个struct的定义本身就带了一个 *, 所以我们在使用其他NSObject类型的实例时需要在前面加上 *, 而使用 id 时却不用。

那么objc_object又是什么呢

objc_object 在 objc.h 中定义如下:

/// Represents an instance of a class.
struct objc_object {Class isa;
};

这个时候我们知道Objective-C中的object在最后会被转换成C的结构体,而在这个struct中有一个 isa 指针,指向它的类别 Class。

那么什么是Class呢

在 objc.h 中定义如下:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

我们可以看到 Class本身指向的也是一个C的struct objc_class

继续看在runtime.h中objc_class定义如下:

struct objc_class {Class isa  OBJC_ISA_AVAILABILITY;#if !__OBJC2__Class super_class                                        OBJC2_UNAVAILABLE;const char *name                                         OBJC2_UNAVAILABLE;long version                                             OBJC2_UNAVAILABLE;long info                                                OBJC2_UNAVAILABLE;long instance_size                                       OBJC2_UNAVAILABLE;struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;struct objc_cache *cache                                 OBJC2_UNAVAILABLE;struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;#endif
} OBJC2_UNAVAILABLE;

该结构体中,isa 指向所属Class, super_class指向父类别。

继续看

下载objc源代码,在 objc-runtime-new.h 中,我们发现 objc_class有如下定义:

struct objc_class : objc_object {// Class ISA;Class superclass;   ......
}

豁然开朗,我们看到在Objective-C的设计哲学中,一切都是对象。Class在设计中本身也是一个对象。而这个Class对象的对应的类,我们叫它 Meta Class。即Class结构体中的 isa 指向的就是它的 Meta Class

Meta Class

根据上面的描述,我们可以把Meta Class理解为 一个Class对象的Class。简单的说:

  • 当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类的方法列表里查找
  • 当我们发送一个消息给一个类时,这条消息会在类的Meta Class的方法列表里查找

而 Meta Class本身也是一个Class,它跟其他Class一样也有自己的 isa 和 super_class 指针。看下图:

  • 每个Class都有一个isa指针指向一个唯一的Meta Class
  • 每一个Meta Class的isa指针都指向最上层的Meta Class(图中的NSObject的Meta Class)
  • 最上层的Meta Class的isa指针指向自己,形成一个回路
  • 每一个Meta Class的super class指针指向它原本Class的 Super Class的Meta Class。但是最上层的Meta Class的 Super Class指向NSObject Class本身
  • 最上层的NSObject Class的super class指向 nil

解惑

为了更加清楚的知道整个函数调用过程,我们使用clang -rewrite-objc main.m重写,可获得如下代码:

 BOOL res1 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isKindOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));BOOL res2 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));BOOL res3 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Sark"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));BOOL res4 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Sark"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));
先看前两个调用:
  • 最外层是 objc_msgSend函数,转发消息。
  • 函数第一个参数是 (id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))
  • 函数第二个参数是转发的selector
  • 函数第三个参数是 ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))

我们注意到第一个参数和第三个参数对应重写的是[NSObject class],即使用objc_msgSend向 NSObject Class 发送 @selector(class) 这个消息

打开objc源代码,在 Object.mm 中发现+ (Class)class实现如下:

+ (Class)class {return self;
}

所以即返回Class类的对象本身。看如下输出:

NSLog(@"%p", [NSObject class]);
NSLog(@"%p", [NSObject class]);2014-11-05 18:48:30.939 Test[11682:865988] 0x7fff768d40f0
2014-11-05 18:48:30.940 Test[11682:865988] 0x7fff768d40f0

继续打开objc源代码,在 Object.mm 中,我们发现 isKindOfClass的实现如下:

- (BOOL)isKindOf:aClass
{Class cls;for (cls = isa; cls; cls = cls->superclass) if (cls == (Class)aClass)return YES;return NO;
}

对着上面Meta Class的图和实现,我们可以看出

  • 当 NSObject Class对象第一次进行比较时,得到它的isa为 NSObject的Meta Class, 这个时候 NSObject Meta Class 和 NSObject Class不相等。

  (这里应为是类函数的调用所以isa是元类, 如果是实例对象isa就不是元类而是类对象)

  • 然后取NSObject 的Meta Class 的Super class,这个时候又变成了 NSObject Class, 所以返回相等

所以上述第一个输出结果是 YES 。

我们在看下 ‘isMemberOfClass’的实现:

- (BOOL)isMemberOf:aClass
{return isa == (Class)aClass;
}

综上所述,当前的 isa 指向 NSObject 的 Meta Class, 所以和 NSObject Class不相等。

所以上述第二个输出结果为 NO 。

继续看后面两个调用:
  • Sark Class 的isa指向的是 Sark的Meta Class,和Sark Class不相等
  • Sark Meta Class的super class 指向的是 NSObject Meta Class, 和 Sark Class不相等
  • NSObject Meta Class的 super class 指向 NSObject Class,和 Sark Class 不相等
  • NSObject Class 的super class 指向 nil, 和 Sark Class不相等

所以后面两个调用的结果都输出为 NO 。

下一篇博客的主要分享的内容是关于 Objective C Runtime中 消息和Category 的学习笔记。


  • 本文是关于Objective C Runtime的学习笔记。有不对的地方,欢迎大家指正。
  • 感谢@唐巧_boy和@sunnyxx分享题目。
  • 本文由@Chun发表于Chun Tips .
  • 版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0

Chun Tips正在使用多说

Copyright © 2015 - Chun Ye - Powered by Octopress

posted on 2015-06-26 16:29 城之内 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/HypeCheng/p/4602603.html

刨根问底Objective-C Runtime(2)- Object Class Meta Class相关推荐

  1. 刨根问底Objective-C Runtime

    该笔记分为四篇: 刨根问底Objective-C Runtime(1)- Self & Super 刨根问底Objective-C Runtime(2)- Object & Class ...

  2. 刨根问底Objective-C Runtime(1)- Self Super

    转:http://chun.tips/blog/2014/11/05/bao-gen-wen-di-objective%5Bnil%5Dc-runtime(1)%5Bnil%5D-self-and-s ...

  3. Install fail! Error: [@@babel/runtime/core-js/object/keys]

    1.Install fail! Error: [@@babel/runtime/core-js/object/keys] "@babel/runtime": "7.0.0 ...

  4. IOS-RunTime(刨根问底)

    方法调用 让我们看一下方法调用在运行时的过程(参照前文类在runtime中的表示) 如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作. 如果调用的是类方法,就会到类对象的 ...

  5. iOS runtime 学习分享

    这是团队小伙伴在内部的一次技术分享, 很开心, 我们团队越来越好了. iOS runtime 学习分享 Author:Liao Zusheng 申明: 部分资料来自于知名论坛和博客,已在文中给出相关源 ...

  6. Runtime底层原理总结--反汇编分析消息转发

    消息转发:发送一个消息,也就是sel查找imp,当没有找到imp,接下来进入动态方法解析,如果开发者并没有处理,会进入消息转发. 消息转发 前几篇文章介绍了Runtime底层原理和动态方法解析总结 , ...

  7. java.lang.Runtime

    2019独角兽企业重金招聘Python工程师标准>>> Runtime是java application与系统的接口, 可以启动单独的进程执行外部命令: 监控方法甚至指令的执行: 加 ...

  8. JDK源码解析-Runtime类

    Runtime类就是使用的单例设计模式 通过源代码查看使用的是哪儿种单例模式 public class Runtime {private static Runtime currentRuntime = ...

  9. Objective-C Runtime的数据类型

    转自:http://www.cnblogs.com/whyandinside/archive/2013/02/26/2933552.html Class Objective-C是支持反射的,先来了解一 ...

最新文章

  1. 从起源到未来,一文看懂70年的人工智能简史
  2. 苹果2010新品发布会图文实录
  3. 自定义Title(可以实现类似于携程网上价格的显示方式)
  4. python游戏程序-python游戏程序
  5. 如何做好一款爬虫产品
  6. TREK1000 评估套件的软件技术分析
  7. AQS.addWaiter
  8. sublime的TAB和空格统一
  9. linux kernel defconfig和.config
  10. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍
  11. Sublime Text各种插件使用方法
  12. 蓝桥杯省赛2017年Java组B组
  13. Unity之物理引擎
  14. 李国庆与当当,一个中国网络书店的传奇
  15. [ERR] 1273 - Unknown collation: ‘utf8mb4_0900_ai_ci‘
  16. HTML菜单中有关selected=true和setAttribute(“selected“,“selected“)的异同以及selected设置无法生效的问题解析
  17. 训练和推理阶段的BN和Dropout
  18. 使用husky + lint-staged助力团队编码规范
  19. 常见的系统架构风格有哪些?各有什么优缺点?
  20. python遍历指定文件夹下所有文件夹和文件

热门文章

  1. html js css如何关联_html+css +js 选项卡
  2. 改变层级_3DMAX基础,可编辑多边形层级介绍及概念
  3. 输入一个数判断是否对称java_判断对称矩阵 - osc_4mawo3g6的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. matlab debug出现k,MATLAB下的程序调试
  5. bash不识别命令 ssh_关于Python脚本在后台运行的几种方式!(linux,bash,tmux,等)...
  6. python巡检脚本juniper_python实现巡检系统(solaris)示例
  7. 小甲鱼python课后题答案_Python 小甲鱼教程 课后练习44
  8. linux命令行怎么播放,如何在在 Linux 命令行中观看彩虹猫
  9. java oracle临时表,JdbcTemplate操作oracle的临时表
  10. python dict下标_python数据类型性能问题