runtime(1)初识

声明:本文仅供记忆使用,并不适合新手小白观看

OC的本质

  • 我们平时编写的Objective-C代码,底层实现其实都是C\C++代码

  • Objective-C的面向对象都是基于C\C++的数据结构实现的

  • 将Objective-C代码转换为C\C++代码

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
    如果需要链接其他框架,使用-framework参数。比如-framework UIKit

对象的内存大小

系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)

1
2
3
4
5
6
7
//创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);

//创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);

类class和元类meta-class

1
2
3
4
5
6
7
8
9
10
//获取实例
NSObject *obj = [[NSObject alloc] init];
//获取类对象方法
Class objClass1 = [obj class];
Class objClass2 = [NSObject class];
Class objClass3 = object_getClass(obj);
Class objClass4 = objc_getClass("NSObject");
Class objClass5 = [[NSObject class] class];//注意:这个还是类对象
//获取元类对象 只有一种方法object_getClass(类对象)
object_getClass([NSObject class]);
  • 实例对象、类对象、元类对象

  • isa 和 superclass

  • 类对象和元类对象内部结构

isa

arm64架构开始,对isa进行了优化,变成了一个共用体union)结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** isa_t 结构体 */
union isa_t {
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1; //是否优化
uintptr_t has_assoc : 1; //是否有设置过关联对象
uintptr_t has_cxx_dtor : 1; //是否有C++的析构函数
uintptr_t shiftcls : 33;//Class、Meta-Class对象的内存地址
uintptr_t magic : 6; //用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; //是否有被弱引用指向过
uintptr_t deallocating : 1; //对象是否正在释放
uintptr_t has_sidetable_rc : 1;//has_sidetable_rc
uintptr_t extra_rc : 19;//里面存储的值是引用计数器减1

};
};

methods 方法列表

  • 方法列表
    class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容
    class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容

  • 方法缓存
    Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度

method

  • method_t

    1
    2
    3
    4
    5
    6
    //方法
    struct objc_method {
    SEL method_name; //函数名
    char *method_types; //返回类型、参数类型
    IMP method_imp; //指向函数指针(函数地址)
    }
  • SEL
    可以通过@selector()sel_registerName()获得
    可以通过sel_getName()NSStringFromSelector()转成字符串
    不同类中相同名字的方法,所对应的方法选择器是相同的

  • method_types

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /** types为(i24@0:8i16f20)
    i 返回值
    24 所有参数的字节数
    @0 参数self,从第0个字节开始
    :8 参数SEL,从第8个字节开始
    i16 参数int,从第16个字节开始
    f20 参数float,从第20个字节开始
    **/
    - (int)addA:(int )a andB:(float)b

  • IMP
    IMP代表函数的具体实现

OC的消息机制

当OC对象调用方法时,大致3个阶段:objc_msgSend动态方法解析消息转发

  • objc_msgSend

  • 动态方法解析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    - (void)other {
    NSLog(@"%s", __func__);
    }
    /**
    类方法解析 + (BOOL)resolveClassMethod:(SEL)sel
    对象方法解析 + (BOOL)resolveInstanceMethod:(SEL)sel
    **/
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
    // 获取其他方法
    Method method = class_getInstanceMethod(self, @selector(other));
    // 动态添加test方法的实现
    class_addMethod(self, sel,
    method_getImplementation(method),
    method_getTypeEncoding(method));
    // 返回YES代表有动态添加方法
    return YES;
    }
    return [super resolveInstanceMethod:sel];
    }
  • 消息转发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    调用别的对象去执行

    对象方法 - (id)forwardingTargetForSelector:(SEL)aSelector
    类对象方法 + (id)forwardingTargetForSelector:(SEL)aSelector
    **/
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
    return [[MJCat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    实现方法签名后,在forwardInvocation里做任何处理
    对象方法 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    - (void)forwardInvocation:(NSInvocation *)anInvocation

    类对象方法 + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    + (void)forwardInvocation:(NSInvocation *)anInvocation
    **/
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test:)) {
    //return [NSMethodSignature signatureWithObjCTypes:"v20@0:8i16"];
    return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
    //return [[[MJCat alloc] init] methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
    }

    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    // 参数顺序:receiver、selector、other arguments
    //int age;
    //[anInvocation getArgument:&age atIndex:2];
    //NSLog(@"%d", age + 10);
    // anInvocation.target == [[MJCat alloc] init]
    // anInvocation.selector == test:
    // anInvocation的参数:15
    // [[[MJCat alloc] init] test:15]

    [anInvocation invokeWithTarget:[[MJCat alloc] init]];
    int ret;
    [anInvocation getReturnValue:&ret];
    NSLog(@"%d", ret);
    }

具体应用

  • 利用关联对象(AssociatedObject)给分类添加属性
  • 遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
  • 交换方法实现(交换系统的方法)
  • 利用消息转发机制解决方法找不到的异常问题

runtime常用API

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //动态创建一个类(参数:父类,类名,额外的内存空间)
    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

    //注册一个类(要在类注册之前添加成员变量)
    void objc_registerClassPair(Class cls)

    //销毁一个类
    void objc_disposeClassPair(Class cls)

    //获取isa指向的Class
    Class object_getClass(id obj)

    //设置isa指向的Class
    Class object_setClass(id obj, Class cls)

    //判断一个OC对象是否为Class
    BOOL object_isClass(id obj)

    //判断一个Class是否为元类
    BOOL class_isMetaClass(Class cls)

    //获取父类
    Class class_getSuperclass(Class cls)
  • 成员变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //获取一个实例变量信息
    Ivar class_getInstanceVariable(Class cls, const char *name)

    //拷贝实例变量列表(最后需要调用free释放)
    Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

    //设置和获取成员变量的值
    void object_setIvar(id obj, Ivar ivar, id value)
    id object_getIvar(id obj, Ivar ivar)

    //动态添加成员变量(已经注册的类是不能动态添加成员变量的)
    BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)

    //获取成员变量的相关信息
    const char *ivar_getName(Ivar v)
    const char *ivar_getTypeEncoding(Ivar v)
  • 属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //获取一个属性
    objc_property_t class_getProperty(Class cls, const char *name)

    //拷贝属性列表(最后需要调用free释放)
    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)

    //获取属性的一些信息
    const char *property_getName(objc_property_t property)
    const char *property_getAttributes(objc_property_t property)
  • 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //获得一个实例方法、类方法
    Method class_getInstanceMethod(Class cls, SEL name)
    Method class_getClassMethod(Class cls, SEL name)

    //方法实现相关操作
    IMP class_getMethodImplementation(Class cls, SEL name)
    IMP method_setImplementation(Method m, IMP imp)
    void method_exchangeImplementations(Method m1, Method m2)

    //拷贝方法列表(最后需要调用free释放)
    Method *class_copyMethodList(Class cls, unsigned int *outCount)

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

    //动态替换方法
    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

    //获取方法的相关信息(带有copy的需要调用free去释放)
    SEL method_getName(Method m)
    IMP method_getImplementation(Method m)
    const char *method_getTypeEncoding(Method m)
    unsigned int method_getNumberOfArguments(Method m)
    char *method_copyReturnType(Method m)
    char *method_copyArgumentType(Method m, unsigned int index)

    //选择器相关
    const char *sel_getName(SEL sel)
    SEL sel_registerName(const char *str)

    //用block作为方法实现
    IMP imp_implementationWithBlock(id block)
    id imp_getBlock(IMP anImp)
    BOOL imp_removeBlock(IMP anImp)

参考资料

runtime源码