iOS Sharing 系列
目录
1、类方法去哪里找?
2、isa指针有几种类型么?
3、分类的方法具体是在什么时候添加到类的方法列表中?
4、class_addMethod()都需要什么参数?
5、iOS消息转发流程
1、类方法去哪里找?
答: 见上一期《iOS Sharing #01 | 2019-03-23》第5问2、isa指针有几种类型么?
答: isa指针分,指针类型和非指针类型,32位只做地址保存,非嵌入式64位架构下,包含除类地址外的其他信息。3、分类的方法具体是在什么时候添加到类的方法列表中?
答: 类在编译后会以 class_ro_t 的结构把类的信息存储在 bits 里,运行时的 realizeClass 之后,会把 ro 中的所有信息拷贝到 bits 的 data 内,即以 class_rw_t 的形式存在,分类里的方法即在这个时候添加到类的方法表里,并在方法表数组的最前面4、class_addMethod()都需要什么参数?
答:/**
* Adds a new method to a class with a given name and implementation.
*
* @param cls The class to which to add a method.
* @param name A selector that specifies the name of the method being added.
* @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
* @param types An array of characters that describe the types of the arguments to the method.
*
* @return YES if the method was added successfully, otherwise NO
* (for example, the class already contains a method implementation with that name).
*
* @note class_addMethod will add an override of a superclass's implementation,
* but will not replace an existing implementation in this class.
* To change an existing implementation, use method_setImplementation.
*/
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
- 给类添加一个新的方法和该方法的具体实现
- BOOL: 返回值,YES -------方法添加成功 NO --------方法添加失败
- Class cls: 将要给添加方法的类,传的类型 [类名 class]
- SEL name: 将要添加的方法名,传的类型 @selector(方法名)
- IMP imp:实现这个方法的函数 ,传的类型
- 1、C语言写法:(IMP)方法名
- 2、OC的写法:class_getMethodImplementation(self,@selector(方法名:))
- const char *types:表示我们要添加的方法的返回值和参数
- "v@:@":
- 'v'是添加方法无返回值
- '@'表示是id(也就是要添加的类)
- ':'表示添加的方法类型
- '@'表示参数类型
const char *types含义表:
Code | Meaning |
---|---|
c | A char |
i | An int |
s | A short |
l | A long l is treated as a 32-bit quantity on 64-bit programs. |
q | A long long |
C | An unsigned char |
I | An unsigned int |
S | An unsigned short |
L | An unsigned long |
Q | An unsigned long long |
f | A float |
d | A double |
B | A C++ bool or a C99 _Bool |
v | A void |
* | A character string (char *) |
@ | An object (whether statically typed or typed id) |
# | A class object (Class) |
: | A method selector (SEL) |
[array type] | An array |
{name=type...} | A structure |
(name=type...) | A union |
bnum | A bit field of num bits |
^type | A pointer to type |
? | An unknown type (among other things, this code is used for function pointers) |
注意:
用这个方法添加的方法是无法直接调用的,必须用performSelector:调用。
因为performSelector是运行时系统负责去找方法的,在编译时候不做任何校验;如果直接调用编译是会自动校验。
添加方法是在运行时添加的,你在编译的时候还没有这个本类方法,所以当然不行。
5、iOS消息转发流程
(1)、答案示例
消息转发机制基本分为三个步骤:- 1、动态方法解析。调用动态解析方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
,如果动态添加方法class_addMethod(self, sel, (IMP)dynamicAddMethodIMP, "@@:");
并返回YES,则结束流程; - 2、备用接受者。如果上一步没有实现动态添加方法,会调用消息接受者重定向
- (id)forwardingTargetForSelector:(SEL)selector
方法,如果返回重定向接受者,则当前流程结束; - 3、完整转发。如果上一步返回nil,则会调用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
获取函数的参数和返回值类型,同时会调用- (void)forwardInvocation:(NSInvocation *)anInvocation
消息通知当前对象; - 4、App crash。如果上一步方法签名返回nil,消息无法处理,提示
doesNotRecognizeSelector
,App crash。
(2)、代码示例
(a)、部分代码
- 1、动态方法解析
//实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
printf("[1️⃣] %s 未实现. \n", NSStringFromSelector(sel).UTF8String);
if (sel == @selector(nonExistentMethod)) { //如果是要响应这个方法,那么动态添加一个方法进去
class_addMethod(self, sel, (IMP)dynamicAddMethodIMP, "@@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
//类方法
//+ (BOOL)resolveClassMethod:(SEL)sel {
// printf("[1️⃣] %s 未实现. \n", NSStringFromSelector(sel).UTF8String);
// if (sel == @selector(nonExistentMethod)) {
// class_addMethod(self, sel, (IMP)dynamicAddMethodIMP, "@@:");
// return YES;
// }
// return [super resolveInstanceMethod:sel];
//}
id dynamicAddMethodIMP(id self, SEL _cmd) {
printf("[✅] 调用了动态添加的方法: %s. \n", __FUNCTION__);
return @"YES!";
}
- 2、备用接受者
- (id)forwardingTargetForSelector:(SEL)aSelector {
printf("[2️⃣] 转发给其它对象去响应 %s方法.\n", NSStringFromSelector(aSelector).UTF8String);
_fastFowarding = [FastFowarding new];
if ([_fastFowarding respondsToSelector:@selector(nonExistentMethod)]) { //如果FastFowarding对象能响应这个方法,那就让该对象去处理
return _fastFowarding;
}
return [super forwardingTargetForSelector:aSelector];
}
- 3、完整转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
printf("[3️⃣] 对方法进行签名: %s.\n", NSStringFromSelector(aSelector).UTF8String);
if (aSelector == @selector(nonExistentMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
printf("[4️⃣] 转发此次调用: %s.\n", NSStringFromSelector(anInvocation.selector).UTF8String);
if (anInvocation.selector == @selector(nonExistentMethod)) {
_normalForwarding = [NormalForwarding new];
[anInvocation invokeWithTarget:_normalForwarding];
}
}
(b)、完整代码
(3)、参考图片
类方法:
实例方法:
详细流程:
感谢大佬提供的图片。
仓库
联系方式
邮箱: adrenine@163.com
邮箱: holaux@gmail.com