RunTime

2022-04-28  本文已影响0人  代码歌

类(objc_class)

OC中的类由Class表示,其实是一个指向objc_class结构体的指针:

typedef struct objc_class *Class;

objc_class的结构:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;             //isa指针

#if !__OBJC2__
    Class _Nullable super_class                            //父类的指针
    const char * _Nonnull name                             //类名
    long version                                            
    long info                                               
    long instance_size                                      
    struct objc_ivar_list * _Nullable ivars               //属性列表
    struct objc_method_list * _Nullable * _Nullable methodLists       //方法列表             
    struct objc_cache * _Nonnull cache                   //方法缓存    
    struct objc_protocol_list * _Nullable protocols      //协议列表
#endif

}

如上所示:结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为【类对象】,类对象在编译期产生用于创建实例对象,是个单例。

实例(objc_object)

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

实例中的isa指针指向类对象

元类(Meta Class)

Q:由上我们知道,类也是个对象,叫做类对象。那么类对象和类方法是从哪创建的呢?
A:就是类中的isa指针指向的结构体,叫元类。元类中保存了创建类对象和类方法的所有信息

Method(objc_method)

//方法
struct objc_method {
    SEL method_name                                          //方法名
    char *method_types                                       //方法类型
    IMP method_imp                                           //方法实现
}
1、SEL(objc_selector)

SEL是selector在Objective-C中的表示类型,selector其实是一个string

2、IMP:指向最终实现程序的内存地址的指针

Runtime中,Method通过selector和IMP两个属性,实现了快速查询方法及实现,相对提高了性能,又保持了灵活性。

类缓存(objc_cache)

iOS消息传递是怎么实现的呢?

  1. 系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
  2. 在它的类中【遍历】查找method_list,是否有selector方法。
  3. 没有则查找父类的method_list。
  4. 找到对应的method,执行它的IMP。

Runtime应用

关联对象(Objective-C Associated Objects):给分类增加属性
#import "ViewController.h"
#import "objc/runtime.h"

@interface UIView (DefaultColor)

@property (nonatomic, strong) UIColor *defaultColor;

@end

@implementation UIView (DefaultColor)

@dynamic defaultColor;

static char kDefaultColorKey;

- (void)setDefaultColor:(UIColor *)defaultColor {
    objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)defaultColor {
    return objc_getAssociatedObject(self, &kDefaultColorKey);
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIView *test = [UIView new];
    test.defaultColor = [UIColor blackColor];
    NSLog(@"%@", test.defaultColor);
}

@end
方法交换(Method Swizzling)
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);

从上面说的 Method 我们知道,Method中包含 Selector 方法名和 IMP 指向方法具体实现地址的指针,当执行 method_exchangeImplementations 方法时,将 A 方法名和 B 方法名对应的 指向具体实现的IMP 指针交换。

KVO实现实现

当观察 a 对象的一个属性时,系统会动态创建一个 a 当前类的子类。并将isa指针指向这个子类 B。通过重写 B 类中被观察属性的 setter 方法实现监听。具体实现如下。

- (void)setName:(NSString *)newName { 
      [self willChangeValueForKey:@"name"];    //KVO 在调用存取方法之前总调用 
      [super setValue:newName forKey:@"name"]; //调用父类的存取方法 
      [self didChangeValueForKey:@"name"];     //KVO 在调用存取方法之后总调用
}
实现字典和模型的自动转换(MJExtension)
#import "NSObject+RunTime.h"
#import <objc/runtime.h>

@implementation NSObject (RunTime)

//步骤一:回去类的属性列表,获取属性的 KEY,加到数组里
+ (NSArray *)or_objcProperties{
    //runtime可以获取以下列表
    //Ivar:成员变量  Property:属性  Method:方法  Protocol:协议
    unsigned int count = 0;
    objc_property_t *proList = class_copyPropertyList([self class], &count);
    //创建数组
    NSMutableArray *arrayM = [NSMutableArray array];
    // 遍历所有的属性
    for (unsigned int i = 0; i < count; i++) {
        // 1. 从数组中取得属性
        /**
         C 语言的结构体指针,通常不需要 `*`
         */
        objc_property_t pty = proList[i];
        
        // 2. 从 pty 中获得属性的名称
        const char *cName = property_getName(pty);
        
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        // 3. 属性名称添加到数组
        [arrayM addObject:name];
    }
    // 释放数组
    free(proList);
    return arrayM.copy;
}

//步骤二:遍历传入的字典,如果步骤一中的数组包含字典的 key,则对象执行 setValue for key
+ (instancetype)or_objWithDic:(NSDictionary *)dict {
    id object = [[self alloc] init];
    NSArray *proList = [self or_objcProperties];
    //遍历传入字典
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        //判断key是否在proList中
        if ([proList containsObject:key]) {
            [object setValue:obj forKey:key];
        }
    }];
    
    return object;
}

@end
上一篇 下一篇

猜你喜欢

热点阅读