1.根據指定規則根據runtime進行頁面選擇跳轉
背景:要根據后臺返回的數據 進行選擇要跳轉到哪一個ViewController
// 這個規則肯定事先跟服務端溝通好,跳轉對應的界面需要對應的參數
NSDictionary *userInfo = @{
@"class": @"HSFeedsViewController",
@"ID": @"123",
@"type": @"12"
}
};
思路:使用runtime去解決這個問題的思路就是 通過運行時 根據對應的類名去創建類以及類的屬性,這就需要和服務端去協商返回的數據形式 要類似:
實現: 這里使用了三個objc/runtime.h中函數分別是:
1 - (void)push:(NSDictionary *)params 2 3 { 4 5 // 類名 6 7 NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]]; 8 9 const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];10 11 12 13 // 從一個字串返回一個類14 15 Class newClass = objc_getClass(className);16 17 if (!newClass)18 19 {20 21 // 創建一個類22 23 Class superClass = [NSObject class];24 25 newClass = objc_allocateClassPair(superClass, className, 0); 26 27 // 注冊你創建的這個類28 29 objc_registerClassPair(newClass);30 31 }32 33 // 創建對象34 35 id instance = [[newClass alloc] init];36 37 38 39 NSDictionary *propertys = params[@"property"];40 41 [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {42 43 // 檢測這個對象是否存在該屬性44 45 if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {46 47 // 利用kvc賦值48 49 [instance setValue:obj forKey:key];50 51 }52 53 }]54 55 // 獲取導航控制器56 57 UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;58 59 UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];60 61 62 63 // 跳轉到對應的控制器64 65 [pushClassStance pushViewController:instance animated:YES];66 67 }
(1)objc_getClass(const char *name) 即返回對應的類;
(2)objc_allocateClassPair(Class superClass,const char*name ,size_t extraBytes) 為對應的類設置父類,并根據父類和extraBytes(變量的字節數) 給這個類分配空間
(3)objc_registerClassPair(class) 注冊這個類
通過這三個函數 創建類一個類
還有一個運行時函數 class_copyPropertyList(Class class,unsigned int *outCount)
1 /** 2 3 * 檢測對象是否存在該屬性 4 5 */ 6 7 - (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName 8 9 {10 11 unsigned int outCount, i;12 13 // 獲取對象里的屬性列表14 15 objc_property_t * properties = class_copyPropertyList([instance16 17 class], &outCount);18 19 20 21 for (i = 0; i < outCount; i++) {22 23 objc_property_t property =properties[i];24 25 // 屬性名轉成字符串26 27 NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];28 29 // 判斷該屬性是否存在30 31 if ([propertyName isEqualToString:verifyPropertyName]) {32 33 free(properties);34 35 return YES;36 37 }38 39 }40 41 free(properties);42 43 44 45 return NO;46 47 }
objc_property_t class_copyPropertyList(Class class,unsigned int *outCount) 返回值為一個數組 數組中為對應的類的屬性列表 以此來對比類中是否含有當前屬性,從而通過KVC為類的屬性賦值.
2.修改系統類的方法實現
背景:如果需求中要統計每個viewController 展示給用戶的次數,我們想到的就是在ViewController中添加對應的統計的代碼,但是會很麻煩,此時可以創建一個UI ViewController的分類更改底層中viewWillAppear的實現 這就用到runtime中method Swizzling (方法混合)
實現:通過methodSwizzling 修改了UIViewController的@selector(viewWillAppear:)對應的函數指針,使他的實現指向了自定的方法
#import "UIViewController+Tracking.h"#import <objc/runtime.h> @implementation UIViewController (Tracking) + (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzlingSelector = @selector(swizzling__viewWillAppear:); Method originMethod = class_getInstanceMethod(class, originalSelector); Method swizzlingMethod = class_getInstanceMethod(class, swizzlingSelector); //將 swizzlingMethod 中的實現過程添加到 originalSelector 這個方法中 BOOL didAddMethod = class_addMethod(class,originalSelector, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod)); if (didAddMethod) { //用 originMethod中的實現 替換 swizzlingSelector這個方法中的實現 這樣原始的和自定義的方法都執行的時同一個實現過程 class_replaceMethod(class, swizzlingSelector, method_getImplementation(originMethod), method_getTypeEncoding(originMethod)); }else{//可能出現添加失敗的情況(比如 被添加的方法中已經存在了對應方法的實現 就會返回NO) //將 originMethod 和 swizzlingMethod中的實現過程進行交換 method_exchangeImplementations(originMethod, swizzlingMethod); } });}/* 經過上面的 class_method 和 class_replaceMethod 或 method_exchangeImplementations 就實現了 //http://blog.jobbole.com/79580/ */ - (void)swizzling__viewWillAppear:(BOOL)animation{ [self swizzling__viewWillAppear:animation]; NSLog(@"swizzling_viewWillAppear");}
(1)通過兩個方法 class_addMethod class_replaceMethod這個方法實現方法Method這個結構體中 IMP對應的函數指針對應的函數實現進行添加 或者 替換SEL 代表了Method的名稱name,IMP代表函數指針 即為函數在內存中的開始位置
另外還有一些注意事項 為什么一定要寫在load中 還要寫在dispatch_once中,因為swilzzing method會影響代碼的整體狀態 甚至有可能改變程序的運行流程,要保證避免并發的競爭,而+load方法是在類初始化就進行了加載(這里不太明白具體原理)詳細參看下面鏈接
http://nshipster.com/method-swizzling/
新聞熱點
疑難解答