本篇文章将一些runtime函数进行一下汇总说明:
一、iOS消息机制
//iOS内部都是通过消息机制实现各个功能的
//最好的体现就是通过指令
`clang -rewrite-objc`实现objc到c++的转换,然后就可以看到里面的代码:
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
//runtime - c++
- (void)runtime_clangCode {
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
p = objc_msgSend(p, sel_registerName("init"));
objc_msgSend(p, @selector(eat));
}
//上面代码等同于
Person *p = [[Person alloc]init];
`
二、函数交换 < method_exchangeImplementations >
Method Swizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzling代码写到任何地方,但是只有在这段Method Swilzzling代码执行完毕之后互换才起作用。
我们应该在那个地方实现函数的IMP交换呢?
没错,load函数,它是先于
main
函数执行的,因此在这里执行可以确保函数在调用之前,函数的实现部分已经完成交换;如下:
+ (void)load {
Method methodOld = class_getClassMethod([self class], @selector(URLWithString:));
Method methodNew = class_getClassMethod([self class], @selector(hook_URLWithString:));
method_exchangeImplementations(methodOld, methodNew);
}
对于NSArray、NSMutableArray、NSDictionary、NSMutableDictionary又不一样,具体见图:
三、添加函数 < class_addMethod >
当我们调用一个不存在的函数式就会报异常,这时我们可以通过添加一个错误处理函数来进行处理:
//解决类方法
+ (BOOL)resolveClassMethod:(SEL)sel {
class_addMethod([self class], sel, (IMP)haha, "V:@");
return [super resolveClassMethod:sel];
}
//解决实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
class_addMethod([self class], sel, (IMP)haha, "V:@");
return [super resolveInstanceMethod:sel];
}
void haha () {
NSLog(@"访问了一个不存在的函数!");
}
四、获取属性及成员变量
//获取成员变量
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *keyChar = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"type :%s",type);
NSString *key = [NSString stringWithCString:keyChar encoding:NSUTF8StringEncoding];
id value = [self valueForKey:key];
//获取属性
unsigned int count;
objc_property_t *props = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
objc_property_t prop = props[i];
const char *proChar = property_getName(prop);
NSString *key = [NSString stringWithCString:proChar encoding:NSUTF8StringEncoding];
id value = [self valueForKey:key];
}
五、归档
普通数据可以进行归档处理,如果一个对象需要实现NSCoding协议才可以进行归档操作;
下面说一个简单的归档(假设我们有一个Person类)
//1、实现NSCoding协议
#import "Person.h"
#import <objc/message.h>
@interface Person()<NSCoding>
@end
@implementation Person
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *keyChar = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"type :%s",type);
NSString *key = [NSString stringWithCString:keyChar encoding:NSUTF8StringEncoding];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *keyChar = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"type :%s",type);
NSString *key = [NSString stringWithCString:keyChar encoding:NSUTF8StringEncoding];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
}
@end
//2、设置一个归档路径
//获取归档路径
- (NSString *)path {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
path = [path stringByAppendingPathComponent:@"p.arc"];
return path;
}
//3、归档实现
//遍历属性、归档
- (void)runtime_archive {
Person *p = [[Person alloc]init];
p.name = @"li";
p.age = 18;
[NSKeyedArchiver archiveRootObject:_p toFile:[self path]];
}
//4、解归档
- (void)runtime_unArchive {
Person *p = [NSKeyedUnarchiver unarchiveObjectWithFile:[self path]];
NSLog(@"person--:%@",p);
}