Objective-C基础-Runtime

1、什么是Runtime

  • Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
  • Objective-C的动态性是由Runtime API来支撑的
  • Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写

2、isa详解

  • 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
    
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;

    };
};
  • nonpointer 0代表普通的指针,存储着Class、Meta-Class对象的内存地址; 1代表优化过,使用位域存储更多的信息。
  • has_assoc 是否有设置过关联对象,如果没有,释放时会更快。
  • has_cxx_dtor 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快。
  • shiftcls 存储着Class、Meta-Class对象的内存地址信息。
  • magic 用于在调试时分辨对象是否未完成初始化。
  • weakly_referenced 是否有被弱引用指向过,如果没有,释放时会更快。
  • deallocating 对象是否正在释放。
  • has_sidetable_rc 引用计数器是否过大无法存储在isa中。如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
  • extra_rc 里面存储的值是引用计数器减1。

3、Class的结构

struct objc_class {
    Class isa;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable, 方法缓存
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags, 用于获取具体的类信息
    
    class_rw_t *data() { 
        return bits.data();
    }
};

class_rw_t

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;           //编译期的只读信息

    method_array_t methods;     //方法列表
    property_array_t properties;    //属性列表
    protocol_array_t protocols; //协议列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
};
  • class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容。
class_rw_t的method_array_t.jpg

class_ro_t

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize; //instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;      //类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  //成员变量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};
  • class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容。
class_ro_t的method_list_t.jpg

method_t

  • method_t是对方法\函数的封装
struct method_t {
    SEL name;               //函数名
    const char *types;  //编码 (返回类型、参数类型)
    IMP imp;                //指向函数的指针(函数地址)
};
  • IMP代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
  • SEL代表方法\函数名,一般叫做选择器,底层结构跟char *类似
    • 可以通过@selector()sel_registerName()获得
    • 可以通过sel_getName()NSStringFromSelector()转成字符串
    • 不同类中相同名字的方法,所对应的方法选择器是相同的
typedef struct objc_selector *SEL;
  • types包含了函数返回值、参数编码的字符串
返回值 参数1 参数2 ...... 参数n
  • Type Encoding
    iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码

方法缓存

  • Class内部结构中有个方法缓存cache_t,用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度。
struct cache_t {
    struct bucket_t *_buckets;  //散列表
    mask_t _mask;                   //散列表的长度减1
    mask_t _occupied;               //已经缓存的方法数量
};

struct bucket_t {
private:
    cache_key_t _key;   //SEL作为Key
    IMP _imp;               //函数的内存地址
};
  • 缓存查找
//objc-cache.mm
bucket_t * cache_t::find(cache_key_t k, id receiver)

4、objc_msgSend执行流程

  • OC中的方法调用,其实都是转换为objc_msgSend函数的调用
  • objc_msgSend的执行流程可以分为3大阶段
  • 消息发送
  • 动态方法解析
  • 消息转发
  • objc_msgSend源码执行流程
//objc-msg-arm64.s
ENTRY _objc_msgSend
b.le    LNilOrTagged
CacheLookup NORMAL
.macro CacheLookup
.macro CheckMiss
STATIC_ENTRY __objc_msgSend_uncached
.macro MethodTableLookup
__class_lookupMethodAndLoadCache3

STATIC_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward


//objc-runtime-new.mm
_class_lookupMethodAndLoadCache3
lookUpImpOrForward
getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
_class_resolveInstanceMethod
_objc_msgForward_impcache

//Core Foundation
__forwarding__(不开源)

4.1 objc_msgSend 消息发送流程

objc_msgSend执行流程01-消息发送.jpg
  • 如果是从class_rw_t中查找方法
    已经排序的,二分查找
    没有排序的,遍历查找
  • receiver通过isa指针找到receiverClass
  • receiverClass通过superclass指针找到superClass

4.2 objc_msgSend 动态方法解析

objc_msgSend执行流程02-动态方法解析.jpg
  • 可以实现以下方法,来动态添加方法实现
+resolveInstanceMethod:
+resolveInstanceMethod:
  • 动态解析过后,会重新走“消息发送”的流程
    从receiverClass的cache中查找方法这一步开始执行

动态添加类方法

@interface Person : NSObject
+ (void)test;
@end

@implementation Person

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Class metaClass = object_getClass(self);//元类
        Method method = class_getClassMethod(metaClass, @selector(other));
        // 动态添加test方法的实现
        class_addMethod(metaClass, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveClassMethod:sel];
}

+ (void)other {
    NSLog(@"%s", __func__);
}
@end

动态添加实例方法

@interface Person : NSObject
- (void)test;
@end

@implementation Person

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        Class class = self;//Class类
        Method method = class_getInstanceMethod(class, @selector(other));
        // 动态添加test方法的实现
        class_addMethod(class, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));
        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (void)other {
    NSLog(@"%s", __func__);
}

@end

4.3 objc_msgSend 消息转发

objc_msgSend的执行流程03-消息转发.jpg
  • 可以在forwardInvocation:方法中自定义任何逻辑
  • 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)

forwardingTargetForSelector

实例方法

@interface Cat : NSObject
- (void)test;
@end

