SEL
SEL
又叫选择器,是表示一个方法的selector的指针,其定义如下:
typedef struct objc_selector *SEL;
方法的selector
用于表示运行时方法的名字。Objective-C
在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL
。两个类之间,只要方法名相同,那么方法的SEL
就是一样的,每一个方法都对应着一个SEL
。所以在Objective-C
同一个类(及类的继承体系)中,不能存在2个同名的方法,即使参数类型不同也不行。
方法调用
[self testMethod:@"数据"];
[self performSelector:@selector(testMethod:) withObject:@"数据"];
// Runtime
objc_msgSend(self, @selector(testMethod:), @"数据");
iOS8之后报错
// 用函数指针,指向了runtime的objc_msgSend方法才能调用,不能直接调用objc_msgSend()
void (*runtime_msgSend)(id self, SEL _cmd, id param) = (void *)objc_msgSend;
runtime_msgSend(self.methodHelper, NSSelectorFromString(@"testMethod:"), @"hello world");
动态决议
对象在接收到未知的消息时,首先会调用所属类的类方法。我们为该未知消息新增一个“处理方法”,通过运行时class_addMethod
函数动态添加到类里面就可以了。
// 接收处理未知实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if ([NSStringFromSelector(sel) isEqualToString:@"testMethod:"]) {
if (!class_addMethod([self class], sel, (IMP)testMethodImplementation, "v@:@")
) {
NSLog(@"动态添加方法失败");
}
}
return [super resolveInstanceMethod:sel];
}
// 接收处理未知类方法
+ (BOOL)resolveClassMethod:(SEL)sel {
// 动态添加类方法
return [super resolveClassMethod:sel];
}
必须要有动态添加方法的实现
void testMethodImplementation(id self, SEL _cmd, id param) {
NSLog(@"这是动态创建的方法的实现");
}
适用场景:
使用关键字@dynamic
在类的实现文件中修饰一个属性,表明我们会为这个属性动态提供存取方法,编译器不会再默认为我们生成这个属性的setter
和getter
方法了,需要我们自己提供。
消息转发
- 第一种方式:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.methodHelper respondsToSelector:aSelector]) {
return self.methodHelper;
}
return [super forwardingTargetForSelector:aSelector];
}
- 第二种方式:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([self.methodHelper respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.methodHelper];
}
}
方法交换
当系统类的方法不能满足我们的需求,比如你想在所有的UIViewController
的viewWillAppear
里设置背景颜色,你可以写一个Base
类,然后去继承。你也可以选择Runtime
的方法交换。
- 第一步
导入ObjectiveC
@import ObjectiveC;
- 第二步
创建一个UIViewController的Category
,实现+ (void)load
方法
+ (void)load {
// class_getInstanceMethod 获取对象方法
// class_getClassMethod 获取类方法
// 获取系统的viewWillAppear方法
Method viewWillAppearMethod = class_getInstanceMethod([UIViewController class], @selector(viewWillAppear:));
// 获取自定义的bgViewWillAppear方法
Method bgViewWillAppearMethod = class_getInstanceMethod([UIViewController class], @selector(bgViewWillAppear:));
// 交换方法实现
method_exchangeImplementations(viewWillAppearMethod, bgViewWillAppearMethod);
}
- 第三步
实现自定义的方法,并调用(因为方法交换了,实际上调用的是系统的方法)
- (void)bgViewWillAppear:(BOOL)animated {
self.view.backgroundColor = [UIColor redColor];
[self bgViewWillAppear:animated];
}
网友评论