iOS开发·runtime原理新葡京网站开发与实践: 基本知识篇

  • 运行时新葡京网站开发haozbbs.comQ1446595067
  • 1.1 基本概念: 运行时

    Runtime 的概念

    Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的。比如:

    以上你可能看不出它的价值,但是我们需要了解的是 Objective-C 是一门动态语言,它会将一些工作放在代码运行时才处理而并非编译时。也就是说,有很多类和成员变量在我们编译的时是不知道的,而在运行时,我们所编写的代码会转换成完整的确定的代码运行。

    因此,编译器是不够的,我们还需要一个运行时系统(Runtime system)来处理编译后的代码。Runtime 基本是用 C 和汇编写的,由此可见苹果为了动态系统的高效而做出的努力。苹果和 GNU 各自维护一个开源的 Runtime 版本,这两个版本之间都在努力保持一致。

    Runtime 的作用

    Objc 在三种层面上与 Runtime 系统进行交互:

    通过 Objective-C 源代码

    通过 Foundation 框架的 NSObject 类定义的方法

    通过对 Runtime 库函数的直接调用

    1.2 各种基本概念的C表达

    在 Objective-C 中,类、对象和方法都是一个 C 的结构体,从 objc/objc.h(对象,objc_object,id)以及objc/runtime.h(其它,类,方法,方法列表,变量列表,属性列表等相关的)以及中,我们可以找到他们的定义。

    ① 类

    类对象(Class)是由程序员定义并在运行时由编译器创建的,它没有自己的实例变量,这里需要注意的是类的成员变量和实例方法列表是属于实例对象的,但其存储于类对象当中的。我们在objc/objc.h下看看Class的定义:

    Class

    在这里我还是要推荐下我自己建的iOS开发学习群:680565220,群里都是学ios开发的,如果你正在学习ios ,小编欢迎你加入,今天分享的这个案例已经上传到群文件,大家都是软件开发党,不定期分享干货(只有iOS软件开发相关的),包括我自己整理的一份2018最新的iOS进阶资料和高级开发教程
    
    可以看到类是由Class类型来表示的,它是一个objc_class结构类型的指针。我们接着来看objc_class结构体的定义:

    objc_class

    参数解析

    isa指针是和Class同类型的objc_class结构指针,类对象的指针指向其所属的类,即元类。元类中存储着类对象的类方法,当访问某个类的类方法时会通过该isa指针从元类中寻找方法对应的函数指针。

    super_class指针指向该类所继承的父类对象,如果该类已经是最顶层的根类(如NSObject或NSProxy), 则 super_class为NULL。

    cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。

    version:我们可以使用这个字段来提供类的版本信息。这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。

    protocols:当然可以看出这一个objc_protocol_list的指针。关于objc_protocol_list的结构体构成后面会讲。

    获取类的类名

    实例对象是我们对类对象alloc或者new操作时所创建的,在这个过程中会拷贝实例所属的类的成员变量,但并不拷贝类定义的方法。调用实例方法时,系统会根据实例的isa指针去类的方法列表及父类的方法列表中寻找与消息对应的selector指向的方法。同样的,我们也来看下其定义:

    objc_object

    可以看到,这个结构体只有一个isa变量,指向实例对象所属的类。任何带有以指针开始并指向类结构的结构都可以被视作objc_object, 对象最重要的特点是可以给其发送消息。 NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

    另外我们常见的id类型,它是一个objc_object结构类型的指针。该类型的对象可以转换为任何一种对象,类似于C语言中void *指针类型的作用。其定义如下所示:

    id

    元类(Metaclass)就是类对象的类,每个类都有自己的元类,也就是objc_class结构体里面isa指针所指向的类. Objective-C的类方法是使用元类的根本原因,因为其中存储着对应的类对象调用的方法即类方法。

    当向对象发消息,runtime会在这个对象所属类方法列表中查找发送消息对应的方法,但当向类发送消息时,runtime就会在这个类的meta class方法列表里查找。所有的meta class,包括Root class,Superclass,Subclass的isa都指向Root class的meta class,这样能够形成一个闭环。

    所以由上图可以看到,在给实例对象或类对象发送消息时,寻找方法列表的规则为:

    当发送消息给实例对象时,消息是在寻找这个对象的类的方法列表(实例方法)

    当发送消息给类对象时,消息是在寻找这个类的元类的方法列表(类方法)

    元类,就像之前的类一样,它也是一个对象,也可以调用它的方法。所以这就意味着它必须也有一个类。所有的元类都使用根元类作为他们的类。比如所有NSObject的子类的元类都会以NSObject的元类作为他们的类。

    根据这个规则,所有的元类使用根元类作为他们的类,根元类的元类则就是它自己。也就是说基类的元类的isa指针指向他自己。

    操作函数

    super_class和meta-class

    在Objective-C中,属性(property)和成员变量是不同的。那么,属性的本质是什么?它和成员变量之间有什么区别?简单来说属性是添加了存取方法的成员变量,也就是:

    @property = ivar + getter + setter;

    因此,我们每定义一个@property都会添加对应的ivar, getter和setter到类结构体objc_class中。具体来说,系统会在objc_ivar_list中添加一个成员变量的描述,然后在methodLists中分别添加setter和getter方法的描述。下面的objc_property_t是声明的属性的类型,是一个指向objc_property结构体的指针。

    用法举例

    另外,关于属性有一个objc_property_attribute_t结构体列表,objc_property_attribute_t结构体包含name和value

    objc_property_attribute_t

    常用的属性如下:

    属性类型 name值:T value:变化

    编码类型 name值:C(copy) &(strong) W(weak)空(assign) 等 value:无

    非/原子性 name值:空(atomic) N(Nonatomic) value:无

    变量名称 name值:V value:变化

    例如

    ⑤ 成员变量

    Ivar: 实例变量类型,是一个指向objc_ivar结构体的

    指针

    Ivar

    在objc_class中,所有的成员变量、属性的信息是放在链表ivars中的。ivars是一个数组,数组中每个元素是指向Ivar(变量信息)的指针。

    objc_ivar_list

    其中,

    方法名类型为 SEL

    方法类型 method_types 是个 char 指针,存储方法的参数类型和返回值类型

    method_imp 指向了方法的实现,本质是一个函数指针

    简言之,Method = SEL + IMP + method_types,相当于在SEL和IMP之间建立了一个映射。

    操作函数

    在源码中没有直接找到 objc_selector 的定义,从一些书籍上与 Blog 上看到可以将 SEL 理解为一个 char* 指针。

    具体这 objc_selector 结构体是什么取决与使用GNU的还是Apple的运行时, 在Mac OS X中SEL其实被映射为一个C字符串,可以看作是方法的名字,它并不一个指向具体方法实现(IMP类型才是)。

    对于所有的类,只要方法名是相同的,产生的selector都是一样的。

    操作函数

    实际上就是一个函数指针,指向方法实现的首地址。通过取得 IMP,我们可以跳过 runtime 的消息传递机制,直接执行 IMP指向的函数实现,这样省去了 runtime 消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些,当然必须说明的是,这种方式只适用于极特殊的优化场景,如效率敏感的场景下大量循环的调用某方法。

    操作函数

    上面提到了objc_class结构体中的cache字段,它用于缓存调用过的方法。这个字段是一个指向objc_cache结构体的指针,其定义如下:

    Cache

    该结构体的字段描述如下:

    mask:一个整数,指定分配的缓存bucket的总数。在方法查找过程中,Objective-C runtime使用这个字段来确定开始线性查找数组的索引位置。指向方法selector的指针与该字段做一个AND位操作(index = (mask & selector))。这可以作为一个简单的hash散列算法。

    occupied:一个整数,指定实际占用的缓存bucket的总数。

    buckets:指向Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。

    ? 协议链表

    前面objc_class的结构体中有个协议链表的参数,协议链表用来存储声明遵守的正式协议

    objc_protocol_list

    1. 方法调用流程

    objc_msgSend() Tour 系列文章通过对 objc_msgSend 的汇编源码分析,总结出以下流程:

    2.1 方法调用流程

    检查 selector 是否需要忽略

    检查 target 是否为 nil,如果是 nil 就直接 cleanup,然后 return

    在 target 的 Class 中根据 selector 去找 IMP

    2.2 寻找 IMP 的过程:

    在当前 class 的方法缓存里寻找(cache methodLists)

    找到了跳到对应的方法实现,没找到继续往下执行

    从当前 class 的 方法列表里查找(methodLists),找到了添加到缓存列表里,然后跳转到对应的方法实现;没找到继续往下执行

    从 superClass 的缓存列表和方法列表里查找,直到找到基类为止

    以上步骤还找不到 IMP,则进入消息动态处理和消息转发流程,详见请关注微信公众号:程序员大牛!

    我们能在 objc4官方源码 中找到上述寻找 IMP 的过程,具体对应的代码如下:

    objc-class.mm

    1. 运行时相关的API

    3.1 通过 Foundation 框架的 NSObject 类定义的方法

    Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以都继承了 NSObject 的行为。(NSProxy 类时个例外,它是个抽象超类)

    一些情况下,NSObject 类仅仅定义了完成某件事情的模板,并没有提供所需要的代码。例如 -description 方法,该方法返回类内容的字符串表示,该方法主要用来调试程序。NSObject 类并不知道子类的内容,所以它只是返回类的名字和对象的地址,NSObject 的子类可以重新实现。

    还有一些 NSObject 的方法可以从 Runtime 系统中获取信息,允许对象进行自我检查。例如:

    -class方法返回对象的类;

    -isKindOfClass: 和 -isMemberOfClass: 方法检查对象是否存在于指定的类的继承体系中(是否是其子类或者父类或者当前类的成员变量);

    -respondsToSelector: 检查对象能否响应指定的消息;

    -conformsToProtocol:检查对象是否实现了指定协议类的方法;

    -methodForSelector: 返回指定方法实现的地址。

    常见的一个例子:

    id 和 void 转换API:(__bridge void )

    在 ARC 有效时,通过 (__bridge void )转换 id 和 void 就能够相互转换。为什么转换?这是因为objc_getAssociatedObject的参数要求的。先看一下它的API:

    可以知道,这个“属性名”的key是必须是一个void *类型的参数。所以需要转换。关于这个转换,下面给一个转换的例子:

    1. 运行时实战指南

    上面的API不是提供大家背的,而是用来查阅的,当你要用到的时候查阅。因为这些原理和API光看没用,需要实战之后再回过头来查阅和理解。笔者另外写了runtime的原理与实践。如果想了解runtime的更多知识,可以关注微信公众号:程序员大牛,每天分享干货!

    原文地址:http://blog.51cto.com/13860019/2139331

    时间: 2024-10-12 08:08:06

    iOS开发·runtime原理新葡京网站开发与实践: 基本知识篇的相关文章

    新葡京源码搭建与Java多线程进程的概述

    1:要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在. 2:什么是进程?通过任务管理器我们就看到了进程的存在.而通过观察,我们发现只有运行的程序才会出现进程.新葡京源码搭建QQ:2152876294 网址diguaym.com 进程:就是正在运行的程序. 进程是系统进行资源分配和调用的独立单位.每一个进程都有它自己的内存空间和系统资源. 3:多进程有什么意义呢?单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情.举例:一边玩游戏(游戏进程)

    新葡京源码搭建与pytest安装&实例

    Pytest介绍?非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考?能够支持简单的单元测试和复杂的功能测试?支持参数化?执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败?支持重复执行失败的case?支持运行由nose, unittest编写的测试case?具有很多第三方插件,并且可以自定义扩展?方便的和持续集成工具集成 新葡京源码搭建QQ:2152876294 网址diguaym.com安装pytest1.安装方法pip install -U pytest2.查

    在网上被黑永利.新葡京.金沙 澳门 ××× 被黑提现一直审核.维护等通知怎么办 求助

    最近很多人赌碰到了这样的问题,自己已经在那个网站玩了很久了,有的甚至玩了将近两年了,但是最后还是碰到了这样的问题:你的账户异常登录.网站维护.网站出款端口维护.账户涉嫌套利.系统自动抽查审核.网站抽查审核.账户违规下注.银行系统维护等,第三方出款通道维护,每个月都会抽查那么几个人进行审核.你们都知道这是为啥?我明明已经对这个网站已经有了绝对的信任,而且以前也一直提款没有任何问题很快就到账了,但是这一次就不行.在这里我不直接点名那些黑网,我就直接跟你讲这是黑你的套路.为啥总有人在文章提到实体公司,

    如何做好网站开发项目的需求分析

    一个网站项目的确立是建立在各种各样的需求上面的,这种需求往往来自于客户的实际需求或者是出于公司自身发展的需 要,其中客户的实际需求也就是说这种交易性质的需求占了绝大部分.面对对网站开发拥有不同知识层面的客户,项目的负责人对用户需求的理解程度,在很大程度 上决定了此类网站开发项目的成败.因此如何更好地的了解.分析.明确用户需求,并且能够准确.清晰以文档的形式表达给参与项目开发的每个成员,保证开发过 程按照满足用户需求为目的正确项目开发方向进行,是每个网站开发项目管理者需要面对的问题. 一.那些人应

    MVC5 网站开发之七 用户功能 1、角色的后台管理

    角色是网站中都有的一个功能,用来区分用户的类型.划分用户的权限,这次实现角色列表浏览.角色添加.角色修改和角色删除. 目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 MVC5 网站开发之三 数据存储层功能实现 MVC5 网站开发之四 业务逻辑层的架构和基本功能 MVC5 网站开发之五 展示层架构 MVC5 网站开发之六 管理员 1.登录.验证和注销 MVC5 网站开发之六 管理员 2.添加.删除.重置密码.修改密码.列表浏览 MVC5 网站开发之七 用

    I am back-电商网站开发&jQuery

    hi 之前有将近两周的时间没有更新,除了懒就是其他的事情耽误了.现在好了,回家了,虽然家里停水,外面又有积雪,天寒地冻的,但诸多不便,都比不过有点闲的好. 开搞每个学PHP的必经之路——电商网站的开发. 1.电商网站开发——前端 一.首页制作 1.1 概况&准备 整个电商网站包括什么呢,就是首页信息,后面的分类信息页,商品详情页,购物页面,售后页面等等,所以就一步步做.自己做的时候可以简单的画个概况图来指导开发,免得逻辑搞混. 准备:项目文件夹,其中要有images(图片素材),js(javas

    Mysql 与 php动态网站开发 入门教程

    这个系列的教程由表单开始写,因为表单可以把数据库和web 之间的交互表现得很明显.提交表单 ,数据库记录注册信息. 本教程属于基础教程.大神请略过. 对于php和mysql之间的稳固性很受程序员的喜爱.虽说最近出现了hack语言准备替代php语言,但是这个语言本身也是基于php的,后面我们可以对hack语言进行一定得学习. 好像博客园有好多大牛,不知道会不会被喷,好怕 ........当然,我是借着自己学习的这股劲,把学习的过程也给记录下来.教程涉及  html 表单 简单的php 和mysql

    MVC5 网站开发之八 栏目功能 添加、修改和删除

    本次实现栏目的浏览.添加.修改和删除. 栏目一共有三种类型. 常规栏目-可以添加子栏目,也可以添加内容模型.当不选择内容模型时,不能添加内容. 单页栏目-栏目只有一个页面,可以设置视图. 链接栏目-栏目为一个链接,点击后转到相应链接. 在视图中原本栏目的树形显示插件使用Bootstrap TreeView 1.2.0(MVC5 网站开发之六 管理员 2.添加.删除.重置密码.修改密码.列表浏览),后来使用中发现zTree使用起来更习惯,所以更换成zTree了. 目录 MVC5网站开发之一 总体概

    MVC5 网站开发之九 网站设置

    网站配置一般用来保存网站的一些设置,写在配置文件中比写在数据库中要合适一下,因为配置文件本身带有缓存,随网站启动读入缓存中,速度更快,而保存在数据库中要单独为一条记录创建一个表,结构不够清晰,而且读写也没有配置文件容易实现.这次要做的是网站的基本信息,数据保存在SiteConfig.config. 目录 MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 MVC5 网站开发之三 数据存储层功能实现 MVC5 网站开发之四 业务逻辑层的架构和基本功能 MVC5 网站开发之五 展示层架