runtime
runtime 是iOS的運(yùn)行時因妙,用于實(shí)現(xiàn)iOS加載和調(diào)用屬性和方法痰憎。
函數(shù)中l(wèi)oad方法沒有使用runtime機(jī)制票髓,是底層直接調(diào)用的函數(shù)。load執(zhí)行順序是由編譯時的文件順序相同铣耘,先編譯的先執(zhí)行l(wèi)oad洽沟,類優(yōu)先于分類的順序調(diào)用 +load
方法。
initialize
+initialize
方法是在類或類的子類收到第一條消息之前被調(diào)用的蜗细,這里所指的消息包括實(shí)例方法和類方法的調(diào)用玲躯。
也就是說 +initialize
方法是以懶加載的方式被調(diào)用的,如果一直沒有給一個類或他的子類發(fā)送消息鳄乏,那么這個類的 +initialize
方法是永遠(yuǎn)不會調(diào)用的。
當(dāng)我們向某個類發(fā)送消息時棘利,runtime
會調(diào)用 IMP lookUpImpOrForward(...)
這個函數(shù)在類中查找相應(yīng)方法的實(shí)現(xiàn)或進(jìn)行消息轉(zhuǎn)發(fā)橱野,打開 objc-runtime-new.h
找到這個函數(shù):
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
Class curClass;
IMP imp = nil;
...
if (initialize && !cls->isInitialized()) {
// 類沒有初始化時,對類進(jìn)行初始化
_class_initialize (_class_getNonMetaClass(cls, inst));
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
...
}
從中可以看到當(dāng)類沒有初始化時善玫,會調(diào)用 _class_initialize(Class cls)
對類進(jìn)行初始化:
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
BOOL reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
// 遞歸調(diào)用水援,對父類進(jìn)行_class_initialize調(diào)用,確保父類的initialize方法比子類先調(diào)用
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
......
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
cls->nameForLogging());
}
// 發(fā)送調(diào)用類方法initialize的消息
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
......
}
在這里茅郎,先是對入?yún)⒌母割愡M(jìn)行遞歸調(diào)用蜗元,以確保父類優(yōu)先于子類初始化。
+initialize
方法在 runtime
中是以發(fā)送消息的方式調(diào)用的系冗,所以子類會覆蓋父類的實(shí)現(xiàn)奕扣,分類會覆蓋類的實(shí)現(xiàn),多個分類只會調(diào)用一個分類的 +initialize
方法掌敬。
分類
通過runtime動態(tài)將分類的屬性和方法合并到類對象惯豆,元類對象中。
- 擴(kuò)展屬性
#import "ClassName + CategoryName.h"
#import <objc/runtime.h>
static void *strKey = &strKey;
@implementation ClassName (CategoryName)
-(void)setStr:(NSString *)str
{
objc_setAssociatedObject(self, &strKey, str, OBJC_ASSOCIATION_COPY);
}
-(NSString *)str
{
return objc_getAssociatedObject(self, &strKey);
}
Method Swizzling
每個類都維護(hù)一個方法(Method)列表奔害,Method則包含SEL和其對應(yīng)IMP的信息楷兽,方法交換做的事情就是把SEL和IMP的對應(yīng)關(guān)系斷開,并和新的IMP生成對應(yīng)關(guān)系华临。
交換前:Asel->AImp Bsel->BImp
交換后:Asel->BImp Bsel->AImp
方法交換之后芯杀,“方法的實(shí)現(xiàn)” 變成了 “你的處理代碼” + “方法的實(shí)現(xiàn)”
//獲取通過SEL獲取一個方法
class_getInstanceMethod
//獲取一個方法的實(shí)現(xiàn)
method_getImplementation
//獲取一個OC實(shí)現(xiàn)的編碼類型
method_getTypeEncoding
//給方法添加實(shí)現(xiàn)
class_addMethod
//用一個方法的實(shí)現(xiàn)替換另一個方法的實(shí)現(xiàn)
class_replaceMethod
//交換兩個方法的實(shí)現(xiàn)
method_exchangeImplementations
+ (void)swizzleMethods:(Class)class originalSelector:(SEL)origSel swizzledSelector:(SEL)swizSel {
Method origMethod = class_getInstanceMethod(class, origSel);
Method swizMethod = class_getInstanceMethod(class, swizSel);
BOOL didAddMethod = class_addMethod(class, origSel, method_getImplementation(swizMethod), method_getTypeEncoding(swizMethod));
if (didAddMethod) {
class_replaceMethod(class, swizSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
method_exchangeImplementations(origMethod, swizMethod);
}
}
調(diào)用過程,涉及到了isa指針
isa
arm64之前雅潭,isa是普通指針
arm64 isa采用共用體數(shù)據(jù)結(jié)構(gòu)揭厚,使用位域存儲更多信息,將64位數(shù)據(jù)寻馏,分開來存儲信息
isa共用體中的結(jié)構(gòu)體沒有實(shí)際意義,只是用來描述內(nèi)存中每一部分的作用
isa只有33位用來存放類地址值(shiftcls在
4-36
位)棋弥,需要&ISA_MASK才能像之前一樣訪問class、meta-class-
isa共用體的好處:
- 可以存放更多的信息
- 更好的利用內(nèi)存空間
ps:MASK 掩碼诚欠,一般用來進(jìn)行按位(與&)運(yùn)算顽染。