iOS 开发 Objective-C iOS常用

iOS 底层 day16 runtime的使用 runtimeA

2020-09-08  本文已影响0人  望穿秋水小作坊

一、什么是 Runtime?平时项目中有用过么?

1. 什么是 Runtime?
2. 平时项目中有用过么(四点)?

二、RuntimeAPI 的具体使用例子

1. 使用 objc_setAssociatedObject 关联对象技术,动态为分类添加属性
2. 字典转模型简化版的实现
#import "NSObject+YYJsonToModel.h"
#import <objc/runtime.h>
@implementation NSObject (YYJsonToModel)
-(void)yy_modelFromJson:(NSDictionary *)dic {
    unsigned int ivarCount;
    Ivar *ivars = class_copyIvarList([self class], &ivarCount);
    for (int i = 0 ; i < ivarCount; i++) {
        NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivars[i])];
        if ([name hasPrefix:@"_"]) {
            name = [[name substringFromIndex:1] mutableCopy];
        }
        [self setValue:dic[name] forKey:name];
    }
    free(ivars);
}
@end
int main(int argc, const char * argv[]) {
    NSDictionary *dic = @{
        @"name":@"LYY",
        @"age":@10,
        @"height":@170
    };
    YYPerson *person = [[YYPerson alloc] init];
    [person yy_modelFromJson:dic];
    NSLog(@"断点");
    return 0;
}
3. 自动归档、自动解档(NSSecureCoding)
#import "YYPerson.h"
#import <objc/runtime.h>
@implementation YYPerson
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
    if (self = [super init]) {
        Class cls = self.class;
        // 截取类和父类的成员变量
        while (cls && cls != [NSObject class]) {
            unsigned int ivarCount = 0;
            Ivar *ivars = class_copyIvarList(cls, &ivarCount);
            for (int i = 0; i < ivarCount; i++) {
                NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
                id value = [coder decodeObjectForKey:key];
                if (value) { // 增加容错
                    [self setValue:value forKey:key];
                 }
            }
            // 获得c的父类
            cls = [cls superclass];
            free(ivars);
        }
    }
    return self;
}

- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    Class cls = self.class;
    // 截取类和父类的成员变量
    while (cls && cls != [NSObject class]) {
        unsigned int ivarCount = 0;
        Ivar *ivars = class_copyIvarList(cls, &ivarCount);
        for (int i = 0; i < ivarCount; i++) {
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
            id value = [self valueForKey:key];
            if (value) { // 增加容错
                [coder encodeObject:value forKey:key];
            }
        }
        cls = [cls superclass];
        // 释放内存
        free(ivars);
    }
}
+ (BOOL)supportsSecureCoding{
    return YES;
}
@end
int main(int argc, const char * argv[]) {
    YYPerson *person = [[YYPerson alloc] init];
    person.name = @"LYY";
    person.age = 10;
    person.height = 180;
    NSLog(@"person: %@",person);
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:nil];
    NSLog(@"data: %@",data);
    
    YYPerson *decodePerson = [NSKeyedUnarchiver unarchivedObjectOfClass:[YYPerson class] fromData:data error:nil];
    NSLog(@"decodePerson: %@",decodePerson);
    
    return 0;
}

三、RuntimeAPI(常用)

1. RuntimeAPI - 类
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name, 
                       size_t extraBytes) 
void objc_registerClassPair(Class _Nonnull cls)
Class object_getClass(id _Nullable obj) 
Class object_setClass(id _Nullable obj, Class _Nonnull cls) 
BOOL object_isClass(id _Nullable obj)
BOOL class_isMetaClass(id _Nullable obj)
Class class_getSuperclass(Class _Nullable cls) 
2. RuntimeAPI - 成员变量
Ivar class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
Ivar *class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value) 
id object_getIvar(id _Nullable obj, Ivar _Nonnull ivar) 
BOOL class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size, 
              uint8_t alignment, const char * _Nullable types) 
const char * ivar_getName(Ivar _Nonnull v) 
const char * ivar_getTypeEncoding(Ivar _Nonnull v) 
3. RuntimeAPI - 属性
objc_property_t *class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
                  const objc_property_attribute_t * _Nullable attributes,
                  unsigned int attributeCount)
4. RuntimeAPI - 方法
Ivar class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name)
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) 
IMP method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) 
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types)
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                    const char * _Nullable types) 
SEL method_getName(Method _Nonnull m)
IMP method_getImplementation(Method _Nonnull m)
const char * method_getTypeEncoding(Method _Nonnull m)
const char * _Nonnull sel_getName(SEL _Nonnull sel)
SEL sel_registerName(const char * _Nonnull str)
IMP imp_implementationWithBlock(id _Nonnull block)
id imp_getBlock(IMP _Nonnull anImp)
BOOL imp_removeBlock(IMP _Nonnull anImp)

四、RuntimeAPI 的一些练习

1. 动态创建一个 YYCat 类,为它添加 nameweight 两个成员变量。再添加一个 print 方法,然后创建实例,分别为 nameweight 赋值再将它们打印出来,调用方法 print
int main(int argc, const char * argv[]) {
    Class YYCat = objc_allocateClassPair([NSObject class], "YYCat", 0);
    class_addIvar(YYCat, "_name", 8, 1, @encode(NSString*));
    class_addIvar(YYCat, "_age", 4, 1, @encode(int));
    IMP catRunImp = imp_implementationWithBlock(^{
        printf("cat run run: %s\n", __func__);
    });
    class_addMethod(YYCat, @selector(run), catRunImp, "v");
    objc_registerClassPair(YYCat);
    
    id cat = class_createInstance(YYCat, 0);
    [cat setValue:[NSString stringWithFormat:@"Mimi"] forKey:@"_name"];
    [cat setValue:@3 forKey:@"_age"];
    [cat run];
    NSLog(@"cat name is %@, age is %d", [cat valueForKey:@"_name"], [[cat valueForKey:@"_age"] intValue]);
    NSLog(@"断点观察");
    return 0;
}
2. 将 YYPersonrun 的方法实现 和 YYDogeat 的方法实现 进行交换
int main(int argc, const char * argv[]) {
    YYPerson *person = [[YYPerson alloc] init];
    YYDog *dog = [[YYDog alloc] init];
    Method personRun = class_getInstanceMethod([YYPerson class], @selector(run));
    Method dogEat = class_getInstanceMethod([YYDog class], @selector(eat));
    method_exchangeImplementations(personRun, dogEat);
    [person run];
    [dog eat];
    return 0;
}

四、源码和本质追踪的时候的一些思路

1. 我们在研究OC 底层实现的时候,一般有三种思路
2. 遗留问题:思考用 object_setIvar 会触发 KVO 吗?
上一篇 下一篇

猜你喜欢

热点阅读