在项目编码中最为常见的就是[object message];这种形式的消息发送,对于其他面向对象语言来说就是实例对象调用类中实现的实例方法;[object message]这种形式到底做了什么呢?
objc_object, objc_class 以及 objc_method
在Objective-C中类、对象、方法都是C语言中结构体类型;具体数据类型可以参照objc/objc.h
文件
// 类
struct objc_class {
Class isa;//指针,顾名思义,表示是一个什么,
//实例的isa指向类对象,类对象的isa指向元类
#if !__OBJC2__
Class super_class; //指向父类
const char *name; //类名
long version;
long info;
long instance_size
struct objc_ivar_list *ivars //成员变量列表
struct objc_method_list **methodLists; //方法列表
struct objc_cache *cache;//缓存一种优化,调用过的方法存入缓存列表,下次调用先找缓存
struct objc_protocol_list *protocols //协议列表
#endif
} OBJC2_UNAVAILABLE;
//对象
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
// 方法
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE; // 方法实现
}
消息的传递
在People
类中声明并实现walk
方法则消息会正常被传递
#import <Foundation/Foundation.h>
@interface HZPeople : NSObject
-(void)walk;
@end
#import "HZPeople.h"
#import <objc/objc-runtime.h>
@implementation HZPeople
-(void)walk{
NSLog(@"people walk!!");
}
控制台能正常打印 people walk!!
如果只声明并不是实现walk
方法则会调用+(BOOL)resolveInstanceMethod:(SEL)sel
;允许在此进行对类增加方法
#import <Foundation/Foundation.h>
@interface HZPeople : NSObject
-(void)walk;
@end
#import "HZPeople.h"
#import <objc/objc-runtime.h>
@implementation HZPeople
void anotherPeopleWalk(id obj ,SEL _cmd){
NSLog(@"anotherPeopleWalk!");
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"resolveInstanceMethod!");
if (sel == @selector(walk)) {
// 通过imp_implementationWithBlock执行新增方法事项
IMP fooIMP = imp_implementationWithBlock(^(id _self) {
NSLog(@"Doing foo");
});
// 给类动态添加执行方法
class_addMethod([self class], sel, fooIMP, "v@:");
// class_addMethod([self class], sel, (IMP)anotherPeopleWalk, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
控制台会先打印resolveInstanceMethod
然后打印Doing foo
+(BOOL)resolveInstanceMethod:(SEL)sel
中没有实现方法的新增则会调用-(id)forwardingTargetForSelector:(SEL)aSelector
允许对消息转发给其他对象
#import <Foundation/Foundation.h>
#import "HZMan.h"
@interface HZPeople : NSObject
@property(nonatomic,strong)HZMan* man;
-(void)walk;
@end
#import "HZPeople.h"
#import <objc/objc-runtime.h>
@implementation HZPeople
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(walk)) {
// 将消息转发给HZMan中的walk方法
return self.man;
}
return [super forwardingTargetForSelector:aSelector];
}
-(id)forwardingTargetForSelector:(SEL)aSelector
如果没有实现还有最后的机会进行一次消息转发;这个需要重写- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
为执行方法进行签名,最后在实现-(void)forwardInvocation:(NSInvocation *)anInvocation
完成一次消息的完整转发
#import <Foundation/Foundation.h>
#import "HZMan.h"
@interface HZPeople : NSObject
@property(nonatomic,strong)HZMan* man;
-(void)walk;
@end
#import "HZPeople.h"
#import <objc/objc-runtime.h>
@implementation HZPeople
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if(!signature){
signature = [_man methodSignatureForSelector:aSelector];
}
return signature;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
SEL sel = anInvocation.selector;
if ([self .man respondsToSelector:sel]) {
[anInvocation invokeWithTarget:self.man];
}else{
[self doesNotRecognizeSelector:sel];
}
}
如果最终也没有对消息进行处理就只能执行doesNotRecognizeSelector:sel
抛出异常了
几个概念
SEL
- SEL:选择器,是表示一个方法的selector的指针
- typedef struct objc_selector *SEL;
- Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL
- 本质上,SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法),它的存在只是为了加快方法的查询速度。
IMP
- IMP:实际上是一个函数指针,指向方法实现的首地址
- id (*IMP)(id, SEL, ...)
- 参数1:实例方法或者是类方法 分别代表类实例的内存地址或是指向原类的指针
Method
Method :表示类定义的方法