iOS Runtime => 计算机原理 => 应用
在iOS开发中我们会用到的语言有《Objective-C》及 《swift》这两门语言。现在我们团队已经渐渐的从Objective-C转向Swift。由于产品已经有长远的历史了,不可能花上几个月的时间重新编写,所以我们采用的是混编的方式来解决这个问题,在新功能迭代时我们采用的是Swift开发。在维护旧功能时还是依旧使用Objective-C.虽然Swift并没有Runtime机制,但是只要继续用上Objective-C就有有可能会用到,所以这就是总结这篇文章的原因。Objective-C是一门动态性语言,通过Runtime机制实现运行时机制。说到这里,首先抛出一个问题,程序是如何运行跑起来的。如果不太清楚的话,先停下来思考几分钟再继续往下看。
程序是如何跑起来的
在我们目前编写的代码基本都是高级语言,计算机并不能识别这些语言。那从我们编写的语言是怎么转换为机器能识别的语言的,机器识别的语言又是怎么执行的?我们把这个思维调换一下方向,我们通过计算机为什么要这样做一步一步推倒回去。这样理解可能才是最有效的方式。
人们都知道计算机只能识别0跟1,并不像人类所熟知的逢十进一,这是因为计算机的是使用二极管的方式,当通电时标识1,不通电标识0,现在的电脑普遍都是支持64位的,是因为继电器是64个二极管组成,同时能处理64个0跟1。只要我们把代码转化为0跟1那就可以让计算机识别。那我们就明白来了
程序运行起来 < 机器识别语言(目标语言) < ( ???????? ) < 编程语言
那我们现在就差一个问题,编程语言是如何装换为机器识别语言(目标语言)的?编程语言分底层语言,高阶(高级)语言,但是在一开始的时候并没有这样去分,刚开始人们想要让机器跑起来的编程语言就只有汇编语言,但由于编写汇编语言的人需要非常专业的工程师才能去编写,对比普通人来说门槛实在是太高了,聪(懒)明(惰)的人类想出了一个办法。编写一些让普通人能理解的语法,他们只负责调用就好,怎么实现他们不用管,这样就能大大降低了编程难度,也提高了编程效率。
程序运行起来 < 机器识别语言(目标语言) < ( 汇编语言 ) < 编程语言
现在我们来总结一下 :
1.编写高级语言
2.通过高级语言一步一步的转化为底层语言(汇编语言)
3.通过底层语言(汇编语言)编译为机器识别语言(目标语言)
4.程序跑起来了
Runtime机制
在Objective-C方法调用时,内部实现是通过消息发送的方式,编译器转换为objc_msgSend(receiver, selector)的函数调用。那为什么既然苹果已经封装的很好,尽量不使用C语言开发,那为什么这个Runtime会受到iOS开发者关注呢?那是因为Objective-C的特性,在编译时,程序只关注方法(函数)有没有声明,并不关注方法有没有实现,等到执行时才会加载(方法)函数内部的指令.通过该特性来实现iOS黑魔法(Method Swizzling)。
通过这个特性可以实现的功能有:
1.需要获取对象的所有属性及方法
2.因为系统框架的达不到你要的效果,你需要对该框架进行修改。
获取对象的所有属性及方法 , 实现字典转模型( sample 1.1 )
# sample 1.1
@interface Person : NSObject
@property (nonatomic,copy )NSString* name;
@property (nonatomic,copy )NSString* address;
@property (nonatomic,copy )NSString* telephoneNumber;
- (void)modelWithDictionary:(NSDictionary*)dictionary;
@end
@implementation Person
- (void)modelWithDictionary:(NSDictionary*)dictionary {
unsigned int outCount;
// 获取对象有多少个属性
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
//便利所有的属性
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
// 获取该属性的属性名
NSString* key = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
// 如果该属性在字典里有值就赋值给该对象
if (dictionary[key]) {
[self setValue:dictionary[key] forKey:key];
}
}
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary* dict = @{@"name":@"Lee Lei",@"telephoneNumber":@"10086",@"address":@"ShenZhen Guangdong China"};
Person* p = [[Person alloc]init];
[p modelWithDictionary:dict];
}
@end
获取对象的所有属性及方法 , 实现NSCoding的自动归档和自动解档( sample 1.2 )
# sample 1.2 注* 需要遵循NSCoding协议
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int outCount;
// 获取对象有多少个属性
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
//便利所有的属性
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
// 获取该属性的属性名
NSString* key = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
[aCoder decodeObjectForKey:key];
}
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
unsigned int outCount;
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
//便利所有的属性
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
// 获取该属性的属性名
NSString* key = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
[aDecoder encodeObject:[self valueForKey:key] forKey:key];
}
}
return self;
}
因为系统框架的达不到你要的效果,当我们的URL包含有中文转为NSURL时会转换出错返回nil。我们需要对该字符串进行一个UTF8(encode)转码( sample 2.1 )
# sample 2.1
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL* url = [NSURL URLWithString:@"https://wwww.baidu.com?a=哈喽"];
NSLog(@"%@",url);
}
@end
#import "NSURL+Runtime.h"
#import <objc/runtime.h>
@implementation NSURL (Runtime)
+ (void)load {
Method beforeMethod = class_getClassMethod([self class], @selector(URLWithString:));
Method afterMethod = class_getClassMethod([self class], @selector(ll_URLWithString:));
method_exchangeImplementations(beforeMethod, afterMethod);
}
+ (nullable instancetype)ll_URLWithString:(NSString *)URLString {
URLString = [URLString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
return [self ll_URLWithString:URLString];
}
@end