這里就不說runtime有多牛逼了。直接進(jìn)入正題鸟蟹。
首先Objective-C是面向?qū)ο蟮模?strong>任何類的定義都是對象建钥。類和類的實(shí)例(對象)沒有任何本質(zhì)上的區(qū)別锦针。任何對象都有isa指針。
Class 是一個(gè) objc_class 結(jié)構(gòu)類型的指針
要理解runtime就要知道這些參數(shù)的意思:
isa:
isa是一個(gè)Class類型的指針悉盆,每個(gè)被實(shí)例化的對象都有一個(gè)isa指針焕盟,指向相對應(yīng)的類脚翘,然后這個(gè)類也有一個(gè)isa指針来农,指向meteClass(元類)崇堰。
可能很多人理解不了元類的概念海诲,我開始我對元類這個(gè)東西特幔,那時(shí)候還和父類聯(lián)系在一起,也是想了很久才想通薄风。
這里說一下類和對象的關(guān)系(幫助理解元類概念):
很多人知道每一個(gè)對象本質(zhì)上都是一個(gè)類的實(shí)例村刨。其中類定義了成員變量和成員方法的列表。對象通過對象的isa指針指向類打洼。
但其實(shí)每一個(gè)類本質(zhì)上都是一個(gè)對象募疮,類其實(shí)是元類的實(shí)例。元類定義了類方法的列表他嚷。類通過類的isa指針指向元類筋蓖。當(dāng)類方法被調(diào)用時(shí),先會(huì)從本身查找類方法的實(shí)現(xiàn)蚣抗,如果沒有翰铡,元類會(huì)向他父類查找該方法锭魔。同時(shí)注意的是:元類也是類路呜,它也是對象拣宰。元類也有isa指針,它的isa指針最終指向的是一個(gè)根元類(root meteClass).根元類的isa指針指向本身,這樣形成了一個(gè)封閉的內(nèi)循環(huán)膛堤。
super_class:父類肥荔,如果該類已經(jīng)是最頂層的根類,那么它為NULL燕耿。
version:類的版本信息,默認(rèn)為0
info:供運(yùn)行期使用的一些位標(biāo)識(shí)姜胖。
instance_size:該類的實(shí)例變量大小
ivars:成員變量的數(shù)組
runtime的應(yīng)用:
1.動(dòng)態(tài)創(chuàng)建一個(gè)類(比如KVO的底層實(shí)現(xiàn))
2.動(dòng)態(tài)地為某個(gè)類添加屬性\方法, 修改屬性值\方法
3.遍歷一個(gè)類的所有成員變量(屬性)\所有方法
實(shí)質(zhì)上右莱,以上的是通過相關(guān)方法來獲取對象或者類的isa指針來實(shí)現(xiàn)的慢蜓。
這里介紹下runtime中經(jīng)常出現(xiàn)的一些字眼(其實(shí)翻譯下就知道了):
- 增加
增加函數(shù):class_addMethod
增加實(shí)例變量:class_addIvar
增加屬性:@dynamic標(biāo)簽晨抡,或者class_addMethod则剃,因?yàn)閷傩云鋵?shí)就是由getter和setter函數(shù)組成
增加Protocol:class_addProtocol (說實(shí)話我真不知道動(dòng)態(tài)增加一個(gè)protocol有什么用,-_-!!)
- 獲取
獲取函數(shù)列表及每個(gè)函數(shù)的信息(函數(shù)指針棍现、函數(shù)名等等):class_getClassMethod method_getName ...
獲取屬性列表及每個(gè)屬性的信息:class_copyPropertyList property_getName
獲取類本身的信息,如類名等:class_getName class_getInstanceSize
獲取變量列表及變量信息:class_copyIvarList
獲取變量的值
- 替換
將實(shí)例替換成另一個(gè)類:object_setClass
替換類方法的定義:class_replaceMethod
4.其他常用方法:
交換兩個(gè)方法的實(shí)現(xiàn):method_exchangeImplementations.
設(shè)置一個(gè)方法的實(shí)現(xiàn):method_setImplementation.
之前比較空的時(shí)候就簡單的整理了一下轴咱,這邊以類方法形式實(shí)現(xiàn) 使用runtime記得導(dǎo)入<objc/message.h>,<objc/runtime.h>朴肺。
/**
* 獲取對象的所有屬性以及相對應(yīng)的值
*
* @param className 一個(gè)實(shí)例化的對象
*
* @return 字典(屬性-值)
*/
+ (NSDictionary *)getAllPropertyNamesAndValuesWithClassName:(id)className {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned int count;
objc_property_t *properties = class_copyPropertyList([className class], &count);
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
// 得到屬性名
NSString *propertyName = [NSString stringWithUTF8String:name];
// 獲取屬性值
id propertyValue = [className valueForKey:propertyName];
if (propertyValue && propertyValue != nil) {
[dict setObject:propertyValue forKey:propertyName];
}
}
// 記得釋放
free(properties);
return dict;
}
/**
* 獲取一個(gè)類(系統(tǒng)類也可以,包括未在.h中沒有聲名的屬性)的所有私有屬性
*
* @param className 類名(注意:不能傳對象)
*/
+ (void)getAttributeWithClassName:(id)className {
u_int count;
objc_property_t *properties = class_copyPropertyList(className, &count);
for (int i = 0; i < count; i++) {
const char *propertyName = property_getName(properties[i]);
NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
NSLog(@"%@", strName);
}
}
/**
* 獲取一個(gè)類(系統(tǒng)類也可以,包括未在.h中沒有聲名的屬性和成員變量)的所有私有屬性和成員變量
*
* @param className 類名(注意:不能傳對象)
*
* @return 一個(gè)數(shù)組
*/
+ (NSArray *)getAllMemberVariables:(id)className {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(className, &count);
NSMutableArray *results = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < count; ++i) {
Ivar variable = ivars[i];
const char *name = ivar_getName(variable);
NSString *varName = [NSString stringWithUTF8String:name];
[results addObject:varName];
}
return results;
}
/**
* 獲取某個(gè)類(包括系統(tǒng)類)的所有方法,包括系統(tǒng)類以及其中沒有在.h聲明的方法
*
* @param className 類名(注意:不能傳對象)
*/
+ (void)getAllMethodsWithClassName:(id)className {
unsigned int outCount = 0;
Method *methods = class_copyMethodList(className, &outCount);
for (int i = 0; i < outCount; ++i) {
Method method = methods[i];
// 獲取方法名稱鞍盗,但是類型是一個(gè)SEL選擇器類型
SEL methodSEL = method_getName(method);
// 需要獲取C字符串
const char *name = sel_getName(methodSEL);
// 將方法名轉(zhuǎn)換成OC字符串
NSString *methodName = [NSString stringWithUTF8String:name];
// 獲取方法的參數(shù)列表
int arguments = method_getNumberOfArguments(method);
NSLog(@"方法名:%@, 參數(shù)個(gè)數(shù):%d", methodName, arguments);
}
// 記得釋放
free(methods);
}
方法交換
/**
* 方法交換也適用系統(tǒng)API
*
* @param oldClassName 調(diào)用舊方法的類名
* @param oldMethod 舊方法
* @param newClassName 調(diào)用新方法的類名
* @param newMethod 新方法
*/
+ (void)methodSwizzlingAboutOldClassName:(id)oldClassName WithOldMethod:(SEL)oldMethod WithNewClassName:(id)newClassName WithNewMethod:(SEL)newMethod {
Method old_Method = class_getInstanceMethod(oldClassName, oldMethod);
Method new_Method = class_getInstanceMethod(newClassName, newMethod);
method_exchangeImplementations(old_Method, new_Method);
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
//利用runTime進(jìn)行歸檔
//利用個(gè)數(shù)取出對應(yīng)的屬性(kvc)屬性的名稱
//通過runtime來取出屬性
unsigned int count=0;
Ivar *ivars=class_copyIvarList([self class], &count);
for(int i=0;i<count;i++){
Ivar ivar=ivars[i];
const char *name=ivar_getName(ivar);
NSString *key=[NSString stringWithUTF8String:name];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if(self=[super init]){
unsigned int count=0;
Ivar *ivars=class_copyIvarList([self class], &count);
for(int i=0;i<count;i++){
Ivar ivar=ivars[i];
const char *name=ivar_getName(ivar);
NSString *key=[NSString stringWithUTF8String:name];
// id value=[self valueForKey:key];
id value=[aDecoder decodeObjectForKey:key];
}
free(ivars);
}
return self;
}
消息轉(zhuǎn)發(fā)機(jī)制:
在調(diào)用某一個(gè)方法時(shí),系統(tǒng)會(huì)查看這個(gè)對象能否接收這個(gè)消息(查看這個(gè)類有沒有這個(gè)方法敷存,或者有沒有實(shí)現(xiàn)這個(gè)方法堪伍。),如果不能并且只在不能的情況下涮俄,就會(huì)調(diào)用下面這幾個(gè)方法彻亲,給你“補(bǔ)救”的機(jī)會(huì)吮廉,你可以先理解為幾套防止程序crash的備選方案茧痕,我們就是利用這幾個(gè)方案進(jìn)行消息轉(zhuǎn)發(fā)踪旷,注意一點(diǎn),前一套方案實(shí)現(xiàn)后一套方法就不會(huì)執(zhí)行舀患。如果這幾套方案你都沒有做處理气破,那么程序就會(huì)報(bào)錯(cuò)crash。
方案一:
+ (BOOL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveClassMethod:(SEL)sel
方案二:
-(id)forwardingTargetForSelector:(SEL)aSelector
方案三:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;
#import <UIKit/UIKit.h>
@interface RunTimeViewController : UIViewController
//屬性(與外界交流的)
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *sex;
-(void)test1;
@end
#import "NSString+testAboutRuntime.h"
#import "RunTimeManage.h"
#import "RunTimeViewController.h"
#import <objc/message.h>
#import <objc/runtime.h>
#import "MYWebViewController.h"
#import <BaiduMapAPI_Search/BMKPoiSearchOption.h>
@interface RunTimeViewController () {
//成員變量
NSString *weight;
}
@property (nonatomic, strong) NSString *phoneNumber;
-(void)test3:(NSString *)str;
@end
@implementation RunTimeViewController
- (id)init {
if (self = [super init]) {
[RunTimeManage methodSwizzlingAboutOldClassName:[self class] WithOldMethod:@selector(viewDidLoad) WithNewClassName:[self class] WithNewMethod:@selector(viewDidLoadByMjj)];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidLoadByMjj {
[self.view setBackgroundColor:[UIColor orangeColor]];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[RunTimeManage methodSwizzlingAboutOldClassName:[self class] WithOldMethod:@selector(test1) WithNewClassName:[self class] WithNewMethod:@selector(test2)];
[self test1];
NSString *str = [[NSString alloc] init];
str.name = @"zhangsan";
// str.AttributeName=@"1111";
NSLog(@"%@", str.name);
// NSLog(@"%@",str.AttributeName);
self.name = @"mjj";
self.sex = @"男";
self.age = 22;
[self test3:self.name];
[RunTimeManage getAllMemberVariables:[RunTimeViewController class]];
[RunTimeManage getAttributeWithClassName:[RunTimeViewController class]];
[self setValue:@"女" forKey:@"sex"];
[RunTimeManage getAllMethodsWithClassName:[NSString class]];
[RunTimeManage getAllMethodsWithClassName:[BMKBasePoiSearchOption class]];
[RunTimeManage getAllPropertyNamesAndValuesWithClassName:self];
NSLog(@"%@", [RunTimeManage getAllPropertyNamesAndValuesWithClassName:self]);
NSLog(@"%@", [[RunTimeManage getAllPropertyNamesAndValuesWithClassName:self] valueForKey:@"sex"]);
}
- (void)test1 {
NSLog(@"111111");
}
- (void)test2 {
NSLog(@"222222");
}
void functionForMethod1(id self,SEL _cmd){
NSLog(@"動(dòng)態(tài)添加方法SUCCESS");
}
//----------先動(dòng)態(tài)決議碳锈,動(dòng)態(tài)決議返回NO,進(jìn)入消息轉(zhuǎn)發(fā)——————————————————
//動(dòng)態(tài)實(shí)現(xiàn)某個(gè)方法(動(dòng)態(tài)決議)
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selectorString=NSStringFromSelector(sel);
if([selectorString isEqualToString:@"test3:"]){
class_addMethod(self, sel, (IMP)functionForMethod1, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
//消息轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSString *selectorString=NSStringFromSelector(aSelector);
//先執(zhí)行 resolveInstanceMethod强重,如果找到方法則不執(zhí)行該方法
if([selectorString isEqualToString:@"test3:"]){
//返回一個(gè)對象间景,如果該對象實(shí)現(xiàn)了該方法則程序不會(huì)crash倘要。
return [[MyWebViewController alloc]init];
}
return nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
具體看我的代碼吧力惯,應(yīng)該還是可以看懂的父晶,其中可能會(huì)有比較多的代碼冗余,因?yàn)檎{(diào)試時(shí)候牽扯比較多尝苇。歡迎交流埠胖。