IOS中Runtime学习笔记


一.什么是Runtime (运行时机制)

  1. Objective-C语言是一门动态语言,他将很多的静态语言在编译和链接时做的事情放到了运行时来Handle. 这中语言的优点: 会让我们写代码时更具灵活性,在运行的时候我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。
  2. 这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。
  3. 对于C语言,函数的调用在编译的时候会决定调用哪个函数。对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
  4. 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而在编译阶段,C语言调用未实现的函数就会报错。

二. 了解Objective-C中的类和对象:
1. 类--> Class
首先我们看一下苹果的API文档, 我们通过工程中导入#import <objc/objc.h>的头文件, 点击去我们会看到OC中的具体类是怎么定义的,如下面代码:

/// An opaque type that represents an Objective-C class.
译:一个不透明的类型代表一个objective - c类
 typedef struct objc_class *Class;

通过上面的部分我们可以看出Objective-C类是由Class类型来表示的,实际上是一个指向objc_class结构体的一个指针. 到这里你肯定想知道objc_class的这个结构体是咋定义的呢? 下面就让我们看看具体它里面是什么东东️

// 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;  类的版本信息,默认为0
 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: 需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类),metaclass 存储类的static类成员变量与static类成员方法(+开头的方法);实例对象中的 isa 指向类结构称作 class(普通的),class 结构存储类的普通成员变量与普通成员方法(-开头的方法,这也就是说通过类对象访问静态变量和函数 实例对象访问普通的变量和函数
super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL
cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率
info:运行期使用的标志位,比如0x1(CLS_CLASS)表示该类为普通class,0x2(CLS_META)表示该类为 metaclass。
** instance_size:实例大小,即内存所占空间。
** ivars:
指向成员变量列表的指针。
methodLists:根据标志位的不同可能指向不同,比如可能指向实例方法列表,或者指向类方法列表。
cache:因为Objective-C的消息转发需要查找dispatch table甚至可能需要遍历继承体系,所以缓存最近使用的方法。
protocols:类需要遵守的协议。

2. 对象--> id:
typedef struct objc_object {
Class isa;
} *id;

可以发现, id可以用来表示任意一个对象,它是一个 objc_object 结构类型的指针,其第一个成员是一个 objc_class 结构类型的指针

我们的根类NSObject也同样是只有一个Class成员:
@interface NSObject <NSObject> {
Class isa;
}

这个isa到底是什么呢?官方介绍是这样的:
Every object is connected to the run-time system through its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.
译: 每个对象连接到运行时系统通过isa实例变量,继承了NSObject类。isa标识对象的类,它指向一个结构,编译后的类定义。通过isa,一个对象可以找到任何信息需要在运行timesuch作为其在继承层次结构,规模和结构的实例变量和方法实现它可以执行的位置响应消息
可见,一个对象(Object)的isa指向了这个对象的类(Class),而这个对象的类(Class)的isa指向了metaclass。这样我们就可以找到静态方法和变量了.

3. 类创建的运行时过程:

① 类的实例对象的 isa 指向它的类;类的 isa 指向该类的 metaclass;
② 类的 super_class 指向其父类,如果该类为根类则值为 NULL;
③ metaclass 的 isa 指向根 metaclass,如果该 metaclass 是根 metaclass 则指向自身;
④ metaclass 的 super_class 指向父 metaclass,如果该 metaclass 是metaclass 则指向该 metaclass 对应的类;
⑤ Object-C 为每个类的定义生成两个 objc_class ,一个普通的 class,另一个即 metaclass。我们可以在运行期创建这两个 objc_class 数据结构,然后使用 objc_addClass将 class 注册到运行时系统中,以此实现动态地创建一个新的类。

三. 项目中具体使用Runtime库里面的函数介绍:
1. 类(Class)相关操作函数:

① ClassName操作的函数:

/** 
 * Returns the name of a class.
 * 
 * @param cls A class object.
 * 
 * @return The name of the class, or the empty string if \\\\e cls is \\\\c Nil.
获取类的类名,对于class_getName函数,如果传入的cls为Nil,则返回一个字字符串。
 */
 const char *class_getName(Class cls) 

/** 
 * Returns the class name of a given object.
 * 
 * @param obj An Objective-C object.
 * 
 * @return The name of the class of which \\\\e obj is an instance.
返回一个给定对象的类名
 */
 const char *object_getClassName(id obj)

② super_class(父类) 与 meta_class(元类)主要操作函数

/** 
 * Returns the superclass of a class.
 * 
 * @param cls A class object.
 * 
 * @return The superclass of the class, or \\\\c Nil if
 *  \\\\e cls is a root class, or \\\\c Nil if \\\\e cls is \\\\c Nil.
 *
 * @note You should usually use \\\\c NSObject's \\\\c superclass method instead of this function.
获取类的父类,当cls为Nil或者cls为根类时,返回Nil。不过通常我们可以使用NSObject类的superclass方法来达到同样的目的。
 */
 Class class_getSuperclass(Class cls) 


/** 
 * Returns a Boolean value that indicates whether a class object is a metaclass.
 * 
 * @param cls A class object.
 * 
 * @return \\\\c YES if \\\\e cls is a metaclass, \\\\c NO if \\\\e cls is a non-meta class, 
 *  \\\\c NO if \\\\e cls is \\\\c Nil.
  判断给定的Class是否是一个元类,如果是cls是元类,则返回YES;如果否或者传入的cls为Nil,则返回NO
 */
BOOL class_isMetaClass(Class cls) 

③ 实例变量大小

/** 
 * Returns the size of instances of a class.
 * 
 * @param cls A class object.
 * 
 * @return The size in bytes of instances of the class \\\\e cls, or \\\\c 0 if \\\\e cls is \\\\c Nil.
 */
OBJC_EXPORT size_t class_getInstanceSize(Class cls) 

④ Ivars(成员变量)

//  在objc_class中,Ivars存放着所有成员变量.属性的信息,ivars是一个数组,数组中每个元素是指向Ivar(变量信息)的指针

/** 
 * Returns the \\\\c Ivar for a specified instance variable of a given class.
 * 
 * @param cls The class whose instance variable you wish to obtain.
 * @param name The name of the instance variable definition to obtain.
 * 
 * @return A pointer to an \\\\c Ivar data structure containing information about 
 *  the instance variable specified by \\\\e name.
获取class中指定名称实例成员变量的信息
 */
 Ivar class_getInstanceVariable(Class cls, const char *name)


/** 
 * Returns the Ivar for a specified class variable of a given class.
 * 
 * @param cls The class definition whose class variable you wish to obtain.
 * @param name The name of the class variable definition to obtain.
 * 
 * @return A pointer to an \\\\c Ivar data structure containing information about the class variable specified by \\\\e name.
获取类成员变量的信息
 */
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name) 


