原文:http://www.jianshu.com/p/e071206103a4
前言
本篇主要介绍Runtime在开发中的一些使用场景,顺便讲解了下MJExtension的底层实现。如果喜欢我的文章,可以关注我微博:袁峥Seemygo,也可以来小码哥,了解下我们的iOS培训课程。后续还会更新更多内容。。。
一、runtime简介
RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。事实证明:
在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。在编译阶段,C语言调用未实现的函数就会报错。
二、runtime作用
1.发送消息
方法调用的本质,就是让对象发送消息。objc_msgSend,只有对象才能发送消息,因此以objc开头.使用消息机制前提,必须导入#import <objc/message.h>消息机制简单使用
Person *p = [[Person alloc] init];
[p eat];
objc_msgSend(p,
@selector(eat));
[Person eat];
[[Person
class] eat];
objc_msgSend([Person
class], @selector(eat));
消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现
Snip20151013_4.png
2.交换方法
开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。方式一:继承系统的类,重写方法.方式二:使用runtime,交换方法.
@implementation ViewController
- (
void)viewDidLoad {
[
super viewDidLoad];
UIImage *image = [
UIImage imageNamed:
@"123"];
}
@end
@implementation UIImage (Image)
+ (
void)load
{
Method imageWithName = class_getClassMethod(
self,
@selector(imageWithName:));
Method imageName = class_getClassMethod(
self,
@selector(imageNamed:));
method_exchangeImplementations(imageWithName, imageName);
}
+ (
instancetype)imageWithName:(
NSString *)name
{
UIImage *image = [
self imageWithName:name];
if (image ==
nil) {
NSLog(
@"加载空的图片");
}
return image;
}
@end
交换原理:
交换之前:
Snip20151013_2.png
交换之后:
Snip20151013_3.png
3.动态添加方法
开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。简单使用
@implementation ViewController
- (
void)viewDidLoad {
[
super viewDidLoad];
Person *p = [[Person alloc] init];
[p performSelector:
@selector(eat)];
}
@end
@implementation Person
void eat(
id self,SEL sel)
{
NSLog(
@"%@ %@",
self,
NSStringFromSelector(sel));
}
+ (
BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel ==
@selector(eat)) {
class_addMethod(
self,
@selector(eat), eat,
"v@:");
}
return [
super resolveInstanceMethod:sel];
}
@end
4.给分类添加属性
原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
@implementation ViewController
- (
void)viewDidLoad {
[
super viewDidLoad];
NSObject *objc = [[
NSObject alloc] init];
objc.name =
@"小码哥";
NSLog(
@"%@",objc.name);
}
@end
static const char *key =
"name";
@implementation NSObject (Property)
- (
NSString *)name
{
return objc_getAssociatedObject(
self, key);
}
- (
void)setName:(
NSString *)name
{
objc_setAssociatedObject(
self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
5.字典转模型
设计模型:字典转模型的第一步
模型属性,通常需要跟字典中的key一一对应问题:一个一个的生成模型属性,很慢?需求:能不能自动根据一个字典,生成对应的属性。解决:提供一个分类,专门根据字典生成对应的属性字符串。
@implementation NSObject (Log)
+ (
void)resolveDict:(
NSDictionary *)dict{
NSMutableString *strM = [
NSMutableString string];
[dict enumerateKeysAndObjectsUsingBlock:^(
id _Nonnull key,
id _Nonnull obj,
BOOL * _Nonnull stop) {
NSString *type;
if ([obj isKindOfClass:
NSClassFromString(
@"__NSCFString")]) {
type =
@"NSString";
}
else if ([obj isKindOfClass:
NSClassFromString(
@"__NSCFArray")]){
type =
@"NSArray";
}
else if ([obj isKindOfClass:
NSClassFromString(
@"__NSCFNumber")]){
type =
@"int";
}
else if ([obj isKindOfClass:
NSClassFromString(
@"__NSCFDictionary")]){
type =
@"NSDictionary";
}
NSString *str;
if ([type containsString:
@"NS"]) {
str = [
NSString stringWithFormat:
@"@property (nonatomic, strong) %@ *%@;",type,key];
}
else{
str = [
NSString stringWithFormat:
@"@property (nonatomic, assign) %@ %@;",type,key];
}
[strM appendFormat:
@"\n%@\n",str];
}];
NSLog(
@"%@",strM);
}
@end
字典转模型的方式一:KVC
@implementation
Status
+ (instancetype)statusWithDict:(NSDictionary *)dict
{
Status *
status = [[self alloc] init];
[
status setValuesForKeysWithDictionary:dict];
return status;
}
@
end
KVC字典转模型弊端:必须保证,模型中的属性和字典中的key一一对应。
如果不一致,就会调用[<Status 0x7fa74b545d60> setValue:forUndefinedKey:] 报key找不到的错。分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖, 就能继续使用KVC,字典转模型了。
- (
void)
setValue:(id)value forUndefinedKey:(NSString *)
key
{
}
字典转模型的方式二:Runtime
思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。
@implementation ViewController
- (
void)viewDidLoad {
[
super viewDidLoad];
NSString *filePath = [[
NSBundle mainBundle] pathForResource:
@"status.plist" ofType:
nil];
NSDictionary *statusDict = [
NSDictionary dictionaryWithContentsOfFile:filePath];
NSArray *dictArr = statusDict[
@"statuses"];
_statuses = [
NSMutableArray array];
for (
NSDictionary *dict
in dictArr) {
Status *status = [Status modelWithDict:dict];
[_statuses addObject:status];
}
NSLog(
@"%@ %@",_statuses,[_statuses[
0] user]);
}
@end
@implementation NSObject (Model)
+ (
instancetype)modelWithDict:(
NSDictionary *)dict
{
id objc = [[
self alloc] init];
unsigned int count;
Ivar *ivarList = class_copyIvarList(
self, &count);
for (
int i =
0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *name = [
NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *key = [name substringFromIndex:
1];
id value = dict[key];
if ([value isKindOfClass:[
NSDictionary class]]) {
NSString *type = [
NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
NSRange range = [type rangeOfString:
@"\""];
type = [type substringFromIndex:range.location + range.length];
range = [type rangeOfString:
@"\""];
type = [type substringToIndex:range.location];
Class modelClass =
NSClassFromString(type);
if (modelClass) {
value = [modelClass modelWithDict:value];
}
}
if ([value isKindOfClass:[
NSArray class]]) {
if ([
self respondsToSelector:
@selector(arrayContainModelClass)]) {
id idSelf =
self;
NSString *type = [idSelf arrayContainModelClass][key];
Class classModel =
NSClassFromString(type);
NSMutableArray *arrM = [
NSMutableArray array];
for (
NSDictionary *dict
in value) {
id model = [classModel modelWithDict:dict];
[arrM addObject:model];
}
value = arrM;
}
}
if (value) {
[objc setValue:value forKey:key];
}
}
return objc;
}
@end
招贤纳士
转载请注明原文地址: https://ju.6miu.com/read-3406.html