SEL
- SEL方法選擇器,表示一個(gè)selector的指針
- 無(wú)論什么類里,只要方法名相同,SEL就相同。項(xiàng)目里的所有SEL都保存在一個(gè)NSSet集合里(NSSet集合里的元素不能重復(fù)),所以查找對(duì)應(yīng)方法,只要找到對(duì)應(yīng)的SEL就可以了。
- SEL實(shí)際是根據(jù)方法名hash化了的字符串
SEL sel_registerName(const char *str)//向runtime system注冊(cè)一個(gè)方法名。如果方法名已經(jīng)注冊(cè),則放回已經(jīng)注冊(cè)的SEL
SEL sel_getUid(const char *str)//同上
@selector(<#selector#>)//oc編譯器提供的
SEL NSSelectorFromString(NSString *aSelectorName)//OC字符串轉(zhuǎn)化
SEL method_getName ( Method m );//根據(jù)Method結(jié)構(gòu)體獲取
等等
SEL的操作函數(shù)
// 比較兩個(gè)選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
//判斷方法名是否映射到某個(gè)函數(shù)實(shí)現(xiàn)上
BOOL sel_isMapped(SEL sel);
出現(xiàn)個(gè)BUG:
既然SEL是方法的唯一標(biāo)識(shí),那不同的類調(diào)用名字相同的方法怎么辦呢?
那就讓下一個(gè)重要任務(wù)出場(chǎng)吧。
IMP
定義:函數(shù)指針,指向方法實(shí)現(xiàn)的首地址。
代碼定義如下:
typedef id (*IMP)(id, SEL, ...);
其參數(shù)包含id,SEL,后面試實(shí)際的參數(shù)列表。
那么,XX調(diào)用了XXX方法,其參數(shù)為XX都確定下來(lái)了。
IMP的高級(jí)作用
既然上述元素都確定下來(lái)了,那么就可以直接繞過(guò)Runtime的消息傳遞機(jī)制,直接執(zhí)行IMP指向的函數(shù)了。省去了一些列的查找,直接向?qū)ο蟀l(fā)送消息,效率會(huì)高一些。
IMP imp_implementationWithBlock(id block)//根據(jù)代碼塊獲取IMP,其實(shí)就是代碼塊與IMP關(guān)聯(lián)
IMP method_getImplementation(Method m) //根據(jù)Method獲取IMP
[[objc Class] instanceMethodForSelector:SEL]//根據(jù)OC方式獲取IMP
當(dāng)我們獲取一個(gè)方法的IMP時(shí)候可以直接調(diào)用IMP
IMP imp = method_getImplementation(Method m);
id objc = imp(id,SEL,argument);//objc用來(lái)保存方法的返回值,id表示調(diào)用這個(gè)方法的對(duì)象,SEL是Method的選擇器,argument是方法的參數(shù)。
Method
Method定義如下:它主要是用語(yǔ)描述類里面的方法
typedef struct objc_method *Method;
objc_method結(jié)構(gòu)體定義如下
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;//方法名
char *method_types OBJC2_UNAVAILABLE;//參數(shù)返回值字符串描述
IMP method_imp OBJC2_UNAVAILABLE;//方法的實(shí)現(xiàn)
}
從上述代碼可以看出,Method是一個(gè)結(jié)構(gòu)體,包含了SEL和IMP成員變量。
實(shí)際上,相當(dāng)于在SEL和IMP之間做了一個(gè)映射,有了Method,SEL就可以找到對(duì)應(yīng)的IMP,從而調(diào)用方法。
Method操作函數(shù)如下:
方法操作主要有以下函數(shù):
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 獲取實(shí)例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實(shí)現(xiàn)
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類實(shí)例是否響應(yīng)指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
此處具體實(shí)現(xiàn)比較簡(jiǎn)單,可以通過(guò)手動(dòng)操作IMP來(lái)完成方法的調(diào)用。
實(shí)例代碼:
#import "OC_IMP.h"
typedef void (^CustomBlock)(NSString *name);
@interface OC_IMP ()
@property(nonatomic, weak) CustomBlock block;
@end
@implementation OC_IMP
- (void)testIMP {
[self addMethodByIMP];
}
//定義一個(gè)block
//手動(dòng)添加方法
- (void)addMethodByIMP {
CustomBlock block = ^(NSString *name){
NSLog(@"執(zhí)行block");
};
IMP impBlock = imp_implementationWithBlock(block);
Method m = class_getInstanceMethod(self.class, @selector(testIMP));
method_setImplementation(m, impBlock);
const char * types = method_getTypeEncoding(m); //因?yàn)榉椒愋拖嗤ǘ际菬o(wú)參數(shù)無(wú)返回值類型,所以方法類型相同,如果知道的話,可以直接制定type為v16@0:8)
sel_registerName("newSel"); //注冊(cè)newSel
BOOL isAdded = class_addMethod([self class], @selector(newSel), impBlock, types);
if (isAdded == YES) {
NSLog(@"添加成功");
[self performSelector:@selector(newSel)];
}
}
@end
OC_IMP *oc_imp = [[OC_IMP alloc] init];
[oc_imp testIMP];
調(diào)用執(zhí)行結(jié)果
2017-07-14 20:14:26.359 OCDeepLearning[5654:530015] 添加成功
2017-07-14 20:14:26.359 OCDeepLearning[5654:530015] 執(zhí)行block
補(bǔ)充:
- 在swift里可以使用#selector(XXX)來(lái)獲取對(duì)應(yīng)的SEL,但這并非指swift的方法調(diào)用是通過(guò)selector來(lái)實(shí)現(xiàn)的,能調(diào)用僅僅是因?yàn)閟wift和OC的混編;
- 每個(gè)方法名有對(duì)應(yīng)的唯一seletor,其SEL相同,但對(duì)應(yīng)的IMP函數(shù)指針不同。