oc基础

iOS探究 --- Runtime的使用场景

2020-04-14  本文已影响0人  空空_Jiminy

一,字典转模型

经典的第三方库MJExtension
https://www.jianshu.com/p/9b0b7cbc1d3c

二,无侵入埋点(系统方法替换)

https://www.jianshu.com/p/735d45745d87

三,访问私有变量

两种方式
1.KVC
2.Runtime
这里只实现runtime

Person定义如下
Person.h

@interface Person : NSObject

- (void)printName;

@end

Person.m

@interface Person()

@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;


@end

@implementation Person

- (void)printName {
    NSLog(@"name === %@", self.name);
}

@end

ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [Person new];
    [self changePersonName:person name:@"newName"];
    [person printName];
}

- (void)changePersonName:(Person *)person name:(NSString *)name {
    
    unsigned int count = 0;
    
    Ivar *ivars = class_copyIvarList(objc_getClass("Person"), &count);
    
    for (int i = 0; i<count; i++) {
        Ivar ivar = ivars[I];
        NSString *ivarName = [[NSString alloc] initWithUTF8String:ivar_getName(ivar)];
        if ([ivarName isEqualToString:@"_name"]) {
            object_setIvar(person, ivar, name);
        }
    }
    
    free(ivars);
}

打印结果


image.png

四,category中添加属性

为刚才的Person类增加一个Category


image.png

我们发现,在.h文件里,是可以增加nickName属性。
但是当我们调用的时候会崩溃。原因是set和get并未实现。


image.png
这是就需要我们用到Runtime来添加set和get方法了。
- (void)setNickName:(NSString *)nickName {
    
    /*
     objc_AssociationPolicy参数使用的策略:
     OBJC_ASSOCIATION_ASSIGN;            //assign策略
     OBJC_ASSOCIATION_COPY_NONATOMIC;    //copy策略
     OBJC_ASSOCIATION_RETAIN_NONATOMIC;  // retain策略

     OBJC_ASSOCIATION_RETAIN;
     OBJC_ASSOCIATION_COPY;
     */
    /*
     关联方法:
     objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

     参数:
     * id object 给哪个对象的属性赋值
     const void *key 属性对应的key
     id value  设置属性值为value
     objc_AssociationPolicy policy  使用的策略,是一个枚举值,和copy,retain,assign是一样的,手机开发一般都选择NONATOMIC
     */
    
    objc_setAssociatedObject(self, "nickName", nickName, OBJC_ASSOCIATION_COPY_NONATOMIC);
    
}

- (NSString *)nickName {
    return objc_getAssociatedObject(self, "nickName");
}

调用

    Person *person = [Person new];
    person.nickName = @"haha";
    NSLog(@"nickName === %@", person.nickName);

打印

image.png

五,NSUserDefault存储自定义对象

由于NSUserDefault并不支持存储自定义对象,但是支持存储NSData。
那我们就先把对象转成NSData再进行存储。

先在Person类里实现- (instancetype)initWithCoder:(NSCoder *)aDecoder和- (void)encodeWithCoder:(NSCoder *)aCoder两个方法

//解档
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
     
        Class c = self.class;
        // 截取类和父类的成员变量
        while (c && c != [NSObject class]) {
            unsigned int count = 0;
            Ivar *ivars = class_copyIvarList(c, &count);
            for (int i = 0; i < count; i++) {
     
                NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
     
                id value = [aDecoder decodeObjectForKey:key];
     
                [self setValue:value forKey:key];
            }
            // 获得c的父类
            c = [c superclass];
            free(ivars);
        }
    }
    return self;
}
 
//归档
- (void)encodeWithCoder:(NSCoder *)aCoder{
    Class c = self.class;
    // 截取类和父类的成员变量
    while (c && c != [NSObject class]) {
        unsigned int count = 0;
     
        Ivar *ivars = class_copyIvarList(c, &count);
     
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
     
            id value = [self valueForKey:key];
     
            [aCoder encodeObject:value forKey:key];
        }
        c = [c superclass];
        // 释放内存
        free(ivars);
    }
}

调用

- (void)savePersonToUserDefault:(Person *)person {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person];
    if (data) {
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"PersonKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

- (Person *)fetchPerson {
    NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"PersonKey"];
    if (data) {
        Person *person = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        return person;
    }
    return nil;
}

六,NSCopy,NSMutableCopy

众所周知,当使用NSCopy和NSMutableCopy时,需要实现<NSCopying, NSMutableCopying>协议,一个一个写起来也是一件麻烦事,使用Runtime可以让这个简单不少。

- (id)mutableCopyWithZone:(nullable NSZone *)zone {
    id data = [[self.class allocWithZone:zone] init];
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(self.class, &count);
    for (int i = 0; i < count; i ++) {
        Ivar ivar = ivarList[i];
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        id value = [self valueForKey:name];
        [data setValue:value forKey:name];
    }
    return data;
}

- (id)copyWithZone:(nullable NSZone *)zone {
    id data = [[self.class allocWithZone:zone] init];
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(self.class, &count);
    for (int i = 0; i < count; i ++) {
        Ivar ivar = ivarList[i];
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        id value = [self valueForKey:name];
        [data setValue:value forKey:name];
    }
    return data;
}
上一篇下一篇

猜你喜欢

热点阅读