OC动态性
oc的动态性主要体现三个方面
-
动态类型:运行时确定对象的类型
-
动态绑定:运行时确定对象的调用方法
-
动态加载:运行时加载需要的资源或者可执行代码。
1 动态类型
- 动态时确定对象的类型指对象指针类型的动态性,如使用id类型接受对象
由赋予对象类型决定对象指针类型。可以通过isKindOfClass动态判断是什么类型(尽量使用静态类型,有错误提前编译器告知)
2 动态绑定
- OC是否调用方法不是在编译期决定的,方法的调用不和代码的绑定在一起,而是在运行时根据发出的具体消息而动态的确定需要调用的代码。
1 . 利用动态类型和动态绑定还可以实现动态改变消息的执行者和消息的接受者。
2 . 利用消息传递和消息转发机制 主要处理应对一些接收者无法处理的消息,有机会将消息转发给其他对象处理。
3 . 运行时可以动态添加方法和属性; 动态绑定是基于动态类型的,运行对象类型确定后,对象的属性和方法也就确定了,可以通过isa指针找到方法列表 和属性
3 动态加载
- 动态加载包括 动态资源加载 和代码块加载。例如 图片资源会根据@2x @3x动态选择加载
4 消息传递机制
- OC中 对象调用方法 应该理解为对象接受消息,消息的发送采用动态绑定机制,具体调用哪个方法 运行时才会确定,确定后才回去执行绑定代码。给一个对象传递消息表达式为 【receiver message】;接受者的类型可以通过动态类型识别在运行时确定 ;【receiver message】转义后的格式如下
///objc_msgSend("对象","SEL","参数"...)
objc_msgSend( id self, SEL op, ... )
- 有了这些参数,objc_msgSend就可以通过接受者的isa指针,到其类对象的方法列表中以SEL的名称为“键”寻找对应的方法,若找到则执行代码。否则从父类寻找,如果到了根类还没找到,说明该接受者无法响应该消息,那么会触发消息转发机制,给开发者 挽救程序崩溃的机会。
5消息转发机制
- 消息转发提供了3道防线,任何一个起作用都会挽救此次消息转发。按照先后顺序3道防线依次为
1 .动态补加方法的实现
+(BOOL)resolveInstanceMethod:(SEL)sel
+(BOOL)resolveClassMethod:(SEL)sel
2 .将消息发送给另一个对象去处理
-(id)forwardingTargetForSelector:(SEL)aSelector
3 . 手动生成方法签名并转发给另一个对象
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(void)forwardInvocation:(NSInvocation *)anInvocation
举例消息转发的过程
.首先创建Person 类 在.h里声明一个实例方法(这里以-(void)instanceMethod)为例子。然后.m不进行实现。在main 函数里初始化并且调用该方法。
//这是 Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)instanceMethod;
@end
///这是main.m
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * person = [[Person alloc]init];
[person instanceMethod];
}
return 0;
. 这时运行 由于方法没有实现 所以一定会触发消息转发机制
. 首先进入第一道防线Person.m文件会提供运行时的转发接应,实现 resolveInstanceMethod 为找不到的方法(-(void)instanceMethod)的实现进行补救。
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
//被动态添加的实例方法
void insMethod(id self,SEL _cmd)
{
NSLog(@"我收到消息了动态补加的");
}
//动态补加方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(instanceMethod)) {
class_addMethod(self, sel, (IMP)insMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
.运行
2019-10-25 11:28:23.777657+0800 OC 动态性[17788:1898041] 我收到消息了动态补加的
Program ended with exit code: 0
.如果没有实现 + (BOOL)resolveInstanceMethod:(SEL)sel 或者返回了NO 那么进入第二道防线 实现-(id)forwardingTargetForSelector:(SEL)aSelector 方法返回一个实例对象,让对象代替原对象处理这个消息
#import "Person.h"
#import "Person2.h"
#import <objc/runtime.h>
@implementation Person
-(id)forwardingTargetForSelector:(SEL)aSelector
{
//返回消息转发的实例 注意消息转发实例要和当前实例未实现的方法名一模一样。
if (aSelector==@selector(instanceMethod)) {
return [[Person2 alloc]init];
}
return nil;
}
///Person2.h 我是头文件
#import <Foundation/Foundation.h>
@interface Person2 : NSObject
-(void)instanceMethod;
@end
///Person2.m 我是实现文件
#import "Person2.h"
@implementation Person2
-(void)instanceMethod;
{
NSLog(@"我是替Person完成的消息,我收到了");
}
@end
运行
2019-10-25 11:36:58.230856+0800 OC 动态性[17884:1901837] 我是替Person完成的消息,我收到了
Program ended with exit code: 0
如果上面的两个补救方法都没有实现 或者forwardingTargetForSelector 返回了nil 那么进入最后一道防线,需要手动生成方法签名并实现forwardInvocation方法将消息转发给另一个对象类似第二道防线 下面看代码
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
//为指定的方法手动生成签名
NSString * selName = NSStringFromSelector(aSelector);
if ([selName isEqualToString:@"instanceMethod"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
//如果另外一个对象可以响应该消息,那么将消息转发给它
Person2 * person2 = [[Person2 alloc]init];
if ( [person2 respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:person2];
}
///Person2.h 我是头文件
#import <Foundation/Foundation.h>
@interface Person2 : NSObject
-(void)instanceMethod;
@end
///Person2.m 我是实现文件
#import "Person2.h"
@implementation Person2
-(void)instanceMethod;
{
NSLog(@"我是替Person完成的消息,我收到了");
}
@end
运行
2019-10-25 11:56:04.493146+0800 OC 动态性[18067:1910152] 我是替Person完成的消息,我收到了
Program ended with exit code: 0
网友评论