iOS中runtime執行機制解析
一.先思考兩個問題:
第一個問題,
1》runtime實現的機制是什麼,怎麼用,一般用於幹嘛?
這個問題我就不跟大家繞彎子了,直接告訴大家,
runtime是一套比較底層的純C語言API, 屬於1個C語言庫, 包含了很多底層的C語言API。
在我們平時編寫的OC程式碼中, 程式執行過程時, 其實最終都是轉成了runtime的C語言程式碼, runtime算是OC的幕後工作者
比如說,下面一個建立物件的方法中,
舉例:
OC :
[[MJPerson alloc] init]
runtime :
objc_msgSend(objc_msgSend(“MJPerson” , “alloc”), “init”)
第二個問題
runtime 用來幹什麼呢??用在那些地方呢?怎麼用呢?
runtime是屬於OC的底層, 可以進行一些非常底層的操作(用OC是無法現實的, 不好實現)
-
在程式執行過程中, 動態建立一個類(比如KVO的底層實現)
-
在程式執行過程中, 動態地為某個類新增屬性\方法, 修改屬性值\方法
-
遍歷一個類的所有成員變數(屬性)\所有方法
例如:我們需要對一個類的屬性進行歸檔解檔的時候屬性特別的多,這時候,我們就會寫很多對應的程式碼,但是如果使用了runtime就可以動態設定!
二.學習runtime機制首先要了解下面幾個問題
Objective-c是一門編譯型、動態語言(這裡強調下oc是靜態型別語言),這在開發語言中是並多見的,一般的動態語言多為解釋性語言。OC之所以能夠做到即使編譯型語言,又是動態語言。就是得益於RunTime機制。
1.相關的標頭檔案和函式
1> 標頭檔案
- 利用標頭檔案,我們可以檢視到runtime中的各個方法!
2> 相關應用
- NSCoding(歸檔和解檔, 利用runtime遍歷模型物件的所有屬性)
- 字典 –> 模型 (利用runtime遍歷模型物件的所有屬性, 根據屬性名從字典中取出對應的值, 設定到模型的屬性上)
- KVO(利用runtime動態產生一個類)
- 用於封裝框架(想怎麼改就怎麼改)
這就是我們runtime機制的只要運用方向
3> 相關函式
1、class_copyPropertyList 獲取一份拷貝的成員列表陣列
2、property_getName獲取成員名稱
3、class_getInstanceVariable 獲取成員物件的Ivar
4、object_getIvar從Ivar物件中取值
5、object_setIvar賦值函式
6、objc_msgSend : 給物件傳送訊息
7、class_copyMethodList : 遍歷某個類所有的方法
8、class_copyIvarList : 遍歷某個類所有的成員變數
9、class_…..
這些是我們學習runtime必須知道的函式!
2.必備常識
1> Ivar : 成員變數
2> Method : 成員方法
從上面例子中我們看到我們定義的成員變數,如果要是動態建立方法,可以使用Method,
三.我們通過一幅圖可以看清楚OC中類與物件的繼承層次關係:
注意:所有的metaclass中isa指標都是指向根metaclass,而根metaclass則指向自身。根metaclass是通過繼承根類產生的,與根class結構體成員一致,不同的是根metaclass的isa指標指向自身。
1、當我們呼叫某個物件的物件方法時,它會首先在自身isa指標指向的類(class)methodLists中查詢該方法,如果找不到則會通過class的super_class指標找到其父類,然後從其methodLists中查詢該方法,如果仍然找不到,則繼續通過 super_class向上一級父類結構體中查詢,直至根class;
2、當我們呼叫某個類方法時,它會首先通過自己的isa指標找到metaclass,並從其methodLists中查詢該類方法,如果找不到則會通過metaclass的super_class指標找到父類的metaclass結構體,然後從methodLists中查詢該方法,如果仍然找不到,則繼續通過super_class向上一級父類結構體中查 找,直至根metaclass;
四.下面我們來看一下程式碼實現:
1、建立一個類
CustomClass.h#import <Foundation/Foundation.h>
@interface CustomClass : NSObject
@property (nonatomic , strong) NSString *age;
- (void)test;
@end
CustomClass.m
#import "CustomClass.h"
@implementation CustomClass
- (void)test
{
NSLog(@"這裡是CustomClass");
}
@end
接下來建立一個類別來實現RunTime機制
NSObject+RunTimeTest.h#import <Foundation/Foundation.h>
@interface NSObject (RunTimeTest)
//動態建立一個類,之後為這個類的age屬性賦值。
- (id) testRunTime:(NSString *)classname age:(NSString *)age;
@end
NSObject+RunTimeTest.m
#import "NSObject+RunTimeTest.h"
#import <objc/runtime.h>
#import "CustomClass.h"
@implementation NSObject (RunTimeTest)
- (CustomClass *)testRunTime:(NSString *)classname age:(NSString *)age
{
// propertyCount 成員屬性的數量
unsigned int propertyCount = 0;
/* objc_property_t 根據蘋果文件的解釋:An opaque type that represents an Objective-C declared property.
個人理解:一個任意型別的物件代表一個oc宣告的屬性成員 (本人英語水平實在有限)*/
/* class_copyPropertyList
文件:Return Value
An array of pointers of type objc_property_t describing the properties declared by the class. Any properties declared by superclasses are not included. The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
個人理解:主要2個意思:返回一個數組指標,型別為objc_property_t,用完要記得free,釋放掉。
*/
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
for (unsigned int i = 0; i < propertyCount; i++) {
objc_property_t property = properties[i];
//獲取成員的名稱
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
NSLog(@"propertyName = %@ -- 成員名稱",propertyName);
//獲取成員內容的Ivar
Ivar iVar = class_getInstanceVariable([self class], [propertyName UTF8String]);
//其實上面那行獲取程式碼是為了保險起見,基本是獲取不到內容的。因為成員的名稱預設會在前面加"_" ,
if (iVar == nil) {
iVar = class_getInstanceVariable([self class], [[NSString stringWithFormat:@"_%@",propertyName] UTF8String]);
}
// 取值
id propertyVal = object_getIvar(self, iVar);
NSLog(@"propertyVal = %@ --值",propertyVal);
// 動態建立類
Class varClass = NSClassFromString(classname);
id varObj = [[varClass alloc] init];
//呼叫新建立類的方法,這裡要主要一定引入對應得標頭檔案,不然呼叫方法會不錯。
[varObj test];
Ivar iVarObj = class_getInstanceVariable(varClass, [@"_age" UTF8String]);
object_setIvar(varObj, iVarObj, age);
return varObj;
}
return nil;
}
@end
測試:
ViewController.m#import "NSObject+RunTimeTest.h"
#import "CustomClass.h"
@interface ViewController ()
@property (nonatomic , strong) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_name = @"xiaonan";
CustomClass *tmp = [self testRunTime:@"CustomClass" age:@"098"];
NSLog(@"tmp.age = %@",tmp.age);
}
結果輸出:
2014-11-29 17:16:55.483 TestRunTime2[21001:176367] propertyName = name -- 成員名稱
2014-11-29 17:16:55.483 TestRunTime2[21001:176367] propertyVal = xiaonan --值
2014-11-29 17:16:55.484 TestRunTime2[21001:176367] 這裡是CustomClass
2014-11-29 17:16:55.484 TestRunTime2[21001:176367] tmp.age = 098