iOS笔记-Runtime-面向对象

Runtime

Runtime 是一个用C写的运行时库,主要为 C 添加了面向对象的能力并创造了 Objective-C,并且拥有消息发送,消息转发等功能。
Runtime源码苹果是公开的,希望看着源码来理解苹果设计的思想,本文下载的是objc4-532.tar.gz,不要下载太新的,新版runtime结合了swift语言运行需要的环境,底层源码改版较多。

Runtime 涉及三个点,面向对象消息发送消息转发

我们都知道高级编程语言想要成为可执行文件需要先编译为汇编语言,再汇编为机器语言,但是OC并不能直接编译为汇编语言,而是要先转写为纯C语言再进行编译和汇编的操作,从OC到C语言的过渡就是由runtime来实现的。C语言是面向过程开发的,OC能够进行面向对象开发主要依靠Runtime,Runtime将OC语言中面向对象的类转换为C语言面向过程的结构体,这个转换过程就是我们所要讲的Runtime。

面向对象

面向对象编程中,最重要的概念就是类,NSObjcet是OC语言中对象的基类(NSProxy除外),所有对象都要继承NSObjcet,下面我们就从代码入手,看看OC是如何实现类的?

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

在NSObjcet源码,只能看到一个Class的isa指针,isa指针指向该类的结构体,我们看看Class:

typedef struct objc_class *Class;
struct objc_class {
  Class isa; // 指向metaclass
   
  Class super_class ; // 指向父类,如果这个类是根类,则为NULL
  const char *name ; // 类名
  long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
  long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
  long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);
  struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址
  struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;
  struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;
  struct objc_protocol_list *protocols; // 存储该类遵守的协议
    }

在这个objc_class结构体中,我们发现类定义所需要的必要信息,名称、父类、成员变量列表、方法列表等。这里的信息多数看字面都能理解,只有Class isa这个类型需要特别关注,它是Runtime里定义数据对象关系的核心。

  • 对象实例化以后会将实例的isa指针指向对象Class,无论初始化多少个实例,其isa指针都会指向同一个对象Class,这个Class中存储普通成员变量和对象方法(“-”开头的方法)。
  • 对象Class会将自身的isa指针指向一个静态Class(metaclass),该实例专门管理静态成员变量和静态方法。
image.png

Objective-C 类也是对象,runtime 通过创建 Meta Classes 来处理这些。当你发送一个消息像这样 [NSObject alloc] 你正在向类对象发送一个消息,这个类对象需要是 MetaClass 的实例,MetaClass 也是 root meta class 的实例。当你说继承自 NSObject 时,你的类指向 NSObject 作为自己的 superclass。然而,所有的 meta class 指向 root metaclass 作为自己的 superclass。所有的 meta class 只是简单的有一个自己响应的方法列表。所以当你向一个类对象发送消息如 [NSObject alloc],然后实际上 objc_msgSend() 会检查 meta class 看看它是否响应这个方法,如果他找到了一个方法,就在这个 Class 对象上执行(译注:class 是一个实例对象的类型,Class 是一个类(class)的类型。对于完全的 OO 来说,类也是个对象,类是类类型(MetaClass)的实例,所以类的类型描述就是 meta class)。

为什么我们继承自苹果的类

从你开始 Cocoa 开发时,那些教程就说如继承自 NSObject 然后开始写一些代码,你享受了很多继承自苹果的类所带来的便利。有一件事你从未意识到的是你的对象被设置为使用 Objective-C 的 runtime。当我们为我们的类的一个实例分配了内存,像这样…

MyObject *object = [[MyObject alloc] init];

最先执行的消息是 +alloc。如果你查看下文档, 它说“新的实例对象的 isa 实例变量被初始化为指向一个数据结构,那个数据结构描述了这个类;其他的实例变量被初始化为 0。”所以继承自苹果的类不仅仅是继承了一些重要的属性,也继承了能在内存中轻松分配内存的能力和在内存中创建满足 runtime 期望的对象结构(设置 isa 指针指向我们的类)。

那么 Class Cache 是什么?(objc_cache *cache)

当 Objective-C runtime 沿着一个对象的 isa 指针检查时,它会发现一个对象实现了许多的方法。然而你可能只调用其中一小部分的方法,也没有意义每次检查时搜索这个类的分发表(dispatch table)中的所有 selector。所以这个类实现了一个缓存,当你搜索一个类的分发表,并找到合适的 selector 后,就会把它放进缓存中。所以当 objc_msgSend() 在一个类中查找 selector 时会先查找类缓存。有个理论是,当你在一个类上调用了一个消息,你很可能之后还会调用它。所以如果我们考虑到这点,就意味着当我们有个子类继承自 NSObject 叫做 MyObject 并且运行了以下的代码

alloc 方法会为对象分配一块内存空间,空间的大小为 isa_t(8 字节)的大小加上所有成员变量所需的空间,再进行一次内存对齐。分配完空间后会初始化 isa_t ,而 isa_t 是一个 union 类型的结构体(或者称之为联合体),它的结构是在 Runtime 里被定义的。

MyObject *obj = [[MyObject alloc] init];

发生了以下的事:

(1) [MyObject alloc] 首先被执行。MyObject 没有实现 alloc 方法,所以我们不能在这个类中找到 +alloc 方法,然后沿着 superclass 指针会指向 NSObject。

(2) 我们询问 NSObject 是否响应 +alloc 方法,它可以。+alloc 检查消息的接收者类,是 MyObject,然后分配一块和我们的类同样大小的内存空间,并初始化它的 isa 指针指向 MyObject 类,我们现在有了一个实例对象,最终把类对象的 +alloc 方法加入 NSObject 的类缓存(class cache)中(lastly we put +alloc in NSObject's class cache for the class object )。

(3) 到现在为止,我们发送了一个类消息,但是现在我们发送一个实例消息,只是简单的调用 -init 或者我们设计的初始化方法。当然,我们的类会响应这个方法,所以 -(id)init 加入到缓存中。(译注:要是 MyObject 实现了 init 方法,就会把 init 方法加入到 MyObject 的 class cache 中,要是没有实现,只是因为继承才有了这个方法,init 方法还是会加入到 NSObject 的 class cache 中)。

(4) 然后 self = [super init] 被调用。super 是个 magic keyword,指向对象的父类,所以我们得到了 NSObject 并调用它的的 init 方法。这样可以确保 OOP(面相对象编程) 的继承功能正常,这个方法可以正确的初始化父类的变量,之后你(在子类中)可以初始化自己的变量,如果需要可以覆盖父类的方法。在 NSObject 的例子中,没什么重要的要做,但并不总是这样。有时要做些重要的初始化。比如…

参考文章:

iOS 开发:『Runtime』详解(一)基础知识
Runtime之消息发送和消息转发
深入浅出Runtime (一) 什么是Runtime? 定义
深入浅出Runtime (二) Runtime的消息机制
深入浅出Runtime (三) Runtime的消息转发
深入浅出Runtime (四) Runtime的实际应用之一,字典转模型

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 913评论 0 6
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,541评论 33 466
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,215评论 1 5
  • Objective-C语言是一门动态语言,他将很多静态语言在编译和链接时期做的事情放到了运行时来处理。这种动态语言...
    tigger丨阅读 1,381评论 0 8