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