ios runtime
来源:互联网 发布:什么是5g网络 编辑:程序博客网 时间:2024/06/05 18:07
1.背景知识
1.1 OC的方法调用流程
下面以实例对象调用方法[blackDog walk]为例描述方法调用的流程
1、编译器会把`[blackDog walk]`转化为`objc_msgSend(blackDog,SEL)`,SEL为@selector(walk)。2、Runtime会在blackDog对象所对应的Dog类的方法缓存列表里查找方法的SEL3、如果没有找到,则在Dog类的方法分发表查找方法的SEL。(类由对象isa指针指向,方法分发表即methodList)4、如果没有找到,则在其父类(设Dog类的父类为Animal类)的方法分发表里查找方法的SEL(父类由类的superClass指向)5、如果没有找到,则沿继承体系继续下去,最终到达NSObject类。6、如果在234的其中一步中找到,则定位了方法实现的入口,执行具体实现7、如果最后还是没有找到,会面临两种情况:``(1) 如果是使用`[blackDog walk]`的方式调用方法````(2) 使用`[blackDog performSelector:@selector(walk)]`的方式调用方法`
1.2 消息转发流程
1、动态方法解析接收到未知消息时(假设blackDog的walk方法尚未实现),runtime会调用+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方法)2、备用接收者如果以上方法没有做处理,runtime会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。如果该方法返回了一个非nil(也不能是self)的对象,而且该对象实现了这个方法,那么这个对象就成了消息的接收者,消息就被分发到该对象。适用情况:通常在对象内部使用,让内部的另外一个对象处理消息,在外面看起来就像是该对象处理了消息。比如:blackDog让女朋友whiteDog来接收这个消息3、完整消息转发在- (void)forwardInvocation:(NSInvocation *)anInvocation方法中选择转发消息的对象,其中anInvocation对象封装了未知消息的所有细节,并保留调用结果发送到原始调用者。比如:blackDog将消息完整转发給主人dogOwner来处理
2.1 json->model
原理描述:用runtime提供的函数遍历Model自身所有属性,如果属性在json中有对应的值,则将其赋值。
核心方法:在NSObject的分类中添加方法
- (instancetype)initWithDict:(NSDictionary *)dict { if (self = [self init]) { //(1)获取类的属性及属性对应的类型 NSMutableArray * keys = [NSMutableArray array]; NSMutableArray * attributes = [NSMutableArray array]; /* * 例子 * name = value3 attribute = T@"NSString",C,N,V_value3 * name = value4 attribute = T^i,N,V_value4 */ 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]; //通过property_getName函数获得属性的名字 NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; [keys addObject:propertyName]; //通过property_getAttributes函数可以获得属性的名字和@encode编码 NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; [attributes addObject:propertyAttribute]; } //立即释放properties指向的内存 free(properties); //(2)根据类型给属性赋值 for (NSString * key in keys) { if ([dict valueForKey:key] == nil) continue; [self setValue:[dict valueForKey:key] forKey:key]; } } return self;}
2.2 一键序列化
原理描述:用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。
核心方法:在Model的基类中重写方法:
- (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { unsigned int outCount; Ivar * ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i ++) { Ivar ivar = ivars[i]; NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)]; [self setValue:[aDecoder decodeObjectForKey:key] forKey:key]; } } return self;}- (void)encodeWithCoder:(NSCoder *)aCoder { unsigned int outCount; Ivar * ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i ++) { Ivar ivar = ivars[i]; NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)]; [aCoder encodeObject:[self valueForKey:key] forKey:key]; }}
2.3 访问私有变量
我们知道,OC中没有真正意义上的私有变量和方法,要让成员变量私有,要放在m文件中声明,不对外暴露。如果我们知道这个成员变量的名称,可以通过runtime获取成员变量,再通过getIvar来获取它的值。方法:
Ivar ivar = class_getInstanceVariable([Model class], "_str1");NSString * str1 = object_getIvar(model, ivar);
3. 关联对象
如何給NSArray添加一个属性(不能使用继承):点击打开链接 OC的分类允许给分类添加属性,但不会自动生成getter、setter方法
所以常规的仅仅添加之后,调用的话会crash
runtime如何关联对象
//关联对象void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)//获取关联的对象id objc_getAssociatedObject(id object, const void *key)//移除关联的对象void objc_removeAssociatedObjects(id object)
应用,如何关联:
- (void)setCustomTabbar:(UIView *)customTabbar { //这里使用方法的指针地址作为唯一的key objc_setAssociatedObject(self, @selector(customTabbar), customTabbar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (UIView *)customTabbar { return objc_getAssociatedObject(self, @selector(customTabbar));}
4.Method Swizzling
method Swizzling原理
每个类都维护一个方法(Method)列表,Method则包含SEL和其对应IMP的信息,方法交换做的事情就是把SEL和IMP的对应关系断开,并和新的IMP生成对应关系
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class selfClass = object_getClass([self class]); SEL oriSEL = @selector(imageNamed:); Method oriMethod = class_getInstanceMethod(selfClass, oriSEL); SEL cusSEL = @selector(myImageNamed:); Method cusMethod = class_getInstanceMethod(selfClass, cusSEL); BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod)); if (addSucc) { class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod)); }else { method_exchangeImplementations(oriMethod, cusMethod); } });}
在iOS7中如果viewdidappear还没有完成,就立刻执行push或者pop操作会crash决方案就是利用method swizzing, 将系统的viewdidappear替换为自己重写的sofaViewDidAppear
@implementation UIViewController (APSafeTransition)+ (void)load{ Method m1; Method m2; m1 = class_getInstanceMethod(self, @selector(sofaViewDidAppear:)); m2 = class_getInstanceMethod(self, @selector(viewDidAppear:)); method_exchangeImplementations(m1, m2);}
|
阅读全文
0 0
- [runtime] iOS-Runtime-Headers
- iOS runtime
- iOS Runtime
- iOS-runtime
- iOS Runtime
- IOS Runtime
- IOS - RunTime
- ios runtime
- ios runtime
- ios runtime
- iOS Runtime
- iOS runtime
- ios runtime
- iOS Runtime
- iOS - Runtime
- iOS--Runtime
- iOS runtime
- ios runtime
- 局部内部类访问final局部变量
- Java之String、StringBuffer、StringBulider辨识
- 面试题:如何测试上传和下载?
- HashSet和HashMap
- Android SDK 2.2 开发环境搭建
- ios runtime
- Spark Streaming实战对论坛网站动态行为pv,uv,注册人数,跳出率的多维度分析
- EXCEL教程-根据身份证号得到年龄性别出生日期等信息
- Java基础知识储备
- PAT乙级1020. 月饼 (25)
- Android应用程序架构
- MBR样本分析
- js判断选择时间是否大于当前实际时间
- Android各版本区别总结