亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

iOS-Runtime知識點整理

2019-11-14 18:54:02
字體:
來源:轉載
供稿:網友

1.Runtime簡介

因為Objc是一門動態語言,所以它總是想辦法把一些決定工作從編譯連接推遲到運行時。也就是說只有編譯器是不夠的,還需要一個運行時系統 (runtime system) 來執行編譯后的代碼。這就是 Objective-C Runtime 系統存在的意義,它是整個Objc運行框架的一塊基石。

Runtime其實有兩個版本:“modern”和 “legacy”。我們現在用的 Objective-C 2.0 采用的是現行(Modern)版的Runtime系統,只能運行在 iOS 和 OS X 10.5 之后的64位程序中。而OS X較老的32位程序仍采用 Objective-C 1中的(早期)Legacy 版本的 Runtime 系統。這兩個版本最大的區別在于當你更改一個類的實例變量的布局時,在早期版本中你需要重新編譯它的子類,而現行版就不需要。

Runtime基本是用C和匯編寫的,可見蘋果為了動態系統的高效而作出的努力。你可以在這里下到蘋果維護的開源代碼。蘋果和GNU各自維護一個開源的runtime版本,這兩個版本之間都在努力的保持一致。

2.Runtime相關的頭文件

ios的sdk中 usr/include/objc文件夾下面有這樣幾個文件

List.hNSObjCRuntime.hNSObject.hObject.hPRotocol.ha.txthashtable.hhashtable2.hmessage.hmodule.mapobjc-api.hobjc-auto.hobjc-class.hobjc-exception.hobjc-load.hobjc-runtime.hobjc-sync.hobjc.hruntime.h

都是和運行時相關的頭文件,其中主要使用的函數定義在message.h和runtime.h這兩個文件中。 在message.h中主要包含了一些向對象發送消息的函數,這是OC對象方法調用的底層實現。 runtime.h是運行時最重要的文件,其中包含了對運行時進行操作的方法。 主要包括:

1.操作對象的類型的定義

/// An opaque type that represents a method in a class definition. 一個類型,代表著類定義中的一個方法typedef struct objc_method *Method;/// An opaque type that represents an instance variable.代表實例(對象)的變量typedef struct objc_ivar *Ivar;/// An opaque type that represents a category.代表一個分類typedef struct objc_category *Category;/// An opaque type that represents an Objective-C declared property.代表OC聲明的屬性typedef struct objc_property *objc_property_t;// Class代表一個類,它在objc.h中這樣定義的  typedef struct objc_class *Class;struct objc_class {    Class isa  OBJC_ISA_AVAILABILITY;#if !__OBJC2__    Class super_class                                        OBJC2_UNAVAILABLE;    const char *name                                         OBJC2_UNAVAILABLE;    long version                                             OBJC2_UNAVAILABLE;    long info                                                OBJC2_UNAVAILABLE;    long instance_size                                       OBJC2_UNAVAILABLE;    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;
這些類型的定義,對一個類進行了完全的分解,將類定義或者對象的每一個部分都抽象為一個類型type,對操作一個類屬性和方法非常方便。OBJC2_UNAVAILABLE標記的屬性是Ojective-C 2.0不支持的,但實際上可以用響應的函數獲取這些屬性,例如:如果想要獲取Class的name屬性,可以按如下方法獲取:
Class classPerson = Person.class;// printf("%s/n", classPerson->name); //用這種方法已經不能獲取name了 因為OBJC2_UNAVAILABLEconst char *cname  = class_getName(classPerson);printf("%s", cname); // 輸出:Person

2.函數的定義

對象進行操作的方法一般以object_開頭

進行操作的方法一般以class_開頭

類或對象的方法進行操作的方法一般以method_開頭

成員變量進行操作的方法一般以ivar_開頭

屬性進行操作的方法一般以property_開頭開頭

協議進行操作的方法一般以protocol_開頭

根據以上的函數的前綴 可以大致了解到層級關系。對于以objc_開頭的方法,則是runtime最終的管家,可以獲取內存中類的加載信息,類的列表,關聯對象和關聯屬性等操作。

例如:使用runtime對當前的應用中加載的類進行打印,別被嚇一跳。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    unsigned int count = 0;    Class *classes = objc_copyClassList(&count);    for (int i = 0; i < count; i++) {        const char *cname = class_getName(classes[i]);        printf("%s/n", cname);    }}

