1. 程式人生 > >iOS中runtime執行機制解析

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