常用方法
Class 反射创建
通过字符串创建类:Class
// 方式1
NSClassFromString(@"NSObject");
// 方式2
objc_getClass("NSObject");
SEL 反射创建
通过字符串创建方法 selector
// 方式1
@selector(init);
// 方式2
sel_registerName("init");
// 方式3
NSSelectorFromString(@"init");
方法替换/交换
- 方法替换:
class_replaceMethod
- 方法交换:
method_exchangeImplementations
// 方法替换
- (void)methodReplace
{
Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA));
IMP impA = method_getImplementation(methodA);
class_replaceMethod(self.class, @selector(myMethodC), impA, method_getTypeEncoding(methodA));
// print: myMethodA
[self myMethodC];
}
// 方法交换
- (void)methodExchange
{
Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA));
Method methodB = class_getInstanceMethod(self.class, @selector(myMethodB));
method_exchangeImplementations(methodA, methodB);
// print: myMethodB
[self myMethodA];
// print: myMethodA
[self myMethodB];
}
- (void)myMethodA
{
NSLog(@"myMethodA");
}
- (void)myMethodB
{
NSLog(@"myMethodB");
}
- (void)myMethodC
{
NSLog(@"myMethodC");
}
新增类
通过字符串动态新增一个类
- 首先创建新类:
objc_allocateClassPair
- 然后注册新创建的类:
objc_registerClassPair
这里有个小知识点,为什么类创建的方法名是objc_allocateClassPair
,而不是objc_allocateClass
呢?这是因为它同时创建了一个类(class)和元类(metaclass)。关于元类可以看这篇文章:What is a meta-class in Objective-C?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self addNewClassPair];
Class MyObject = NSClassFromString(@"MyObject");
NSObject *myObj = [[MyObject alloc] init];
[myObj performSelector:@selector(sayHello)];
return YES;
}
- (void)addNewClassPair
{
Class myCls = objc_allocateClassPair([NSObject class], "MyObject", 0);
objc_registerClassPair(myCls);
[self addNewMethodWithClass:myCls];
}
新增方法
新增方法:class_addMethod
这里也有个小知识点,就是使用特定字符串描述方法返回值和参数,例如:v@:
。其具体映射关系请移步:Type Encodings
void sayHello(id self, SEL _cmd)
{
NSLog(@"%@ %s", self, __func__);
}
- (void)addNewMethodWithClass:(Class)targetClass
{
class_addMethod(targetClass, @selector(sayHello), (IMP)sayHello, "v@:");
}
消息转发
当给对象发送消息时,如果对象没有找到对应的方法实现,那么就会进入正常的消息转发流程。其主要流程如下:
// 1.运行时动态添加方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
// 2.快速转发
- (id)forwardingTargetForSelector:(SEL)aSelector
// 3.构建方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
// 4.消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
其中最后的forwardInvocation:
会传递一个NSInvocation
对象(Ps:NSInvocation 可以理解为是消息发送objc_msgSend(void id self, SEL op, ... )
的对象)。NSInvocation 包含了这个方法调用的所有信息:selector、参数类型、参数值和返回值类型。此外,你还可以去更改参数值和返回值。
除了上面的正常消息转发,我们还可以借助_objc_msgForward
方法让消息强制转发。
Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA));
IMP impA = method_getImplementation(methodA);
IMP msgForwardIMP = _objc_msgForward;
// 替换 myMethodA 的实现后,每次调用 myMethodA 都会进入消息转发
class_replaceMethod(self.class, @selector(myMethodC), msgForwardIMP, method_getTypeEncoding(methodA));
Method 调用方式
- 常规调用
- 反射调用
- objc_msgSend
- C 函数调用
- NSInvocation 调用
@interface People : NSObject
- (void)helloWorld;
@end
// 常规调用
People *people = [[People alloc] init];
[people helloWorld];
// 反射调用
Class cls = NSClassFromString(@"People");
id obj = [[cls alloc] init];
[obj performSelector:NSSelectorFromString(@"helloWorld")];
// objc_msgSend
((void(*)(id, SEL))objc_msgSend)(people, sel_registerName("helloWorld"));
// C 函数调用
Method initMethod = class_getInstanceMethod([People class], @selector(helloWorld));
IMP imp = method_getImplementation(initMethod);
((void (*) (id, SEL))imp)(people, @selector(helloWorld));
// NSInvocation 调用
NSMethodSignature *sig = [[People class] instanceMethodSignatureForSelector:sel_registerName("helloWorld")];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
invocation.target = people;
invocation.selector = sel_registerName("helloWorld");
[invocation invoke];
第五种 NSInvocation 调用
是热修复调用任意OC方法的核心基础。通过 NSInvocation 不但可以自定义函数的参数值和返回值,而且还可以自定义方法:selector
和消息接收对象:target
。因此,我们可以通过字符串的方式构建任意OC方法调用。
文档信息
- 本文作者:Yawei Wang
- 本文链接:https://pfcstyle.github.io/2017/01/07/iOS-oc-runtime-2/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)