Runtime API

Runtime库主要做下面几件事:

  1. 封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
  2. 找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。这将在后面详细介绍。

相关结构体声明

  • Class
typedef struct objc_class *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;
  • objc_object 和 id
struct objc_object {
    Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
  • objc_method
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name OBJC2_UNAVAILABLE;
    char *method_types OBJC2_UNAVAILABLE;
    IMP method_imp OBJC2_UNAVAILABLE;
}OBJC2_UNAVAILABLE;
  • objc_ivar
typedef struct objc_ivar *Ivar;

struct objc_ivar {
    char *ivar_name OBJC2_UNAVAILABLE;
    char *ivar_type OBJC2_UNAVAILABLE;
    int ivar_offset  OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space OBJC2_UNAVAILABLE;
#endif
}OBJC2_UNAVAILABLE;
  • objc_category
typedef struct objc_category *Category;

struct objc_category {
    char *category_name OBJC2_UNAVAILABLE;
    char *class_name OBJC2_UNAVAILABLE;
    struct objc_method_list *instance_methods OBJC2_UNAVAILABLE;
    struct objc_method_list *class_methods OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
  • objc_cache
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;

struct objc_cache {
    unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
    unsigned int occupied OBJC2_UNAVAILABLE;
    Method buckets[1] OBJC2_UNAVAILABLE;
};

Runtime类相关方法

获取类名,如果传入cls为Nil,则返回一个空字符串

const char* class_getName(Class cls);

获取类的父类,当cls为Nil或者cls为根类时,返回Nil。等同于NSObject的superClass方法。

Class class_getSuperClass(Class cls);

判断给定的Class是否是一个元类

BOOL class_isMetaClass(Class cls);

实例变量大小

size_t class_getInstanceSize(Class cls);

获取类中指定名称实例成员变量的信息,返回一个包含name指定成员变量信息的objc_ivar结构体指针。

Ivar class_getInstanceVariable(Class cls, const char* name);

获取类成员变量的信息,一般认为Objective-C不支持类变量。

Ivar class_getClassVariable(Class cls, const char* name);

添加成员变量,不支持在已存在的类中添加实例变量,因此不管是系统库提供的类还是我们自定义的类都无法动态添加成员变量。但是如果运行时创建类可以调用class_addIvar函数。而且只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。

BOOL class_addIvar(Class cls, const char* name, size_t size, uint8_t alignment, const char* types);

获取整个成员变量列表,不包含父类中声明的变量。

Ivar* class_copyIvarList(Class cls, unsigned int* outCount);

获取指定属性,同ivar。

objc_property_t class_getProperty(Class cls, const char* name);

获取属性列表

objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount);

为类添加属性

BOOL class_addProperty(Class cls, const char* name, const objc_property_attribute_t *attributes, unsigned int attributeCount);

替换类的属性

void class_replaceProperty(Class cls, const char* name, const objc_property_attribute_t *attributes, unsigned int attributeCount);

runtime提供了几个函数来确定一个对象的内存区域是否可以被垃圾回收器扫描,以处理strong/weak引用。

const uint8_t * class_getIvarLayout(Class cls);
void class_setIvarLayout(Class cls, const uint8_t *layout);
const uint8_t * class_getWeakIvarLayout(Class cls);
void class_setWeakIvarLayout(Class cls, const uint8_t *layout);

添加方法,会覆盖父类的方法实现,但不会取代本类中已存在的实现,如果本类中包含一个同名的实现,则函数会返回NO。如果要修改已存在实现,可以使用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);

替代方法的实现,如果类中不存在name指定的方法,则类似于class_addMethod函数一样会添加方法;如果类中已存在name指定的方法,则类似于method_setImplementation一样替代原方法的实现。

IMP class_replaceMethod (Class cls, SEL name, IMP imp, const char *types);

返回方法的具体实现,该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。

IMP class_getMethodImplementation (Class cls, SEL name);
IMP class_getMethodImplementation_stret (Class cls, SEL name);

