ios runtime 二
2016-12-30 本文已影响23人
piao152
(本文部分内容参考:http://www.jianshu.com/p/927c8384855a,文/兴宇(简书作者))
OC的动态特性表现为了三个方面:
-动态类型
-动态关联
-动态加载
一、动态类型
简单来说就是在编译时不确定具体对象类型,在运行时动态匹配,这个跟继承、多态不同,简单来说就是id类型的定义使用,id类型用于隐藏对象类型的类名部分,相当于C语言中的“void *”,这个概念容易理解,不多做记录。
动态类型识别常用方法
- (BOOL)isKindOfClass:classObj //是否是classObj类或其子类**
- (BOOL)isMemberOfClass:classObj //是否是classObj的实例**
- (BOOL)respondsTosSelector:selector //类中是否有这个方法**
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; //是否遵守协议Protocol
- (BOOL)isProxy; //是否代理
二、动态关联
1、动态关联方法
借助IOS的方法调用机制(发消息),OC可以先跳过编译,到运行的时候才动态地添加函数调用,在运行时才决定要调用什么方法,需要传什么参数进去。这就是动态绑定,要实现他就必须用SEL变量绑定一个方法。最终形成的这个SEL变量就代表一个方法的引用。SEL并不是C里面的函数指针,SEL变量只是一个整数,他是该方法的ID
形式1---从外部隐式调用一个不存在的方法:
//隐式调用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"];
形式2---对象内部动态添加方法:
//方法定义
void runAddMethod(id self, SEL _cmd, NSString *string){
NSLog(@"add C IMP ", string);
}
//调用:
//给本类动态添加一个方法
SEL sel;
class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
其中class_addMethod
的四个参数分别是:
1、Class cls 给哪个类添加方法,本例中是self
2、SEL name 添加的方法名称
3、IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;
获得方法的实现
4、"v@:*"方法的签名,代表有一个参数的方法
另外附上:替换方法定义、交换两个方法实现、设置一个方法的实现摘自简书:http://www.jianshu.com/p/927c8384855a,文/兴宇,谨慎使用
#import "UIViewController+swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (swizzling)
//load方法会在类第一次加载的时候被调用
//调用的时间比较靠前,适合在这个方法里做方法交换
+ (void)load{
//方法交换应该被保证,在程序中只会执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//获得viewController的生命周期方法的selector
SEL systemSel = @selector(viewWillAppear:);
//自己实现的将要被交换的方法的selector
SEL swizzSel = @selector(swiz_viewWillAppear:);
//两个方法的Method
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,说明类中不存在这个方法的实现
//将被交换方法的实现替换到这个并不存在的实现
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
//否则,交换两个方法的实现
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)swiz_viewWillAppear:(BOOL)animated{
//这时候调用自己,看起来像是死循环
//但是其实自己的实现已经被替换了
[self swiz_viewWillAppear:animated];
NSLog(@"swizzle");
}
@end
PS:当调用一个不存在的方法时(含未实现),系统会在崩溃之前调用系统预设的拦截方法,也就是说,采用动态绑定方法时可以重写NSObject的拦截方法,防止此类崩溃。
+ (BOOL)resolveClassMethod:(SEL)sel; //当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。
+ (BOOL)resolveInstanceMethod:(SEL)sel; //同上,实例方法的处理
- (id)forwardingTargetForSelector:(SEL)aSelector; //将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
- (void)forwardInvocation:(NSInvocation *)anInvocation; //将你调用的不存在的方法打包成`NSInvocation`传给你。做完你自己的处理后,调用`invokeWithTarget:`方法让某个target触发这个方法
2、动态关联对象
//首先定义一个全局变量,用它的地址作为关联对象的key
static char associatedObjectKey;
//设置关联对象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性",OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//获取关联对象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
NSLog(@"AssociatedObject = %@", string);
使用方式举例
const char JDPaySingleObjectDictionary;
- (void)sendObject:(id)object
{
[self sendObject:object withIdentifier:@"JDPaySingleObjectDictionary"];
}
- (void)sendObject:(id)object withIdentifier:(NSString *)identifier
{
NSAssert(identifier != nil, @"identifier can't be nil.");
NSMutableDictionary *eventHandlerDictionary = objc_getAssociatedObject(self,&JDPaySingleObjectDictionary);
if (eventHandlerDictionary == nil) {
return;
}
void(^block)(id object) = [eventHandlerDictionary objectForKey:identifier];
if (block!=NULL)
{
block(object);
[eventHandlerDictionary removeObjectForKey:identifier];
return;
}
}
- (void)receiveObject:(void(^)(id object))sendObject
{
[self receiveObject:sendObject withIdentifier:@"JDPaySingleObjectDictionary"];
}
- (void)receiveObject:(void(^)(id object))sendObject withIdentifier:(NSString *)identifier
{
NSAssert(identifier != nil, @"identifier can't be nil.");
NSMutableDictionary *eventHandlerDictionary = objc_getAssociatedObject(self,&JDPaySingleObjectDictionary);
if (eventHandlerDictionary == nil) {
eventHandlerDictionary = [[NSMutableDictionary alloc] init];
objc_setAssociatedObject(self, &JDPaySingleObjectDictionary, eventHandlerDictionary, OBJC_ASSOCIATION_RETAIN);
}
[eventHandlerDictionary setObject:sendObject forKey:identifier];
}
- (void)receiveObjectSuccess:(void (^)(id successObj))sendObject withSuccessId:(NSString *)successId andFailed:(void (^)(id errorObj))errorObject WithFailedId:(NSString *)failedId
{
NSAssert(successId != nil, @"successId can't be nil.");
NSAssert(failedId != nil, @"failedId can't be nil.");
[self receiveObject:sendObject withIdentifier:successId];
[self receiveObject:errorObject withIdentifier:failedId];
}
三、动态加载(动态创建新类)
使用OC提供的与runtime相关的函数,动态的创建一个新的类
举例说明:
-(void)viewDidLoad
{
[super viewDidLoad];
//创建一个名为CustomView的类,它是UIView的子类
Class newClass = objc_allocateClassPair([UIView class], "CustomView", 0);
//为该类增加一个名为 report的方法,具体实现是reportFunction方法
class_addMethod(newClass, @selector(report), (IMP)reportFunction, "v@:");
//注册该类
objc_registerClassPair(newClass);
}
void reportFunction(id self, SEL _cmd)
{
NSLog(@"This object is %p, and class is %@ and super class is %@", self, [self class], [self superclass]);
}