3.技術點和應用場景

在以下的代碼中,都用到了Person類,Person類知識簡單的定義了一個成員變量和兩個屬性

@interface Person : NSObject{    @private    float _height;}@property (nonatomic, copy) NSString *name;@property (nonatomic, assign) int age;@end

3_1.獲取屬性/成員變量列表

對于獲取成員變量的列表可以使用class_copyIvarList函數,如果想要獲取屬性列表可以使用class_copyPropertyList函數,使用的示例如下:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {        Class classPerson = NSClassFromString(@"Person"); // 與下面一句效果一樣,可以不用導入頭文件//    Class clazz = Person.class;    unsigned int count = 0;    Ivar *ivarList = class_copyIvarList(classPerson, &count); // 獲取成員變量數組    for (int i = 0; i < count; i++) {        const char *cname = ivar_getName(ivarList[i]); // 獲取成員變量的名字        NSString *name = [NSString stringWithUTF8String:cname];        NSLog(@"%@", name);    }    NSLog(@"-------------------分割線------------------");    objc_property_t *propertyList = class_copyPropertyList(classPerson, &count); // 獲取屬性數組    for (int i = 0; i < count; i++) {        const char *cname = property_getName(propertyList[i]);        NSString *name = [NSString stringWithUTF8String:cname];        NSLog(@"%@", name);    }}
以上代碼的輸出為:
2015-06-05 22:28:16.194 runtime終極[4192:195757] _height2015-06-05 22:28:16.195 runtime終極[4192:195757] _age2015-06-05 22:28:16.195 runtime終極[4192:195757] _name2015-06-05 22:28:16.195 runtime終極[4192:195757] -------------------分割線------------------2015-06-05 22:28:16.195 runtime終極[4192:195757] name2015-06-05 22:28:16.195 runtime終極[4192:195757] age

為什么會有上面的輸出結果,因為@property會做三份工作:
1.生成一個帶下劃線的成員變量
2.生成這個成員變量的get方法
3.生成這個成員變量的set方法

因此會輸出三個成員變量_height、_age和_name。需要注意的是屬性名是不帶下劃線的,和定義時的名字一樣。因此可以說:ivarList可以獲取到@property關鍵字定義的屬性 ,而propertyList不可以獲取到成員變量。也就是:使用ivarList是可以將所有的成員變量和屬性都獲取的。

當屬性是readonly的而且重寫了getter時,這種情況還是會遇見的,比如一個屬性是計算型屬性,需要依賴其他屬性的值計算而來。此時生成的帶下劃線的成員變量就不在了,通過ivarList不能獲取該屬性了。因此當有這種值的時候,無論使用ivarList還是使用propertyList都無法獲取全部的屬性或變量。

在進行下一個話題之前:先需要弄清楚另一個問題:對于一個readonly的屬性,到底是didSet+set好,還是重寫getter好?

大部分的readonly的屬性是計算型的,依舊是依賴于其他屬性,因此可以使用didSet+set,也就是在其他屬性的set方法內,將本屬性set。 但是didSet+set有時候完全沒有必要,不符合懶加載的規則,浪費了計算能力,用重寫getter的方法好一些。 因此重寫getter總是會好一點。

回歸正題:在KVC時,想要獲取全部的成員變量和屬性, 怎么辦呢?

首先要了解setValue: forKeyPath:方法的底層實現:以name屬性為例
1.首先先去類的方法列表去尋找有木有setName:,如果有,就直接調用[person setName:value]
2.找找有沒有帶下劃線的成員變量_name,如果有 _name = value;
3.找有沒有成員變量name,如果有 name = value;
4.如果都沒有找到,就直接報錯。
因此對于readonly的又重寫了getter的屬性而言:如果對propertyList的屬性一次使用kvc,就會報錯,因此為保證代碼正常,不能使用propertyList的屬性進行kvc;
另外:這種屬性本來就是計算型的了,為什么還有為它賦值呢,因此對它進行kvc也不合情理。
當使用ivaList時,直接就無法獲取到這種屬性,因此是kvc的最佳方案。再者,使用propertyList無法獲取成員變量(_height),無法對成員變量進行賦值。而使用ivaList是可以將該賦值的成員變量都獲取的。

以上就是使用ivar還是使用property進行kvc的論證。

話題外: 很多類 有些成員變量 既沒有暴露給外部調用的getter又沒有setter,只是用@private聲明了一下:為什么??
猜測是:是方法調用時使用的中間變量,因為是跟隨對象產生,不適合使用靜態static,又因為外部不會使用,所以沒必要給外部提供接口,但是可能有好幾個方法都需要這個量,不適合做局部變量,所以就這樣定義了。

對于這種情況,要想不對這種成員變量賦值,在KVC時又可以這樣改進一下,通過ivarList獲取,去掉propertyList中沒有的成員變量,這樣就過濾掉了上面的那種成員變量了。

3_1_1.應用1:KVC字典轉模型

獲取屬性/成員列表一個重要的應用就是,一次取出模型中的屬性/成員變量,根據它的名字獲取字典中的key然后取出字典中這個key對應的value,使用setValue: forKeyPath:方法設置值。為什么要這樣,而不再使用方法setValuesForKeysWithDictionary:。因為在setValuesForKeysWithDictionary:方法內部會執行這樣一個過程
遍歷字典里面的所有key,一個一個取出來,遍歷每個key按照以下過程
1.取出key,
2.取出key的value,即dict[key],直接給模型的屬性/成員變量賦值
3.怎么給模型的屬性賦值,使用方法setValue:value forKeyPath:key進行賦值,這個方法的執行過程在前面已經提到。

因此,開發中經常遇到的字典中的key比模型中多時,會出現的 this class is not key-value compliant for ‘xxx’這個bug就很好解釋了,通常是因為字典中的key,比模型中的屬性/成員變量多。那么當模型中的屬性比字典中多時,使用setValuesForKeysWithDictionary:會不不會有bug呢?經測試:當多出來的屬性是對象數據類型時,為null,當屬性是基本數據類型時,會有一個系統默認值(如int為0)。

因此使用逐一為屬性賦值的方法進行KVC:

Class clazz = Person.class;unsigned int count = 0;Person *person = [[Person alloc]init];NSDictionary *dict = @{@"name":@"zhangsan",@"age":@19, @"height": @1.75};Ivar *ivars = class_copyIvarList(clazz, &count);// NSLog(@"%tu", count); // 3for (int i = 0; i < count; i++) {    const char *cname = ivar_getName(ivars[i]);    NSString *name = [NSString stringWithUTF8String:cname];    NSString *key = [name substringFromIndex:1]; // 去掉'_'    [person setValue:dict[key] forKey:key];}NSLog(@"%@", person); // 已經重寫了description方法
輸出是:
<Person, 0x7ff15b80f230>{ name = zhangsan, height = 1.750000, age = 19}

使用這種方式進行kvc,即使字典中的key多的時候也不會有bug,但是新的問題出現了,如果模型中的屬性比字典中的key多便會出現bug而且:如果多的是對象類型不會有bug,該屬性的值為null,如果是基本數據類型就會出錯could not set nil as the value for the key ‘xxx’例如,將上面的字典改為:

NSDictionary *dict = @{@"age":@19, @"height": @1.75}; // 去掉了name NSString類型
修改之后輸出為:
<Person, 0x7f996263fbd0>{ name = (null), height = 1.750000, age = 19}
如果將字典改為:
NSDictionary *dict = @{@"name":@"zhangsan",@"age":@19}; // 去掉了height float類型
程序直接崩潰。

如何解決上面的bug:可以在setValue:value forKeyPath:key方法調用之前進行如下處理:取出屬性對應的類型,如果類型是基本數據類型,value替換為默認值(如int對應默認值為0)。

runtime提供的ivar_getTypeEncoding函數可以獲取到屬性的類型,返回值代表的含義如下:


height是float類型對應的TypeCode是"f"因此可以進行過濾一下,代碼改動如下:
Class clazz = Person.class;unsigned int count = 0;Person *person = [[Person alloc]init];NSDictionary *dict = @{@"name":@"zhangsan",@"age":@19, @"height": @1.75};Ivar *ivars = class_copyIvarList(clazz, &count);for (int i = 0; i < count; i++) {    const char *cname = ivar_getName(ivars[i]);    NSString *name = [NSString stringWithUTF8String:cname];    NSString *key = [name substringFromIndex:1];        const char *coding = ivar_getTypeEncoding(ivars[i]); // 獲取類型    NSString *strCode = [NSString stringWithUTF8String:coding];    id value = dict[key];    if ([strCode isEqualToString:@"f"]) {// 判斷類型是否是float        value = @(0.0);    }        [person setValue:value forKey:key];}NSLog(@"%@", person);
這樣就可以正常執行了,輸出為:
<Person, 0x7fc75d004a00>{ name = zhangsan, height = 0.000000, age = 19}

3_1_2.應用2:NSCoding歸檔和解檔

獲取屬性/成員列表另外一個重要的應用就是進行歸檔和解檔,其原理和上面的kvc基本上一樣,這里只是展示一些代碼:
- (void)encodeWithCoder:(NSCoder *)aCoder {    unsigned int count = 0;    Ivar *ivars = class_copyIvarList(self.class, &count);    for (int i = 0; i < count; i++) {        const char *cname = ivar_getName(ivars[i]);        NSString *name = [NSString stringWithUTF8String:cname];        NSString *key = [name substringFromIndex:1];                id value = [self valueForKey:key]; // 取出key對應的value        [aCoder encodeObject:value forKey:key]; // 編碼    }}- (id)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++) {            const char *cname = ivar_getName(ivars[i]);            NSString *name = [NSString stringWithUTF8String:cname];            NSString *key = [name substringFromIndex:1];                        id value = [aDecoder decodeObjectForKey:key]; // 解碼            [self setValue:value forKey:key]; // 設置key對應的value        }    }    return self;    }

3_2.交換方法實現

交換方法實現的需求場景:自己創建了一個功能性的方法,在項目中多次被引用,當項目的需求發生改變時,要使用另一種功能代替這個功能,要求是不改變舊的項目(也就是不改變原來方法的實現)。

可以在類的分類中,再寫一個新的方法(是符合新的需求的),然后交換兩個方法的實現。這樣,在不改變項目的代碼,而只是增加了新的代碼 的情況下,就完成了項目的改進。

交換兩個方法的實現一般寫在類的load方法里面,因為load方法會在程序運行前加載一次,而initialize方法會在類或者子類在 第一次使用的時候調用,當有分類的時候會調用多次。

// 程序一運行的時候調用+ (void)load{	// 如果是類方法,使用的是class_getClassMethod,如果是對象方法使用的是class_getInstanceMethod    Method methodOne = class_getInstanceMethod(self, @selector(methodOne:));    Method methodTwo = class_getInstanceMethod(self, @selector(methodTwo:));    // 交換兩個方法的實現    method_exchangeImplementations(methodOne, methodTwo);}
注意的是
1.可以交換的兩個方法的參數必須是匹配的,參數的類型一致。
2.如果在方法one的內部想要調用方法two,此時在方法one的內部應該用one調用,而實際上是在調用two,否則會造成死循環。
例如:
// 交換前- (NSString *) methodOne:(NSString *)str{	NSLog(@"%@", [self methodTwo:str]);	return "suc";}// 交換后 在方法的實現中,要注意將調用two的地方,換成自己的名字- (NSString *) methodOne:(NSString *)str{	NSLog(@"%@", [self methodOne:str]);	return "suc";}
任何一個方法都有兩個重要的屬性:SEL是方法的編號 ,IMP是方法的實現,方法的調用過程實際上去根據SEL去尋找IMP。
在這個例子中,假設在交換之前SEL為methodOne:的方法指向著IMP1,SEL為methodTwo的方法指向著IMP2。
交換實現實際上是在底層是交換了方法編號的指向,也就是讓methodOne:指向了IMP2,methodTwo指向了IMP1。

應用場景

3_3.類/對象的關聯對象

關聯對象不是為類/對象添加屬性或者成員變量(因為在設置關聯后也無法通過ivarList或者propertyList取得),而是為類添加一個相關的對象,通常用于存儲類信息,例如存儲類的屬性列表數組,為將來字典轉模型的方便。例如,將屬性的名稱存到數組中設置關聯
const char* propertiesKey = "propertiesKey";unsigned int count = 0;Ivar *ivars = class_copyIvarList([Person class], &count);NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];for (unsigned int i = 0; i < count; ++i) {    Ivar pty = ivars[i];    const char *cname = ivar_getName(ivars[i]);    NSString *name = [NSString stringWithUTF8String:cname];    NSString *key = [name substringFromIndex:1]; // 去掉_    [arrayM addObject:key];}free(ivars);objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);NSLog(@"%@", arrayM);
輸出是
(    age,    height,    name)

objc_setAssociatedObject方法的參數解釋:

第一個參數id object, 當前對象
第二個參數const void *key, 關聯的key,是c字符串
第三個參數id value, 被關聯的對象
第四個參數objc_AssociationPolicy policy關聯引用的規則,取值有以下幾種:
enum {   OBJC_ASSOCIATION_ASSIGN = 0,   OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,   OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   OBJC_ASSOCIATION_RETAIN = 01401,   OBJC_ASSOCIATION_COPY = 01403};
如果想要獲取已經關聯的對象,通過key取得即可
NSArray *pList = objc_getAssociatedObject(Person, propertiesKey);
可以將以上兩種操作封裝起來,為Person類增加一個properties類方法,封裝上面的操作,用于方便獲取類的屬性列表。
const char* propertiesKey = "propertiesKey";@implementation Person+ (NSArray *)properties {    // 如果已經關聯了,就依據key取出被關聯的對象并返回    NSArray *pList = objc_getAssociatedObject(self, propertiesKey);    if (pList != nil) {        return pList;    }    // 如果沒有關聯,則設置關聯對象,并將對象返回    unsigned int count = 0;    Ivar *ivars = class_copyIvarList([self class], &count);        NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];        for (unsigned int i = 0; i < count; ++i) {        Ivar pty = ivars[i];        const char *cname = ivar_getName(ivars[i]);        NSString *name = [NSString stringWithUTF8String:cname];        NSString *key = [name substringFromIndex:1];        [arrayM addObject:key];    }    free(ivars);    objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);    return arrayM.copy;}@end

3_4.動態添加方法,攔截未實現的方法

每個類都有都有一下兩個類方法(來自NSObject)
+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel
以上兩個一個使用于類方法,一個適用于對象方法。在代碼中調用沒有實現的方法時,也就是sel標識的方法沒有實現都會現調用這兩個方法中的一個(如果是類方法就調用第一個,如果是對象方法就調用第二個)攔截。通常的做法是在resolve的內部指定sel對應的IMP,從而完成方法的動態創建和調用兩個過程,也可以不指定IMP打印錯誤信息后直接返回。

假如在Person類中沒有-sayHi這個方法,如果對象p使用[p performSelector:@selector(sayHi) withObject:nil];那么就會必須經過Person類的resolveInstanceMethod:(SEL)sel方法,在這里為-sayHi指定實現。

void abc(id self, SEL _cmd){    NSLog(@"%@說了hello", [self name]);}@implementation Person//動態添加方法:在resolve中添加相應的方法,注意是類方法還是對象方法。+ (BOOL)resolveInstanceMethod:(SEL)sel{    if ([NSStringFromSelector(sel) isEqualToString:@"sayHi"]) {        class_addMethod(self, sel, abc, "v@:"); // 為sel指定實現為abc    }    return YES;}@end

對實現(abc)的前兩個參數的說明

每個方法的內部都默認包含兩個參數,被稱為隱式參數
id類型self(代表類或對象)和SEL類型的_cmd(方法編號)

class_addMethod函數參數的含義:

第一個參數Class cls, 類型
第二個參數SEL name, 被解析的方法
第三個參數 IMP imp, 指定的實現
第四個參數const char *types,方法的類型,具體參照類型的codeType那張圖,但是要注意一點:Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).譯為:因為函數必須至少有兩個參數self和_cmd,第二個和第三個字符必須是“@:”。如果想要再增加參數,就可以從實現的第三個參數算起,看下面的例子就明白。
返回值:YES if the method was found and added to the receiver, otherwise NO.

為-sayHi方法的實現增加參數

調用時:
Person *p = [[Person alloc] init];    p.name = @"zhangsan";p.age = 10;[p performSelector:@selector(sayHi:) withObject:@"world"]; // 增加了一個參數,多了冒號
對Person類中的代碼做了修改
void abc(id self, SEL _cmd, NSString *content){ // 增加了一個參數content    NSLog(@"%@說了hello%@", [self name], content);}@implementation Person//動態添加方法:在resolve中添加相應的方法,注意是類方法還是對象方法。+ (BOOL)resolveInstanceMethod:(SEL)sel{    if ([NSStringFromSelector(sel) isEqualToString:@"sayHi:"]) {        class_addMethod(self, sel, abc, "v@:@"); // 增加了一個對象類型參數 增加了@    }    return YES;}@end
輸出為:
zhangsan說了helloworld

3_5.動態創建一個類

動態創建一個類,為這個類添加成員變量和方法,并創建這個類型的對象:
#import "ViewController.h"#import <objc/runtime.h>#import <objc/message.h>#import "Person.h"static void printSchool(id self, SEL _cmd) {    NSLog(@"我的學校是%@", [self valueForKey:@"schoolName"]);}@implementation ViewController- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    Class classStudent = objc_allocateClassPair(Person.class, "Student", 0);        // 添加一個NSString的變量,第四個參數是對其方式,第五個參數是參數類型    if (class_addIvar(classStudent, "schoolName", sizeof(NSString *), 0, "@")) {        NSLog(@"添加成員變量schoolName成功");    }        // 為Student類添加方法 "v@:"這種寫法見參數類型連接    if (class_addMethod(classStudent, @selector(printSchool), (IMP)printSchool, "v@:")) {        NSLog(@"添加方法printSchool:成功");    }        // 注冊這個類到runtime系統中就可以使用他了    objc_registerClassPair(classStudent); // 返回void                // 使用創建的類    id student = [[classStudent alloc] init];    NSString *schoolName = @"清華大學";    // 給剛剛添加的變量賦值    // object_setInstanceVariable(student, "schoolName", (void *)&str);在ARC下不允許使用    [student setValue:schoolName forKey:@"schoolName"];    // 調用printSchool方法,也就是給student這個接受者發送printSchool:這個消息//    objc_msgSend(student, "printSchool"); // 我嘗試用這種方法調用但是沒有成功    [student performSelector:@selector(printSchool) withObject:nil]; // 動態調用未顯式在類中聲明的方法    }@end
輸出的結果是:
添加成員變量schoolName成功添加方法printSchool成功我的學校是清華大學

面試題

說說什么是runtime

1>OC 是一個全動態語言,OC 的一切都是基于 Runtime 實現的
平時編寫的OC代碼, 在程序運行過程中, 其實最終都是轉成了runtime的C語言代碼, runtime算是OC的幕后工作者
比如:
OC :
[[Person alloc] init]
runtime :
objc_msgSend(objc_msgSend("Person" , "alloc"), "init")
2>runtime是一套比較底層的純C語言API, 屬于1個C語言庫, 包含了很多底層的C語言API
3>runtimeAPI的實現是用 C++ 開發的(源碼中的實現文件都是mm),是一套蘋果開源的框架

使用過runtime嗎,用它來做什么。

本文二、三部分。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美亚洲成人精品| 日韩黄在线观看| 久色乳综合思思在线视频| 69国产精品成人在线播放| 亚洲欧美日韩在线高清直播| 激情av一区二区| 色妞欧美日韩在线| 日本久久精品视频| 欧美孕妇性xx| 亚洲日本欧美日韩高观看| 欧美重口另类videos人妖| 日韩高清免费观看| 欧美性猛交xxxx乱大交蜜桃| 亚洲欧美另类中文字幕| 色偷偷偷亚洲综合网另类| 精品国产一区二区三区久久| 久久久亚洲影院你懂的| 精品电影在线观看| 欧美极品第一页| 色综合视频网站| 亚洲第一页中文字幕| 亚洲自拍偷拍色片视频| 国内精品美女av在线播放| 亚洲欧美国产精品专区久久| 欧美日韩国产精品一区二区不卡中文| 久久在线免费观看视频| 国产精品av网站| 日韩大片免费观看视频播放| 日韩成人中文字幕在线观看| 国产69精品久久久| 欧美一区三区三区高中清蜜桃| 日本a级片电影一区二区| 亚洲欧美激情视频| 亚洲视频精品在线| 青青在线视频一区二区三区| 午夜美女久久久久爽久久| 国产中文字幕91| 亚洲欧美日韩视频一区| 久久99亚洲精品| 欧美洲成人男女午夜视频| 日韩电影中文字幕一区| 日韩av在线网址| 欧美午夜女人视频在线| 久久国产精品久久国产精品| 国产精品第3页| 国产91|九色| 午夜精品一区二区三区在线视频| 精品成人69xx.xyz| 亚洲mm色国产网站| 精品国产一区二区三区四区在线观看| 国产精品免费电影| 亚洲韩国日本中文字幕| 欧美在线一级视频| 日韩精品免费在线视频观看| 98精品国产自产在线观看| 亚洲男人天堂九九视频| 97久久久久久| 国产成人拍精品视频午夜网站| 亚洲欧洲免费视频| 国产精品无av码在线观看| 在线视频中文亚洲| 精品国产一区二区三区四区在线观看| 日本久久久久亚洲中字幕| 亚洲视频欧美视频| 欧美人交a欧美精品| 久久大大胆人体| 久久免费精品日本久久中文字幕| 欧美日韩xxxxx| 欧美激情亚洲自拍| 亚洲第一免费网站| 国产精品直播网红| 91精品久久久久久久久久另类| 欧美野外猛男的大粗鳮| 欧美午夜宅男影院在线观看| 国产精品一二区| 欧美日韩电影在线观看| 国产精品久久一区| 国产精品电影观看| 日韩精品免费在线播放| 日韩av一卡二卡| 成人看片人aa| 欧美电影在线观看网站| 91久久嫩草影院一区二区| 久久伊人精品天天| 亚洲色图校园春色| 国产欧美日韩亚洲精品| 国产日韩欧美在线视频观看| 亚洲视频在线免费看| 欧美日韩性视频在线| 国产精品免费小视频| 一本色道久久88亚洲综合88| 成人在线播放av| 国产精品福利在线观看| 亚洲wwwav| 亚洲第一福利视频| 国产不卡精品视男人的天堂| 欧美男插女视频| 91免费观看网站| 亚洲第一区在线观看| 欧美国产一区二区三区| 正在播放欧美视频| 国产日韩精品综合网站| 国产成人精品网站| 欧美在线欧美在线| 国内精品模特av私拍在线观看| 91九色单男在线观看| 久久亚洲私人国产精品va| 成人www视频在线观看| 久久久精品一区二区| 自拍偷拍亚洲区| 久久免费视频观看| 尤物九九久久国产精品的分类| 国产欧美精品va在线观看| 一区二区三区无码高清视频| 66m—66摸成人免费视频| 78m国产成人精品视频| 亚洲国产精品va在线观看黑人| 日本乱人伦a精品| 日韩欧美在线一区| 黑人巨大精品欧美一区二区三区| 97婷婷大伊香蕉精品视频| 日韩中文字幕不卡视频| 亚洲男人天堂久| 激情亚洲一区二区三区四区| 在线观看久久久久久| 亚洲欧美中文在线视频| 热久久视久久精品18亚洲精品| 久久九九有精品国产23| 欧美风情在线观看| 成人黄色免费在线观看| 亚洲欧美一区二区三区情侣bbw| 日韩免费观看视频| 国产精品久久久久久久久久久新郎| 亚洲国产精久久久久久久| 国产网站欧美日韩免费精品在线观看| 亚洲3p在线观看| 日韩亚洲欧美中文高清在线| 这里只有精品丝袜| 欧美高清在线视频观看不卡| 91日韩在线视频| 欧美理论电影网| 日韩av影片在线观看| 欧美剧在线观看| 亚洲aⅴ男人的天堂在线观看| 成人免费视频在线观看超级碰| 亚洲成人网在线| 91香蕉嫩草神马影院在线观看| 亚洲欧美精品一区二区| 亚洲欧洲美洲在线综合| 欧美日韩中文在线观看| 久久影视电视剧凤归四时歌| 欧美日韩中文在线观看| 欧美在线视频观看| 国内精品久久久| 精品中文字幕在线| 日韩精品极品视频| 久久成人精品视频| 亚洲毛片在线观看| 久久久女人电视剧免费播放下载| 78色国产精品| 国产精品久久久久免费a∨| 久久久精品免费视频| 亚洲精品自产拍| 亚洲国产天堂久久国产91|