类实例是否响应指定的selector,通常使用NSObject类的respondsToSelector:或instancesRespondToSelector:方法来达到相同目的。

BOOL class_respondsToSelector (Class cls, SEL sel);

添加协议

BOOL class_addProtocol ( Class cls, Protocol *protocol );

返回类是否实现指定的协议

BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );

返回类实现的协议列表,返回的是一个数组,在使用后我们需要使用free()手动释放。

Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );

获取版本号

int class_getVersion ( Class cls );

设置版本号

void class_setVersion ( Class cls, int version );

动态创建一个新类和元类,如果我们要创建一个根类,则superclass指定为Nil。extraBytes通常指定为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。
为了创建一个新类,我们需要调用objc_allocateClassPair。然后使用诸如class_addMethod,class_addIvar等函数来为新创建的类添加方法、实例变量和属性等。完成这些后,我们需要调用objc_registerClassPair函数来注册类,之后这个新类就可以在程序中使用了。

Class objc_allocateClassPair( Class cls, const char *name, size_t extraBytes );

销毁一个类及其相关联的类,用于销毁一个类,不过需要注意的是,如果程序运行中还存在类或其子类的实例,则不能调用针对类调用该方法。

void objc_disposeClassPair ( Class cls );

在应用中注册由objc_allocateClassPair创建的类

void objc_registerClassPair ( Class cls );

动态创建对象,创建实例时,会在默认的内存区域为类分配内存。extraBytes参数表示分配的额外字节数。这些额外的字节可用于存储在类定义中所定义的实例变量之外的实例变量。该函数在ARC环境下无法使用。

id class_createInstance ( Class cls, size_t extraBytes );

在指定位置创建类实例

id objc_constructInstance ( Class cls, void *bytes );

销毁类实例,但不会释放并移除任何与其相关的引用。慎用

void * objc_destructInstance ( id obj );

实例操作方法

  • 针对整个对象进行操作的函数,这类函数包含
// 返回指定对象的一份拷贝

id object_copy ( id obj, size_t size );

// 释放指定对象占用的内存

id object_dispose ( id obj );
  • 针对对象实例变量进行操作的函数,这类函数包含
// 修改类实例的实例变量的值

Ivar object_setInstanceVariable ( id obj, const char *name, void *value );

// 获取对象实例变量的值

Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );

// 返回指向给定对象分配的任何额外字节的指针

void * object_getIndexedIvars ( id obj );

// 返回对象中实例变量的值

id object_getIvar ( id obj, Ivar ivar );

// 设置对象中实例变量的值

void object_setIvar ( id obj, Ivar ivar, id value );
  • 针对对象的类进行操作的函数,这类函数包含
// 返回给定对象的类名

const char * object_getClassName ( id obj );

// 返回对象的类

Class object_getClass ( id obj );

// 设置对象的类

Class object_setClass ( id obj, Class cls );

获取类定义

获取已注册的类定义的列表,获取已注册的类定义的列表。我们不能假设从该函数中获取的类对象是继承自NSObject体系的,所以在这些类上调用方法是,都应该先检测一下这个方法是否在这个类中实现。

int objc_getClassList ( Class *buffer, int bufferCount );

创建并返回一个指向所有已注册类的指针列表

Class * objc_copyClassList ( unsigned int *outCount );

返回指定类的类定义,获取已注册的类定义的列表。我们不能假设从该函数中获取的类对象是继承自NSObject体系的,所以在这些类上调用方法是,都应该先检测一下这个方法是否在这个类中实现。

Class objc_lookUpClass ( const char *name );

Class objc_getClass ( const char *name );

Class objc_getRequiredClass ( const char *name );

返回指定类的元类,如果指定的类没有注册,则该函数会调用类处理回调,并再次确认类是否注册,如果确认未注册,再返回nil。不过,每个类定义都必须有一个有效的元类定义,所以这个函数总是会返回一个元类定义,不管它是否有效。

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

推荐阅读更多精彩内容