![](https://img.haomeiwen.com/i1276164/79463d9ce679a314.jpg)
I - 归档
你还在一个一个的setValue: forKey:
吗?? 更好的办法就是我们使用runtime来实现归档,这个就不必多说了,相信都用过,这里就抛砖引玉记录一个我使用过例子。
场景:
1. 这里有一个SPPerson类有一些属性,将来对这个类的实例属性做归档
2. 遵循NSSecureCoding协议,在.m文件中实现协议方法
@interface SPPerson : NSObject<NSSecureCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) NSInteger age;
@end
- (void)encodeWithCoder:(NSCoder *)coder {
unsigned int count = 0;
Ivar* ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar var = ivars[i];
const char* name = ivar_getName(var);
NSString* key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[coder encodeObject:value forKey:key];
}
free(ivars);
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar var = ivars[i];
const char *name = ivar_getName(var);
NSString *key = [NSString stringWithUTF8String:name];
id value = [coder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
+ (BOOL)supportsSecureCoding {
return YES;
}
- 实现一个SPArchiveTool的归档工具类,提供归档和解档类方法
@interface SPArchiveTool : NSObject
+ (BOOL)sp_archiveObject:(id)object prefix:(NSString *)prefix;
+ (id)sp_unarchiveClass:(Class)class prefix:(NSString *)prefix;
@end
+ (BOOL)sp_archiveObject:(id)object prefix:(NSString *)prefix {
if (!object) {
return NO;
}
NSError *error;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:&error];
if (error) {
return NO;
}
[data writeToFile:[SPArchiveTool getPathWithPrefix:prefix] atomically:YES];
return YES;
}
+ (id)sp_unarchiveClass:(Class)class prefix:(NSString *)prefix {
NSError *error;
NSData *data = [[NSData alloc] initWithContentsOfFile:[SPArchiveTool getPathWithPrefix:prefix]];
if (!data) {
return nil;
}
// 会调用对象的 initWithCoder方法
id content = [NSKeyedUnarchiver unarchivedObjectOfClass:class fromData:data error:&error];
if (error) {
return nil;
}
return content;
}
// 存放的文件路径
+ (NSString *)getPathWithPrefix:(NSString *)prefix {
NSError *error;
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePathFolder = [documentPath stringsByAppendingPaths:@[@"archiveTemp"]].firstObject;
if (![[NSFileManager defaultManager] fileExistsAtPath:filePathFolder]) {
[[NSFileManager defaultManager] createDirectoryAtPath:filePathFolder withIntermediateDirectories:YES attributes:nil error:&error];
}
NSString *path = [NSString stringWithFormat:@"%@/%@.plist", filePathFolder, prefix];
return path;
}
- 测试
SPPerson *p = [[SPPerson alloc] init];
p.name = @"spirej";
p.nickName = @"sp";
p.age = 18;
// 归档
[SPArchiveTool sp_archiveObject:p prefix:NSStringFromClass(SPPerson.class)];
// 解档
[SPArchiveTool sp_unarchiveClass:SPPerson.class prefix:NSStringFromClass(SPPerson.class)];
NSLog(@"name = %@, nickName = %@, age = %ld", p.name, p.nickName, (long)p.age);
/*
打印结果
name = spirej, nickName = sp, age = 18
*/
II - 动态创建类
场景:
- 动态创建一个SPCat类,并测试添加实例变量,添加方法,创建实例对象并对实例变量赋值,调用方法等
void hunting(id self, SEL _cmd) {
NSLog(@"%@ - %s", self, __func__);
}
- (void)createClassTest {
// 创建一类对
Class SPCat = objc_allocateClassPair([NSObject class], "SPCat", 0);
// 添加实例变量
NSString *name = @"spCat";
class_addIvar(SPCat, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
// 添加方法
class_addMethod(SPCat, @selector(hunting), (IMP)hunting, "v@:");
// 注册类
objc_registerClassPair(SPCat);
// 创建实例对象
id cat = [[SPCat alloc] init];
[cat setValue:@"Tom" forKey:name];
NSLog(@"name = %@", [cat valueForKey:name]);
// 方法调用
[cat performSelector:@selector(hunting)];
}
- 打印结果
2019-11-06 02:31:46.433386+0800 runtime应用[22760:2231475] name = Tom
2019-11-06 02:31:46.434725+0800 runtime应用[22760:2231475] <SPCat: 0x6000023444d0> - hunting
III - 动态创建类(动态创建不存在的控制器)
场景:
- 我们将要在控制器间跳转,有红绿蓝三个vc,其中红绿vc已经在项目中创建过的,蓝vc是没有的,那么我们需要点击按钮也可以跳转到想要的蓝色vc,怎么做呢?答案依然是通过runtime底层动态创建
废话不多说,直接上代码
self.dataArray = @[@{@"class":@"SPRedViewController",
@"data":@{@"name":@"spirej-18",
@"backgroundColor":@"red"}},
@{@"class":@"SPGreenViewController",
@"data":@{@"slogan":@"和谐学习,不急不躁",
@"backgroundColor":@"green"}},
@{@"class":@"SPBlueViewController",
@"data":@{@"ending":@"我就是我,颜色不一样的烟火",
@"backgroundColor":@"blue"}}];
- (IBAction)goRedVC:(UIButton *)sender {
[self pushToAnyVCWithData:self.dataArray[0]];
}
- (IBAction)goGreenVC:(UIButton *)sender {
[self pushToAnyVCWithData:self.dataArray[1]];
}
- (IBAction)goBlueVC:(UIButton *)sender {
[self pushToAnyVCWithData:self.dataArray[2]];
}
- (void)pushToAnyVCWithData:(NSDictionary *)dataDic {
const char *clsName = [dataDic[@"class"] UTF8String];
Class cls = objc_getClass(clsName);
// 1.创建类
if (!cls) {
Class superClass = [UIViewController class];
// 创建一类对
cls = objc_allocateClassPair(superClass, clsName, 0);
// 添加成员变量
class_addIvar(cls, "ending", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
class_addIvar(cls, "show_lb", sizeof(UILabel *), log2(sizeof(UILabel *)), @encode(UILabel *));
// 添加方法
Method method = class_getInstanceMethod([self class], @selector(sp_instanceMethod));
IMP methodIMP = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
BOOL rest = class_addMethod(cls, @selector(viewDidLoad), methodIMP, types);
NSLog(@"rest == %d", rest);
}
// 实例化对象
id instance = nil;
@try {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
instance = [sb instantiateViewControllerWithIdentifier:dataDic[@"class"]];
} @catch (NSException *exception) {
instance = [[cls alloc] init];
} @finally {
NSLog(@"OK");
}
NSDictionary *dict = dataDic[@"data"];
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// 检测是否存在key的属性
if (class_getProperty(cls, [key UTF8String])) {
[instance setValue:obj forKey:key];
}
// 检测是否存在key的变量
if (class_getInstanceVariable(cls, [key UTF8String])) {
[instance setValue:obj forKey:key];
}
}];
[self.navigationController pushViewController:instance animated:YES];
}
- (void)sp_instanceMethod {
[super viewDidLoad];
[self setValue:[UIColor blueColor] forKeyPath:@"view.backgroundColor"];
[self setValue:[[UILabel alloc] initWithFrame:CGRectMake(100, 200, 200, 30)] forKey:@"show_lb"];
UILabel *show_lb = [self valueForKey:@"show_lb"];
[[self valueForKey:@"view"] addSubview:show_lb];
show_lb.text = [self valueForKey:@"ending"];
show_lb.font = [UIFont systemFontOfSize:14];
show_lb.textColor = [UIColor blackColor];
show_lb.textAlignment = NSTextAlignmentCenter;
show_lb.backgroundColor = [UIColor whiteColor];
NSLog(@"hello word");
}
- 测试效果,发现成功跳转动态创建的vc
![](https://img.haomeiwen.com/i1276164/eec06d6e6939a0c9.gif)
网友评论