一:runtime簡單介紹
默認(rèn)項(xiàng)目中使用runtime代碼是不可以的,需要進(jìn)行下面設(shè)置
Project-->Build Setting--> Enable Strict Checking of objc_msgSend Calls 修改為NO即可
二:runtime涵蓋內(nèi)容
1.objc/message 消息發(fā)送和轉(zhuǎn)發(fā)機(jī)制
objc_msgSend 負(fù)責(zé)動態(tài)消息發(fā)送
_objc_msgForward 負(fù)責(zé)消息轉(zhuǎn)發(fā)
I.代碼如下:
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class class;
__unsafe_unretained _Nonnull Class super_class;
};
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_EXPORT void
method_invoke(void /* id receiver, Method m, ... */ )
OBJC_EXPORT id _Nullable
_objc_msgForward(id _Nonnull receiver, SEL _Nonnull sel, ...)
II.消息轉(zhuǎn)發(fā)機(jī)制
2.objc/objc
typedef struct objc_class *Class;
struct objc_object {
Class isa;
};
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
//sel和object方法
const char * _Nonnull sel_getName(SEL _Nonnull sel)
SEL _Nonnull sel_registerName(const char * _Nonnull str)
BOOL sel_isMapped(SEL _Nonnull sel)
SEL _Nonnull sel_getUid(const char * _Nonnull str)
const char * _Nonnull object_getClassName(id _Nullable obj)
void * _Nullable object_getIndexedIvars(id _Nullable obj)
3.objc/runtime runtime核心內(nèi)容
I.objc_class結(jié)構(gòu)體
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef struct objc_category *Category;
typedef struct objc_property *objc_property_t;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
II.屬性澳化,方法,變量獲取常用方法
參考鏈接:http://www.reibang.com/p/cefa1da5e775
代碼如下:
//1.屬性
u_int count;
objc_property_t *properties = class_copyPropertyList([Person class], &count);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i <count; i++) {
const char *propertyName = property_getName(properties[i]);
//包含類型奈懒,修飾詞等內(nèi)容 參考:上面鏈接
const char *propertyAttributes = property_getAttributes(properties[i]);
[propertiesArray addObject:[NSString stringWithUTF8String:propertyName]];
}
free(properties);
NSLog(@"----%@",propertiesArray);
//2.方法
unsigned int methodCount;
Method *methods = class_copyMethodList([Person class], &methodCount);
NSMutableArray *methodArray = [NSMutableArray arrayWithCapacity:methodCount];
for (int i = 0; i <count; i++) {
Method temp = methods[i];
IMP imp = method_getImplementation(temp);
SEL name_f = method_getName(temp);
const char *name_s = sel_getName(name_f);
int arguments = method_getNumberOfArguments(temp);
const char *encoding = method_getTypeEncoding(temp);
NSLog(@"方法名:%@,參數(shù)個數(shù):%d,編碼方式:%@",[NSString stringWithUTF8String:name_s],arguments,[NSString stringWithUTF8String:encoding]);
[methodArray addObject:[NSString stringWithUTF8String:name_s]];
}
free(methods);
NSLog(@"----%@",methodArray);
//3.全局變量
u_int ivarcount;
Ivar *ivars = class_copyIvarList([Person class], &ivarcount);
NSMutableArray *ivarArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i <ivarcount; i++) {
const char *ivarName = ivar_getName(ivars[i]);
[ivarArray addObject:[NSString stringWithUTF8String:ivarName]];
}
free(ivars);
NSLog(@"----%@",ivarArray);
III.方法交換常用的經(jīng)典代碼
+ (void)load {
Class class = [self class];
SEL origSEL = @selector(viewWillAppear:);
SEL replaceSEL = @selector(viewWillAppear:);
Method originMethod = class_getInstanceMethod(class, origSEL);
Method replaceMethod = class_getInstanceMethod(class, replaceSEL);
IMP origIMP = class_getMethodImplementation_stret(class, origSEL);
IMP replaceIMP = class_getMethodImplementation_stret(class, replaceSEL);
//先為原始方法添加實(shí)現(xiàn),false有實(shí)現(xiàn)方法同衣,true沒有實(shí)現(xiàn)方法
BOOL isAdd = class_addMethod(class, origSEL,replaceIMP , "v@:");
if (isAdd) {
//此時兩個方法名字皆指向同一個實(shí)現(xiàn)方法糊饱,下面為新方法變更實(shí)現(xiàn)方法
class_replaceMethod(class, replaceSEL,origIMP , "v@:");
}else{
//交換兩個方法的實(shí)現(xiàn)
method_exchangeImplementations(originMethod, replaceMethod);
}
}
- (void)viewWillAppearN:(BOOL)animated {
//調(diào)用系統(tǒng)方法
[self viewWillAppearN:animated];
NSString *name = [NSString stringWithUTF8String:object_getClassName(self)];
NSString *trackName = [NSString stringWithFormat:@"%@----viewWillAppear",name];
[[TrackObject sharedManager]track:trackName];
}
按鈕事件監(jiān)聽[是UIControl的方法 sendAction:to:forEvent:]
+ (void)load {
Class class = [self class];
SEL origSEL = @selector(sendAction:to:forEvent:);
SEL replaceSEL = @selector(sendActionN:to:forEvent:);
Method originMethod = class_getInstanceMethod(class, origSEL);
Method replaceMethod = class_getInstanceMethod(class, replaceSEL);
IMP origIMP = class_getMethodImplementation_stret(class, origSEL);
IMP replaceIMP = class_getMethodImplementation_stret(class, replaceSEL);
//先為原始方法添加實(shí)現(xiàn)魔市,false有實(shí)現(xiàn)方法,true沒有實(shí)現(xiàn)方法
BOOL isAdd = class_addMethod(class, origSEL,replaceIMP , "v@:");
if (isAdd) {
//此時兩個方法名字皆指向同一個實(shí)現(xiàn)方法讲婚,下面為新方法變更實(shí)現(xiàn)方法
class_replaceMethod(class, replaceSEL,origIMP , "v@:");
}else{
//交換兩個方法的實(shí)現(xiàn)
method_exchangeImplementations(originMethod, replaceMethod);
}
}
- (void)sendActionN:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
[self sendActionN:action to:target forEvent:event];
NSString *trackName = [NSString stringWithFormat:@"%@----%@",target,NSStringFromSelector(action)];
[[TrackObject sharedManager]track:trackName];
}
其他的參照WOCrashProtector尿孔,里面的內(nèi)容很全
4.objc/NSObject 最基礎(chǔ)類NSObject介紹
三:項(xiàng)目應(yīng)用場景
1.防止項(xiàng)目閃退
WOCrashProtector 就是很好的防止閃退的依賴庫
2.代碼無縫埋點(diǎn)
3.類別中設(shè)置屬性實(shí)現(xiàn)
4.切面編程的核心
5.開啟動態(tài)創(chuàng)建類,讓你代碼有逼格【裝逼專用磺樱,項(xiàng)目中勿用】
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建類對象
Class newClass = objc_allocateClassPair([NSObject class], "EdenModel", 0);
class_addMethod(newClass, @selector(test), (IMP) test, "v@:"); //添加方法
NSString*name =@"name";
class_addIvar(newClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id)); //添加屬性
objc_registerClassPair(newClass); //注冊此類對象纳猫,至關(guān)重要
//開始用這個類
NSObject *edenModelInstance = [NSClassFromString(@"EdenModel") new];
[edenModelInstance setValue:@"杰克" forKey:@"name"];
NSLog(@"我的名字:---%@",[edenModelInstance valueForKey:@"name"]); //我的名字:---杰克
[edenModelInstance performSelector:@selector(test)];
objc_msgSend(edenModelInstance, @selector(test)); //通過消息發(fā)送調(diào)用方法
}
//方法的實(shí)現(xiàn)
void test(id self, SEL _cmd){
NSLog(@"This object is %p.",self); //edenModelInstance
NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]); //edenModelInstance
Class currentClass = [self class];
for( int i = 1; i < 5; ++i )
{
NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]); //NSObject 0x7fff89be1d00
NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class])); //NSObject 0x7fff89be1cd8
}
四:第三方庫有關(guān)runtime知識源碼探索
1.MJExtension
2.Aspects
3.jrswizzle
4.UIViewController-Swizzled
五:額外相關(guān)知識load,initialize和init
load 在程序啟動前初始化執(zhí)行一次,且只執(zhí)行一次
initialize 在init前初始化執(zhí)行一次竹捉,且只執(zhí)行一次
init 為實(shí)例方法顯而易見芜辕,每調(diào)用一次執(zhí)行一次
-
區(qū)別及調(diào)用順序
參考鏈接: http://www.reibang.com/p/d2ba735fa0bd
http://www.reibang.com/p/939765be93a7int main(int argc, char * argv[]) { NSLog(@"%s:1",__func__);//第一個監(jiān)測點(diǎn) NSString * appDelegateClassName; @autoreleasepool { NSLog(@"%s:2",__func__);//第二個監(jiān)測點(diǎn) appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName); } //AView.m @implementation AView + (void)load { NSLog(@"%s",__func__); } + (void)initialize { NSLog(@"%s",__func__); } -(instancetype )init{ if (self = [super init]) { NSLog(@"%s",__func__); } return self; } @end
執(zhí)行結(jié)果如下:
iOSAnimation[17421:357707] +[AView load]
iOSAnimation[17421:357707] main:1
iOSAnimation[17421:357707] main:2
iOSAnimation[17421:357707] +[AView initialize]
iOSAnimation[17421:357707] -[AView init]
iOSAnimation[17421:357707] -[AView init]
因此load里面的代碼不可過多,否則會造成啟動時間過長
load和initialize在父類和子類中執(zhí)行的順序
load 調(diào)用 父類-->子類-->類別
initialize調(diào)用 (無類別)父類-->子類 有類別 父類類別-->子類類別 有類別方法會覆蓋本類的方法
代碼執(zhí)行結(jié)果如下:
+[Person load]
+[Son load]
+[Person(Extention) load]
main
+[Person(Extention) initialize] Person
-[Person init]
+[Person(Extention) initialize] Son
+[Son initialize] Son
-[Person init]
-[Son init]