SEL
- SEL方法選擇器,表示一個selector的指針
- 無論什麼類裏,只要方法名相同,SEL就相同。項目裏的所有SEL都保存在一個NSSet集合裏(NSSet集合裏的元素不能重複),所以查找對應方法,只要找到對應的SEL就可以了。
- SEL實際是根據方法名hash化了的字符串
SEL sel_registerName(const char *str)//向runtime system註冊一個方法名。如果方法名已經註冊,則放回已經註冊的SEL
SEL sel_getUid(const char *str)//同上
@selector(<#selector#>)//oc編譯器提供的
SEL NSSelectorFromString(NSString *aSelectorName)//OC字符串轉化
SEL method_getName ( Method m );//根據Method結構體獲取
等等
SEL的操作函數
// 比較兩個選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
//判斷方法名是否映射到某個函數實現上
BOOL sel_isMapped(SEL sel);
出現個BUG:
既然SEL是方法的唯一標識,那不同的類調用名字相同的方法怎麼辦呢?
那就讓下一個重要任務出場吧。
IMP
定義:函數指針,指向方法實現的首地址。
代碼定義如下:
typedef id (*IMP)(id, SEL, ...);
其參數包含id,SEL,後面試實際的參數列表。
那麼,XX調用了XXX方法,其參數爲XX都確定下來了。
IMP的高級作用
既然上述元素都確定下來了,那麼就可以直接繞過Runtime的消息傳遞機制,直接執行IMP指向的函數了。省去了一些列的查找,直接向對象發送消息,效率會高一些。
IMP imp_implementationWithBlock(id block)//根據代碼塊獲取IMP,其實就是代碼塊與IMP關聯
IMP method_getImplementation(Method m) //根據Method獲取IMP
[[objc Class] instanceMethodForSelector:SEL]//根據OC方式獲取IMP
當我們獲取一個方法的IMP時候可以直接調用IMP
IMP imp = method_getImplementation(Method m);
id objc = imp(id,SEL,argument);//objc用來保存方法的返回值,id表示調用這個方法的對象,SEL是Method的選擇器,argument是方法的參數。
Method
Method定義如下:它主要是用語描述類裏面的方法
typedef struct objc_method *Method;
objc_method結構體定義如下
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;//方法名
char *method_types OBJC2_UNAVAILABLE;//參數返回值字符串描述
IMP method_imp OBJC2_UNAVAILABLE;//方法的實現
}
從上述代碼可以看出,Method是一個結構體,包含了SEL和IMP成員變量。
實際上,相當於在SEL和IMP之間做了一個映射,有了Method,SEL就可以找到對應的IMP,從而調用方法。
Method操作函數如下:
方法操作主要有以下函數:
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 獲取實例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實現
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實現
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類實例是否響應指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
此處具體實現比較簡單,可以通過手動操作IMP來完成方法的調用。
實例代碼:
#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];
}
//定義一個block
//手動添加方法
- (void)addMethodByIMP {
CustomBlock block = ^(NSString *name){
NSLog(@"執行block");
};
IMP impBlock = imp_implementationWithBlock(block);
Method m = class_getInstanceMethod(self.class, @selector(testIMP));
method_setImplementation(m, impBlock);
const char * types = method_getTypeEncoding(m); //因爲方法類型相同(都是無參數無返回值類型,所以方法類型相同,如果知道的話,可以直接制定type爲v16@0:8)
sel_registerName("newSel"); //註冊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];
調用執行結果
2017-07-14 20:14:26.359 OCDeepLearning[5654:530015] 添加成功
2017-07-14 20:14:26.359 OCDeepLearning[5654:530015] 執行block
補充:
- 在swift裏可以使用#selector(XXX)來獲取對應的SEL,但這並非指swift的方法調用是通過selector來實現的,能調用僅僅是因爲swift和OC的混編;
- 每個方法名有對應的唯一seletor,其SEL相同,但對應的IMP函數指針不同。