/** 
 * Adds a new instance variable to a class.
 * 
 * @return YES if the instance variable was added successfully, otherwise NO 
 *         (for example, the class already contains an instance variable with that name).
 *
 * @note This function may only be called after objc_allocateClassPair and before objc_registerClassPair. 
 *       Adding an instance variable to an existing class is not supported.
 * @note The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
 * @note The instance variable's minimum alignment in bytes is 1<<align. The minimum alignment of an instance 
 *       variable depends on the ivar's type and the machine architecture. 
 *       For variables of any pointer type, pass log2(sizeof(pointer_type)).
添加成员变量
 */
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size, 
                               uint8_t alignment, const char *types)

/** 
 * Describes the instance variables declared by a class.
 * 
 * @param cls The class to inspect.
 * @param outCount On return, contains the length of the returned array. 
 *  If outCount is NULL, the length is not returned.
 * 
 * @return An array of pointers of type Ivar describing the instance variables declared by the class. 
 *  Any instance variables declared by superclasses are not included. The array contains *outCount 
 *  pointers followed by a NULL terminator. You must free the array with free().
 * 
 *  If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.

获取整个成员变量列表, 返回是一个数组,outCount指针返回数组的大小
 */
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

⑤ Property(属性)操作函数

/** 
 * Returns a property with a given name of a given class.
 * 
 * @param cls The class you want to inspect.
 * @param name The name of the property you want to inspect.
 * 
 * @return A pointer of type \\\\c objc_property_t describing the property, or
 *  \\\\c NULL if the class does not declare a property with that name, 
 *  or \\\\c NULL if \\\\e cls is \\\\c Nil.
获取指定属性
 */
 objc_property_t class_getProperty(Class cls, const char *name)


/** 
 * Describes the properties declared by a class.
 * 
 * @param cls The class you want to inspect.
 * @param outCount On return, contains the length of the returned array. 
 *  If \\\\e outCount is \\\\c NULL, the length is not returned.        
 * 
 * @return An array of pointers of type \\\\c objc_property_t describing the properties 
 *  declared by the class. Any properties declared by superclasses are not included. 
 *  The array contains \\\\c *outCount pointers followed by a \\\\c NULL terminator. You must free the array with \\\\c free().
 * 
 *  If \\\\e cls declares no properties, or \\\\e cls is \\\\c Nil, returns \\\\c NULL and \\\\c *outCount is \\\\c 0.

获取属性列表,返回一个数组,outCount指针返回数组的大小
 */
 objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

/** 
 * Adds a property to a class.
 * 
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \\\\e attributes.
 * 
 * @return \\\\c YES if the property was added successfully, otherwise \\\\c NO
 *  (for example, the class already has that property).

为类添加属性
 */
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)


/** 
 * Replace a property of a class. 
 * 
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \\\\e attributes. 

替换类中的属性
 */
 void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

⑥ Method(方法)操作

/** 
 * Adds a new method to a class with a given name and implementation.
 * 
 * @param cls The class to which to add a method.
 * @param name A selector that specifies the name of the method being added.
 * @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
 * @param types An array of characters that describe the types of the arguments to the method. 
 * 
 * @return YES if the method was added successfully, otherwise NO 
 *  (for example, the class already contains a method implementation with that name).
 *
 * @note class_addMethod will add an override of a superclass's implementation, 
 *  but will not replace an existing implementation in this class. 
 *  To change an existing implementation, use method_setImplementation.

添加方法
 */
 BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types)

/* 
* 获取实例方法
*/
Method class_getInstanceMethod ( Class cls, SEL name );

 /* 
* 获取类方法
*/
Method class_getClassMethod ( Class cls, SEL name );

  /* 
* 获取所有方法的数组
*/
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

  /* 
* 替代方法的实现
*/
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

  /* 
* 返回方法的具体实现
*/
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

  /* 
* 类实例是否响应指定的selector
*/ 
BOOL class_respondsToSelector ( Class cls, SEL sel );

⑦ Version(版本)

// 获取版本号
int class_getVersion ( Class cls );
 
// 设置版本号
void class_setVersion ( Class cls, int version );

实例将之后更新......

参考文献:hengshujiyi[Objective-C Runtime 运行时之一:类与对象]

Civel_Xu [runtime 运行时机制 + 应用场景]

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,686评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,548评论 33 466
  • 前言 runtime其实在我们日常开发过程中很少使用到,尤其是像我现在比较初级的程序猿就更用不到了。但是去面试很多...
    WolfTin阅读 616评论 0 2
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,220评论 1 5
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 729评论 0 2