Objective-C:Runtime

objc_msgSend底层调用有3大阶段

1.消息发送

objc_msgSend执行流程01-消息发送.png

2.动态方法解析

objc_msgSend执行流程02-动态方法解析.png
void c_other(id self, SEL _cmd)
{
    NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}


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

+ (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));

// 第一个参数是object_getClass(self)
//        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");

        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

3.消息转发

objc_msgSend的执行流程03-消息转发.png
int __forwarding__(void *frameStackPointer, int isStret) {
    id receiver = *(id *)frameStackPointer;
    SEL sel = *(SEL *)(frameStackPointer + 8);
    const char *selName = sel_getName(sel);
    Class receiverClass = object_getClass(receiver);

  // 调用 forwardingTargetForSelector:
  if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) 
  {
      id forwardingTarget = [receiver forwardingTargetForSelector:sel];
      if (forwardingTarget && forwardingTarget != receiver) 
      {
          return objc_msgSend(forwardingTarget, sel, ...);
      }
  }

    // 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
    if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:)))
    {
        NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
        if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) 
        {
            NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];

            [receiver forwardInvocation:invocation];

            void *returnValue = NULL;
            [invocation getReturnValue:&value];
            return returnValue;
        }
    }

    if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:)))
    {
        [receiver doesNotRecognizeSelector:sel];
    }

    // The point of no return.
    kill(getpid(), 9);
}

一个具体的使用

/************************消息转发**************************/

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
        // objc_msgSend([[MJCat alloc] init], aSelector)
        return [[MJCat alloc] init]; //返回处理方法的调用对象
    }
    return [super forwardingTargetForSelector:aSelector];
}

/**************************上面代码没有实现时调用以下代码************************/

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

// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation invokeWithTarget:[[MJCat alloc] init]];
}

isa_t结构体

    struct {
        uintptr_t nonpointer        : 1;//0,代表普通的指针,存储着Class、Meta-Class对象的内存地址 1,代表优化过,使用位域存储更多的信息
        uintptr_t has_assoc         : 1;//是否有设置过关联对象,如果没有,释放时会更快
        uintptr_t has_cxx_dtor      : 1;//是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
        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;//引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
        uintptr_t extra_rc          : 19;//里面存储的值是引用计数器减1
    };
Class结构.png
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容

method包含:SEL name,types,方法的实现

method_t.png

types包含了函数返回值、参数编码的字符串

IMP代表函数的具体实现

SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似

1.可以通过@selector()和sel_registerName()获得

2.可以通过sel_getName()和NSStringFromSelector()转成字符串

3.不同类中相同名字的方法,所对应的方法选择器是相同的

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

hash表类似字典,一个key对应一个value,只不过hash表里面的数据根据key生成索引,能够快速的取出数据


方法缓存.png

面试题及具体应用:

讲一下 OC 的消息机制

1.OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
2.objc_msgSend底层有3大阶段:消息发送(当前类、父类中查找)、动态方法解析、消息转发

什么是Runtime?

1.OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
2.OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,源码由C\C++\汇编语言编写
3.平时编写的OC代码,底层都是转换成了Runtime API进行调用

平时在项目中的应用

1.利用关联对象(AssociatedObject)给分类添加属性


@implementation NSObject (Property)

- (NSString *)name
{
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, key);
}

- (void)setName:(NSString *)name
{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

2.遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)

2.1 、遍历UITextField的私有属性,修改placeholder的颜色
    unsigned int count;
    Ivar *ivars = class_copyIvarList([UITextField class], &count);
    for (int i = 0; i < count; i++) 
    {
        // 取出i位置的成员变量
        Ivar ivar = ivars[i];
        NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
    }
    free(ivars);
    
    self.textField.placeholder = @"请输入用户名";
    
    [self.textField setValue:[UIColor redColor]forKeyPath:@"_placeholderLabel.textColor"];
    
    UILabel *placeholderLabel = [self.textField valueForKeyPath:@"_placeholderLabel"];
    placeholderLabel.textColor = [UIColor redColor];
    
    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    attrs[NSForegroundColorAttributeName] = [UIColor redColor];
    self.textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:@"请输入用户名" attributes:attrs];
2.2、字典转模型
+ (instancetype)ly_objectWithJson:(NSDictionary *)json
{
    id obj = [[self alloc] init];
    
    unsigned int count;
    Ivar *ivars = class_copyIvarList(self, &count); //利用Runtime遍历所有的成员变量
    for (int i = 0; i < count; i++) {
        // 取出i位置的成员变量
        Ivar ivar = ivars[i];
        NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
        [name deleteCharactersInRange:NSMakeRange(0, 1)]; //去掉变量名前面的_
        
        // 设值
        id value = json[name];
        if ([name isEqualToString:@"ID"]) {
            value = json[@"id"];
        }
        [obj setValue:value forKey:name]; //利用KVC设值
    }
    free(ivars);
    return obj;
}
3.交换方法实现
      MJPerson *person = [[MJPerson alloc] init];
      Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
      Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
      method_exchangeImplementations(runMethod, testMethod);
      [person run];  //-[MJPerson test]

    //替换方法的实现
    class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
        NSLog(@"123123");
    }), "v");