@implementation Cat
- (void)test {
    NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
- (void)test;
@end

@implementation Person
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

类方法

@interface Cat : NSObject
+ (void)test;
@end

@implementation Cat
+ (void)test {
    NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
+ (void)test;
@end

@implementation Person
+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [Cat class];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

forwardInvocation

实例方法

@interface Cat : NSObject
- (void)test;
@end

@implementation Cat
- (void)test {
    NSLog(@"%s", __func__);
}
@end

@interface Dog : NSObject
- (void)test;
@end

@implementation Dog
- (void)test {
    NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
- (void)test;
@end

@implementation Person

// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

/*
 NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
 anInvocation.target 方法调用者
 anInvocation.selector 方法名
 [anInvocation getArgument:NULL atIndex:0]
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
    [anInvocation invokeWithTarget:[[Dog alloc] init]];
}
@end

类方法

@interface Cat : NSObject
+ (void)test;
@end

@implementation Cat
+ (void)test {
    NSLog(@"%s", __func__);
}
@end

@interface Dog : NSObject
+ (void)test;
@end

@implementation Dog
+ (void)test {
    NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
+ (void)test;
@end

@implementation Person

// 方法签名:返回值类型、参数类型
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation invokeWithTarget:[Cat class]];
    [anInvocation invokeWithTarget:[Dog class]];
}
@end

4.4 降低doesNotRecognizeSelector崩溃

@interface Person : NSObject
- (void)run;
- (void)test;
- (void)other;
@end
@implementation Person

- (void)run {
    NSLog(@"run-123");
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 本来能调用的方法
    if ([self respondsToSelector:aSelector]) {
        return [super methodSignatureForSelector:aSelector];
    }
    
    // 找不到的方法
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

// 找不到的方法,都会来到这里
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        [person run];
        [person test];
        [person other];
    }
    return 0;
}
找不到方法输出.jpg

写一个NSObject的分类解决找不到方法问题


@implementation NSObject (ForwardInvocation)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = self;
        Method method1 = class_getInstanceMethod(cls, @selector(methodSignatureForSelector:));
        Method method2 = class_getInstanceMethod(cls, @selector(fi_methodSignatureForSelector:));
        method_exchangeImplementations(method1, method2);
        
        Method method3 = class_getClassMethod(cls, @selector(methodSignatureForSelector:));
        Method method4 = class_getClassMethod(cls, @selector(fi_methodSignatureForSelector:));
        method_exchangeImplementations(method3, method4);
    });
}

- (NSMethodSignature *)fi_methodSignatureForSelector:(SEL)aSelector {
    if ([self respondsToSelector:aSelector]) {
        return [self fi_methodSignatureForSelector:aSelector];
    }
    
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

+ (NSMethodSignature *)fi_methodSignatureForSelector:(SEL)aSelector {
    if ([self respondsToSelector:aSelector]) {
        return [self fi_methodSignatureForSelector:aSelector];
    }
    
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}

@end

5、super的本质

  • super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
    • struct objc_super2
    • SEL
struct objc_super2 {
    id receiver;
    Class current_class;
};
  • receiver是消息接收者
  • current_classreceiverClass对象
@interface Person : NSObject
@end

@implementation Person
@end

@interface Student : Person
@end

@implementation Student

- (instancetype)init {
    if (self = [super init]) {
        NSLog(@"[self class] = %@", [self class]);
        NSLog(@"[self superclass] = %@", [self superclass]);
        NSLog(@"[super class] = %@", [super class]);
        NSLog(@"[super superclass] = %@", [super superclass]);
    }
    return self;
}

@end
superClass输出.jpg

看objc源码

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}

+ (Class)superclass {
    return self->superclass;
}

- (Class)superclass {
    return [self class]->superclass;
}

struct objc_super {
    __unsafe_unretained _Nonnull id receiver; // 消息接收者
    __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};
  • [super message]的底层实现
    1. 消息接收者仍然是子类对象
    2. 从父类开始查找方法的实现
//[super run]转化的源码
struct __rw_objc_super { 
    struct objc_object *object; 
    struct objc_object *superClass;
};

static void _I_Student_run(Student * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
}

简化下相当于

struct __rw_objc_super arg = {self, [Person class]};
objc_msgSendSuper(arg, @selector(run));

[super class]输出Student的原因
[super superclass]输出Person的原因,

  • [super class]中的class方法只在NSObject才有。
  • 调用的是object_getClass(self); ,
  • self实际上是Student对象,对应Student类
  • 同理[super superclass]---> [self class]->superclass, 对应Person类

6、isKindOfClass、isMemberOfClass方法

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 这句代码的方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES
        NSLog(@"%d", [NSObject isKindOfClass:[NSObject class]]);   // 1
        NSLog(@"%d", [NSObject isMemberOfClass:[NSObject class]]); // 0
        NSLog(@"%d", [Person isKindOfClass:[Person class]]);       // 0
        NSLog(@"%d", [Person isMemberOfClass:[Person class]]);     // 0
    }
    return 0;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
isa-superclass.png

7、Runtime应用

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

8、练习

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)print;
@end

@implementation Person
- (void)print {
    NSLog(@"name = %@", self.name);
}
@end
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    id cls = [Person class];
    void *obj = &cls;
    [(__bridge id)obj print];
}
@end

调用输出


练习输出.jpg

1、为什么print能打印成功

  • obj存储的是cla的地址,cls存储的地址是[Person class]的地址。
  • 与实例对象调用类似,person存储的是isa的地址,通过isa找到[Person class],调用print方法。所以能调用成功
Person *person = [[Person alloc] init];
[person print];
cls指向问题.jpg

2、输出为什么变为ViewController或者其他的

[super viewDidLoad];

转化为C++函数,其实有个临时的self变量

struct abc = {
    self,
    [ViewController class]
};
objc_msgSendSuper2(abc, sel_registerName("viewDidLoad"));

viewDidLoad方法各个临时变量在函数中的内存地址是在栈中的,栈的地址是从高到低的,地址分布如下:

栈地址.jpg

cls相当于isa, 其下面一个地址就是_name的地址,所以会输出ViewController

你可能感兴趣的:(Objective-C基础-Runtime)