CPFRuntimeKit
源代碼傳送門,編寫不易疤祭,看完給個Star哦湾揽。
用來做什么?
一個用來處理 Objective-C Runtime 騷操作的小工具踢关,包含一下幾個功能:
- CPFRuntimeHelper —— 為你指定的Class提供便捷的Hacker方法伞鲫;
- CPFRuntimeInvoker —— 為你指定的Class提供便捷的響應方法;
- CPFWeakSingleton —— 一種另類的單例模式實現(xiàn)方式签舞,解決單例在生命周期不釋放的問題秕脓。
- CPFTestClass —— 結(jié)合CPFRuntimeHelper,處理Runtime消息轉(zhuǎn)發(fā)儒搭。
一吠架、CPFRuntimeHelper
提供簡單易用的API用于快速獲取成員變量列表、屬性列表搂鲫、類方法列表傍药、實例方法列表、協(xié)議列表,此外還支持動態(tài)注入實例方法怔檩、交換實例方法實現(xiàn)等褪秀。
API如下:
- 獲取類實例的成員變量列表
+ (NSArray *)fetchIvarList:(Class)class;
- 獲取類實例的屬性列表
+ (NSArray *)fetchPropertyList:(Class)class;
- 獲取類實例的方法列表
+ (NSArray *)fetchMethodList:(Class)class;
- 為類實例動態(tài)添加實例方法,或者改變某methodSel的methodSelImpl
+ (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl;
- 交換類實例的方法實現(xiàn)
+ (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2;
以上僅展示一些相關(guān)API薛训,具體說明和使用請參考Demo媒吗,內(nèi)附有完整注釋。
二乙埃、CPFRuntimeInvoker
與CPFRuntimeHelper不同闸英,CPFRuntimeInvoker的實現(xiàn)和調(diào)用方式主要有兩種,一種是為NSObject類添加Category介袜,這樣一來甫何,就能為所有的類,包括自定義的類添加實例方法和類方法了遇伞。
CPFRuntimeInvoker就是利用了Category的特性為NSObject和NSString添加擴展辙喂,這樣就能通過Object和String,直接執(zhí)行私有方法鸠珠,并注入可變參數(shù)巍耗。
例如:
- 響應實例對象的私有方法,提供可變參數(shù)的注入渐排,用于不確定參數(shù)個數(shù)的私有方法
CPFTestClass *test = CPFTestClass.new;
[test invoke:@"testInvokSelectorWithArguments:arg2:" args:@"參數(shù)1",@"參數(shù)2",nil];
- 根據(jù) Class Name 響應私有方法
// CPFTestClass 是自定義類的類名
[@"CPFTestClass" invokeClassMethod:@"testClassMethod"];
以上僅展示一些相關(guān)API炬太,具體說明和使用請參考Demo,內(nèi)附有完整注釋驯耻。
特別的
除此之外亲族,里面一些實現(xiàn)的細節(jié)需要簡單的說明一下,最主要的是NSInvocation相關(guān)的理解:
- NSInvocation對象被用于對象存儲以及對象與Application之間的消息轉(zhuǎn)發(fā)可缚;
- 自定義NSInvocation對象霎迫,需要提供相應類型的NSMethodSignature對象、arguments帘靡、target知给、返回值類型等,對NSInvocation對象執(zhí)行 - invoke 方法测柠,來執(zhí)行響應的signature,并得到Return Type缘滥;
- 在2中提到的響應的arguments需要特別注意參數(shù)類型的問題轰胁,一旦類型出現(xiàn)錯誤可能引發(fā)意想不到的Crash。但萬幸的是NSMethodSignature對象提供 -getArgumentTypeAtIndex: 的實力方法朝扼,可以返回當前索引位置的參數(shù)類型赃阀,不過參數(shù)類型是 char * 型的;
- @encode 關(guān)鍵字,可以將類型轉(zhuǎn)換為 char * 型的字符串榛斯,如@encode(int) 观游,結(jié)合strcmp這個C標準函數(shù)可以判斷參數(shù)類型是否相同的問題;
- 執(zhí)行的返回結(jié)果驮俗,通過NSInvocation對象的 - getReturnValue: 實力方法得到懂缕,其中的參數(shù)是一個地址指針,用來指向返回值變量王凑;
- 除此之外搪柑,提供k_COVERT_ARRAY_FROM_args宏定義,用于將OC方法的可變參數(shù)轉(zhuǎn)換成NSArray索烹;
- 需要知道的是工碾,編譯器在處理可變參數(shù)的時候,是根據(jù)第一個可變參數(shù)在內(nèi)存中的地址百姓、參數(shù)類型渊额、偏移量等動態(tài)的計算出下一個參數(shù)的位置,從而取得相應的值垒拢,直到讀取到nil為止旬迹;
- 在6中的宏定義,就是利用了這個特性將可變參數(shù)轉(zhuǎn)換成NSArray的子库。
三舱权、CPFWeakSingleton
在開發(fā)中,我們通常會使用單例模式仑嗅,但是單例會有一個不好的問題就是宴倍,在整個程序的運行周期中,單例對象都不會被釋放仓技,從而會對內(nèi)存造成一定的影響鸵贬,那么我們可以利用 weak 關(guān)鍵字對單例模式進行改造,達到如果單例對象被外部持有脖捻,則永遠不會被釋放阔逼,一旦不被外部持有,則會在 Runloop 時被回收內(nèi)存的目的地沮。
以名為CPFWeakSingleton的類名為例嗜浮,代碼如下:
@implementation CPFWeakSingleton
+ (instancetype)sharedInstacne {
return [[self alloc] init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static __weak CPFWeakSingleton *weakInstance;
CPFWeakSingleton *strongInstance = weakInstance;
@synchronized(self) {
if (strongInstance == nil) {
strongInstance = [super allocWithZone:zone];
weakInstance = strongInstance;
}
}
return strongInstance;
}
@end
下面對其進行驗證:
_strongInstance = [CPFWeakSingleton sharedInstacne];
NSLog(@"1---%p",_strongInstance);
_strongInstance.testStr = @"保留所有權(quán)";
NSLog(@"2---%p",_strongInstance);
sleep(5);
NSLog(@"3---%p",[CPFWeakSingleton sharedInstacne]);
運行結(jié)果如下:
1---0x604000202ea0
2---0x604000202ea0
3---0x604000202ea0
可以看出,當我們通過_strongInstance變量持有單例對象時摩疑,在經(jīng)過 Runloop 之后危融,單例對象也不會被釋放(sleep函數(shù)是為了驗證 Runloop 后對象是否會被回收)。
然而我們對上例稍加改動雷袋,使_strongInstance被釋放后會發(fā)生什么呢吉殃?
_strongInstance = [CPFWeakSingleton sharedInstacne];
NSLog(@"1---%p",_strongInstance);
_strongInstance.testStr = @"保留所有權(quán)";
NSLog(@"2---%p",_strongInstance);
_strongInstance = nil;
sleep(5);
NSLog(@"3---%p",[CPFWeakSingleton sharedInstacne]);
此時的運行結(jié)果如下:
1---0x600000009430
2---0x600000009430
3---0x604000007570
可以看出,當外部的_strongInstance對象被釋放,不再持有單例對象的時候蛋勺,或者超出此時單例對象的作用域時(上述代碼未演示)瓦灶,該單例對象也會在 Runloop 中被系統(tǒng)回收,當我們再次使用sharedInstacne類方法獲取單例對象的時候抱完,則會創(chuàng)建一個新的單例對象贼陶。這樣,就能即使用單例乾蛤,又解決了產(chǎn)生的單例對象一直占用內(nèi)存資源每界,而且在整個程序的運行周期內(nèi)都不會被釋放的問題。
四家卖、CPFTestClass
有關(guān)消息轉(zhuǎn)發(fā)的一些內(nèi)容眨层,網(wǎng)上有很多相關(guān)的資料說的都很清楚,在這里就不做過多的贅述上荡。本Demo中主要是結(jié)合CPFRuntimeHelper和CPFRuntimeInvoker去強行實現(xiàn)消息轉(zhuǎn)發(fā)的過程趴樱,以及解決消息轉(zhuǎn)發(fā)帶來的doesNotRecognizeSelector異常Crash的問題。
以上僅展示一些相關(guān)API酪捡,具體說明和使用請參考Demo叁征,內(nèi)附有完整注釋。
源代碼傳送門逛薇,編寫不易捺疼,看完給個Star哦。