+ (void)load
{
    // hook:钩子函数
    Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:));
    method_exchangeImplementations(method1, method2);
}
类簇,交换系统类的方法实现,NSString、NSArray、NSDictionary,真实类型是其他类型
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
        Class cls = NSClassFromString(@"__NSArrayM");
       //Class cls = NSClassFromString(@"__NSDictionaryM");  可变字典
       // Class cls2 = NSClassFromString(@"__NSDictionaryI"); 不可变字典
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}
4.利用消息转发机制解决方法找不到的异常问题

objc_msgSend消息发送,动态方法解析,消息转发,添加方法实现防止触发找不到方法的异常问题

5.创建一个类
    // 创建类
    Class newClass = objc_allocateClassPair([NSObject class], "MJDog", 0);
    class_addIvar(newClass, "_age", 4, 1, @encode(int));
    class_addIvar(newClass, "_weight", 4, 1, @encode(int));
    class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
    // 注册类
    objc_registerClassPair(newClass);
    
    MJPerson *person = [[MJPerson alloc] init];
    object_setClass(person, newClass);
    [person run];

    id dog = [[newClass alloc] init];
    [dog setValue:@10 forKey:@"_age"];
    [dog setValue:@20 forKey:@"_weight"];
    [dog run];

    NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
    
    // 在不需要这个类时释放
//    objc_disposeClassPair(newClass);

  // 获取成员变量信息
    Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
    NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));//_age i

  // 设置和获取成员变量的值
    Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");

    MJPerson *person = [[MJPerson alloc] init];
    object_setIvar(person, nameIvar, @"123");
    object_setIvar(person, ageIvar, (__bridge id)(void *)10);
    NSLog(@"%@ %d", person.name, person.age); //123 10
给对象设置对应的Class
    MJPerson *person = [[MJPerson alloc] init];
    [person run]; //[MJPerson run]
    
    object_setClass(person, [MJCar class]);
    [person run]; //[MJCar run]

Runtime API

1.类

Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)   //动态创建一个类(参数:父类,类名,额外的内存空间)

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

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

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

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

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

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

Class class_getSuperclass(Class cls)//获取父类

2.成员变量

Ivar class_getInstanceVariable(Class cls, const char *name)//获取一个实例变量信息

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

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)//获取成员变量的相关信息

3.属性

objc_property_t class_getProperty(Class cls, const char *name)//获取一个属性

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)//拷贝属性列表(最后需要调用free释放)

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)//获取属性的一些信息

4.方法

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) //交换方法实现

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

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

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

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) //获取方法的相关信息(带有copy的需要调用free去释放)

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

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

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

IMP imp_implementationWithBlock(id block)//用block作为方法实现

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

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


bucket_t、cache_t、method_t、class_ro_t、class_rw_t数据结构

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
    
    IMP imp(SEL selector)
    {
        mask_t begin = _mask & (long long)selector;
        mask_t i = begin;
        do {
            if (_buckets[i]._key == 0  ||  _buckets[i]._key == (long long)selector) {
                return _buckets[i]._imp;
            }
        } while ((i = cache_next(i, _mask)) != begin);
        return NULL;
    }
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

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;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // 方法列表
    property_list_t *properties;    // 属性列表
    const protocol_list_t * protocols;  // 协议列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

/* OC对象 */
struct objc_object {
    void *isa;
};

/* 类对象 */
struct objc_class : objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    objc_class* metaClass() {
        return (objc_class *)((long long)isa & ISA_MASK);
    }
};

/* OC对象 */
struct ly_objc_object {
    void *isa;
};

/* 类对象 */
struct ly_objc_class : ly_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    ly_objc_class* metaClass() {
        return (ly_objc_class *)((long long)isa & ISA_MASK);
    }
};

//由于方法缓存是存在hash表中,存入的下标为方法名&cache._mask
  bucket_t *buckets = cache._buckets;
  bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask];
  NSLog(@"%s %p", bucket._key, bucket._imp);
//遍历方法缓存中方法的key和实现        
  for (int i = 0; i <= cache._mask; i++) {
      bucket_t bucket = buckets[i];
      NSLog(@"%s %p", bucket._key, bucket._imp);
  }

[super message]的底层实现

1.消息接收者仍然是子类对象

2.从父类开始查找方法的实现

OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )


/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained Class class;
#else
    __unsafe_unretained Class super_class;
#endif
    /* super_class is the first class to search */
};


struct objc_super {
    __unsafe_unretained _Nonnull id receiver; // 消息接收者
    __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};

    // super调用的receiver仍然是MJStudent对象
    [super run];

    NSLog(@"[super class] = %@", [super class]); // MJStudent
    NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson


- (void)print
{
    [super print];
    
    objc_msgSendSuper(
    {
        self,
        [MJPerson class]
    }, sel_registerName("print"));
}


objc_msgSendSuper的工作原理应该是这样的:
从objc_super结构体指向的superClass父类的方法列表开始查找selector,找到后以objc->receiver去调用父类的这个selector。注意,最后的调用者是objc->receiver,而不是super_class!

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