看別人寫的框架,看到了如下代碼:
id (*objc_msgSendGetCellIdentifier)(id self, SEL _cmd) = (void *)objc_msgSend;
CGFloat (*objc_msgSendGetCellHeight)(id self, SEL _cmd) = (void *)objc_msgSend;
id (*objc_msgSendCreateCellWithIndexPath)(id self, SEL _cmd, NSIndexPath *) = (void *)objc_msgSend;
Class cellClazz = NSClassFromString(model.containerCellClass);
if ([(id)cellClazz respondsToSelector:@selector(cellHeight)]) {
CGFloat cellHeight = objc_msgSendGetCellHeight(cellClazz, NSSelectorFromString(@"cellHeight"));
return cellHeight;
}
NSString *identifier = objc_msgSendGetCellIdentifier(cellClazz, NSSelectorFromString(@"cellReuseIdentifier"));
SideSlipBaseTableViewCell *templateCell = [self.templateCellDict objectForKey:identifier];
if (!templateCell) {
templateCell = objc_msgSendCreateCellWithIndexPath(cellClazz, NSSelectorFromString(@"createCellWithIndexPath:"), indexPath);
templateCell.delegate = self;
[self.templateCellDict setObject:templateCell forKey:identifier];
}
//update
[templateCell updateCellWithModel:&model indexPath:indexPath];
cell的.h文件
+ (NSString *)cellReuseIdentifier;
+ (CGFloat)cellHeight;
+ (instancetype)createCellWithIndexPath:(NSIndexPath *)indexPath;
cell的.m文件
+ (NSString *)cellReuseIdentifier {
NSAssert(NO, @"\nERROR: Must realize this function in subClass %s", __func__);
return nil;
}
+ (instancetype)createCellWithIndexPath:(NSIndexPath *)indexPath {
NSAssert(NO, @"\nERROR: Must realize this function in subClass %s", __func__);
return nil;
}
以前專門看過這塊的源碼,可是時間長了又忘了,從現在開始記錄吧。
一點一點來吧、
先看下面代碼
//編譯前
[xh send];
//編譯后
((void (*)(id, SEL))(void *)objc_msgSend)((id)xh, sel_registerName("send"));
oc中的方法調用實際上就是我們看到的現在這種情況,運用了objc_msgsend來發送消息。
繼續來看下面的代碼。
@interface A : NSObject
@property (nonatomic, assign) NSInteger a;
- (void)b;
+ (void)c;
@end
@implementation A
- (void)b {
NSLog(@"b");
}
+ (void)c {
NSLog(@"c");
}
@end
int main(int argc, char * argv[]) {
A *aObject = [[A alloc] init];
// 實例方法調用
[aObject b];
// 類方法調用
[A c];
}
經過clang轉換之后
typedef struct objc_object A;
// objc_getClass方法
struct objc_class *objc_getClass(const char *);
// sel_registerName方法
SEL sel_registerName(const char *str) __attribute__((availability(ios,introduced=2.0)));
// 對應上述main函數
int main(int argc, char * argv[]) {
A *aObject = ((A *(*)(id, SEL))(void *)objc_msgSend)((id)((A *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("A"), sel_registerName("alloc")), sel_registerName("init"));
// 實例方法調用
((void (*)(id, SEL))(void *)objc_msgSend)((id)aObject, sel_registerName("b"));
// 類方法調用
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("A"), sel_registerName("c"));
}
直接對比上下兩個代碼,我想有些意思應該可以明白。比如sel_registerName這個方法返回的SEL這個鬼東西。sel_registerName("alloc")), sel_registerName("init")這兩個分別對應著alloc和init方法。有木有搞懂?
上面還有兩點不同是實例方法和類方法的調用。
// 實例方法調用
((void (*)(id, SEL))(void *)objc_msgSend)((id)aObject, sel_registerName("b"));
// 類方法調用
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("A"), sel_registerName("c"));
主要的區別,實例方法是aobject而類方法,則需要objc_getClass獲取這個類。
繼續往下進行。。
先看下類的構成
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
我們都知道類是一個結構體。
那么方法的構成是什么鬼東西呢?
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
其實也是個結構體。 method_name 是方法名,method_types指的是方法的返回值和參數對照表。而method_imp其實就是一個方法指針,目的是找到這個方法。
下面有個例子
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
上面的方法是給類添加實例方法。如何來用?
int c(NSString *str) {
NSLog(@"c");
return 0;
}
int main(int argc, char * argv[]) {
class_addMethod([A class], @selector(c:), (IMP)c, "i@");
A *aObject = [[A alloc] init];
[aObject performSelector:@selector(c:)];
}
class cls 對應 [A class];
SEL name 對應 @selector(c:);
IMP imp 對應 (IMP)c
const char *types 則對應的是 “I@”;
其實前面說的都是構成之類的東西。那么objc_msgsend到底是如何工作呢?
看下面的源碼
// 首先看一下objc_msgSend的方法實現的偽代碼
id objc_msgSend(id self, SEL op, ...) {
if (!self) return nil;
// 關鍵代碼(a)
IMP imp = class_getMethodImplementation(self->isa, SEL op);
imp(self, op, ...); // 調用這個函數,偽代碼...
}
// 查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
if (!cls || !sel) return nil;
IMP imp = lookUpImpOrNil(cls, sel);
if (!imp) {
... // 執行動態綁定
}
IMP imp = lookUpImpOrNil(cls, sel);
if (!imp) return _objc_msgForward; // 這個是用于消息轉發的
return imp;
}
// 遍歷繼承鏈,查找IMP
IMP lookUpImpOrNil(Class cls, SEL sel) {
if (!cls->initialize()) {
_class_initialize(cls);
}
Class curClass = cls;
IMP imp = nil;
do { // 先查緩存,緩存沒有時重建,仍舊沒有則向父類查詢
if (!curClass) break;
if (!curClass->cache) fill_cache(cls, curClass);
imp = cache_getImp(curClass, sel);
if (imp) break;
} while (curClass = curClass->superclass); // 關鍵代碼(b)
return imp;
}
首先在Class中的緩存查找imp(沒緩存則初始化緩存),如果沒找到,則向父類的Class查找。如果一直查找到根類仍舊沒有實現,則進入消息轉發的流程,四步。如果最后還沒有那么只能抱錯,至于消息轉發的流程,我會另開一篇來講述。
前面看過類方法和實例方法的objc_msgsend調用。那么有個疑問,實例方法和類方法都是存在class中的哪里,又是如何被找到的呢?
這里要涉及到幾個地方,我們知道,類中有實例方法,而很多人可能不知道,類方法實際也是實例方法,只不過是父類的實例方法。我們看過前面類的結構中,有個isa指針,這個指針是機上就是指向這個class的父類。類指向父類,父類的指針一直向上一直指向根源類。
對于實例方法我們可以去類的methodlists里去取。
IMP bIMP = class_getMethodImplementation([A class], @selector(b));
那么類方法我們只能去,mataclass中的methodlist中去取。
// 獲取A類對應的metaClass
Class aMeta = objc_getMetaClass(class_getName([A class]));
// 在metaClass中找類方法d
IMP dIMP = class_getMethodImplementation(aMeta, @selector(d));
dIMP();
很完美。