<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                1. 概念 runtime(運行時系統),是一套基于C語言API,包含在 <objc/runtime.h>和<objc/message.h>中,運行時系統的功能是在運行期間(而不是編譯期或其他時機)通過代碼去動態的操作類(獲取類的內部信息和動態操作類的成員),如創建一個新類、為某個類添加一個新的方法或者為某個類添加實例變量、屬性,或者交換兩個方法的實現、獲取類的屬性列表、方法列表等和Java中的反射技術類似。 2. 探索 程序最終運行的是二進制的可執行文件,編譯器需要將OC代碼轉換為運行時代碼,再將運行時代碼經過一些處理成最終的二進制可執行文件 mian.m int main(int argc, const char * argv[]) { @autoreleasepool { [[NSObject alloc] init]; } return 0; } 使用終端命令行切換到mian.m文件所在的目錄下并執行: clang -rewrite-objc main.m 在mian.m文件目錄所在的位置會有一個 main.cpp文件,打開文件可以看到OC代碼都被轉換成運行時runtime代碼了 runtime.h和message.h中的方法一般以objc_、 class_、 method_、 property_、 ivar_、 protocol_、object_、 sel_等作為前綴,用前綴表明操作的對象 3.常用功能 1.動態交換兩個方法的實現 2.動態添加對象的成本變量和成員方法 3.獲取某個類的所有成員變量和成員方法 4.實現NSCoding的自動歸檔和自動解檔 5.實現字典和模型的自動轉換 6.為類別添加屬性(我們知道類別是不能擴展屬性的,只能擴展方法,但可以運行時可以實現,通過為類增加屬性) 4.runtime常用的數據類型 OC源代碼最終會翻譯成運行時代碼,而runtime是一套C語言API,也就是說OC的數據類型最終也會翻譯成C語言中的數據類型 Objective-C -------> runtime 類(Class) objc_class* id objc_object* 方法(Method) objc_method 變量(Ivar) objc_ivar* struct objc_class { Class isa; Class super_class const char *name long version long info long instance_size struct objc_ivar_list *ivars struct objc_method_list **methodLists struct objc_cache *cache struct objc_protocol_list *protocols }; typedef struct objc_class* Class; struct objc_object { Class isa; }; typedef struct objc_object* id; struct objc_method { SEL method_name, // 方法名 char* method_types, // 方法的參數類型 IMP method_imp // 方法實現代碼的指針 }; typedef objc_method Method; struct objc_ivar { char *ivar_name char *ivar_type int ivar_offset int space } typedef struct objc_ivar* Ivar; 5.runtime 常用API // 創建類對 Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) // 添加方法 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) // 添加屬性 BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types) // 注冊類對 void objc_registerClassPair(Class cls) // 向某個對象發送某個消息 id objc_msgSend(id self, SEL op, ...) // 獲取某個類的類方法 Method class_getClassMethod(Class cls, SEL name) // 獲取某個類的實例方法 Method class_getInstanceMethod(Class cls, SEL name) // 為類添加一個屬性 void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) // 獲取屬性對應的值 id objc_getAssociatedObject(id object, const void *key) // 獲取實例變量列表 Ivar *class_copyIvarList(Class cls, unsigned int *outCount) // 獲取方法的類型(方法的簽名,返回值類型,參數類型) const char *method_getTypeEncoding(Method m) 6.使用runtime 示例1:交換兩個方法的實現 <span style="font-weight: normal;">#import <Foundation/Foundation.h> @interface Person : NSObject @property (copy, nonatomic) NSString *name; @property (copy, nonatomic) NSString *address; @property (assign, nonatomic)int age; @property (assign, nonatomic)double hight; @property (assign, nonatomic)BOOL gender; + (void)run; - (void)study; @end //-------------------------------------------------------------------------------------- #import "Person.h" @implementation Person + (void)run { NSLog(@"run。。。"); } - (void)study { NSLog(@"study..."); } @end //-------------------------------------------------------------------------------------- int main(int argc, const char * argv[]) { @autoreleasepool { Method runMethod = class_getClassMethod([Person class], @selector(run)); Method studyMethod = class_getInstanceMethod([Person class], @selector(study)); method_exchangeImplementations(runMethod, studyMethod); [Person run]; // 打印 study... [[[Person alloc] init] study]; // 打印 run。。。 } return 0; }</span> 實例2:為類添加屬性 分類(類別)是用來擴展方法的,不能擴展屬性,但并不是說類別中不能寫 @proprty, 如果類別中有 @proprty,意思是說為該屬性生產getter&&setter方法,但是不生成帶下劃線的實例變量。 新建一個類別為列表添加屬性,并重寫getter&&setter #import <Foundation/Foundation.h> #import <objc/runtime.h> @interface Person (AddProperty) @property (copy, nonatomic) NSString *name; @end #import "Person+AddProperty.h" @implementation Person (AddProperty) - (void)setName:(NSString *)name { objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self, "name"); } @end //------------------------------------------------------------------------------ #import <Foundation/Foundation.h> #import "Person+AddProperty.h" #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [[Person alloc] init]; person.name = @"小紅"; NSLog(@"使用類別(分類)間接為類添加屬性, person.name = %@", person.name); } return 0; } 程序解釋:當程序通過點語法調用 person.name = @"小紅" 的時候實際上是執行的[person setName:@"小紅"]; 即調用相應的Set方法,該方法使用runtime動態的為類關聯一個屬性并賦值;當執行person.name 的時候, 實際上是執行[person name]; 即調用相應的Get方法,使用runtime獲取關聯的屬性值。 使用類別也是可以做到為類添加屬性的。(當會的知識增多時,就會發現之前認為對的可能都是錯的) 示例3:獲取實例變量列表 int main(int argc, const char * argv[]) { @autoreleasepool { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([Person class], &outCount); for (int i = 0; i < outCount; i++) { Ivar ivar = ivars[i]; const char* name = ivar_getName(ivar); const char* type = ivar_getTypeEncoding(ivar); NSLog(@"%s :%s", name, type); } free(ivars); } return 0; } 2016-07-19 17:48:28.788 Runtime[13864:3404989] _gender :c 2016-07-19 17:48:28.789 Runtime[13864:3404989] _age :i 2016-07-19 17:48:28.789 Runtime[13864:3404989] _address :@"NSString" 2016-07-19 17:48:28.789 Runtime[13864:3404989] _hight :d Program ended with exit code: 0 示例4:動態創建類并添加方法 使用運行時系統API以動態方式創建類的步驟: 1.新建一個類及元類 2.向這個類添加方法和實例變量 3.注冊新建的類 static void display(id self, SEL _cmd){ NSLog(@"invoke method with selector %@ on %@ instance", NSStringFromSelector(_cmd), [self class]); } int main(int argc, const char * argv[]) { @autoreleasepool { // 1. 創建一個類對 Class WidgetClass = objc_allocateClassPair([NSObject class], "Widget", 0); // 2. 為該類添加一個方法 class_addMethod(WidgetClass, @selector(display), (IMP)display, "v@:"); // 3. 為該類添加一個實例變量 class_addIvar(WidgetClass, "height", sizeof(id), rint(log2(sizeof(id))), @encode(id)); // 4. 注冊類對 objc_registerClassPair(WidgetClass); // 5. 創建實例變量并賦值、調用方法 id widge = [[WidgetClass alloc] init]; [widge setValue:@(15) forKey:[NSString stringWithUTF8String:"height"]]; NSLog(@"Widge instance height = %@", [widge valueForKey:[NSString stringWithUTF8String:"height"]]); objc_msgSend(widge, NSSelectorFromString(@"display")); // 6. 動態方式添加一個屬性 objc_setAssociatedObject(widge, @"width", @(10), OBJC_ASSOCIATION_RETAIN_NONATOMIC); // 7. 獲取 id result = objc_getAssociatedObject(widge, @"width"); NSLog(@"Widget instance width = %@", result); } return 0; } 示例5:歸檔解檔 先來看一個常用的寫法: #import <Foundation/Foundation.h> @interface Student : NSObject <NSCoding> @property (copy, nonatomic) NSString *name; @property (assign, nonatomic)int age; @property (assign, nonatomic)double weight; @property (copy, nonatomic)NSArray *hobby; @property (copy, nonatomic)NSDictionary *others; @end #import "Student.h" #define knameKey @"name" #define kageKey @"age" #define kweightKey @"weight" #define khobbyKey @"hobby" #define kothersKey @"others" @implementation Student - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_name forKey:knameKey]; [aCoder encodeInt:_age forKey:kageKey]; [aCoder encodeDouble:_weight forKey:kweightKey]; [aCoder encodeObject:_hobby forKey:khobbyKey]; [aCoder encodeObject:_others forKey:kothersKey]; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { _name = [aDecoder decodeObjectForKey:knameKey]; _age = [aDecoder decodeIntForKey:kageKey]; _weight = [aDecoder decodeDoubleForKey:kweightKey]; _hobby = [aDecoder decodeObjectForKey:khobbyKey]; _others = [aDecoder decodeObjectForKey:kothersKey]; } return self; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[Student alloc] init]; student.name = @"小紅"; student.age = 25; student.weight = 100.5; student.hobby = @[@"吃", @"喝", @"玩", @"樂"]; student.others = @{@"phone": @"1234567890", @"wechat": @"123456"}; NSString *path = @"/Users/macmini/Documents/Test/Student.plist"; [NSKeyedArchiver archiveRootObject:student toFile:path]; Student *xiaoming = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; NSLog(@"xiaoming:%@", xiaoming); } return 0; } 普通的寫法每個類如果要歸檔解檔的話都要實現NSCoding的協議方法,該方法的實現都很類似,可以使用runtime進行簡化操作,盡可能的達到至簡 #import "Student.h" #import <objc/runtime.h> @implementation Student // 獲取所有成員變量進行循環編碼 - (void)encodeWithCoder:(NSCoder *)aCoder { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i++) { NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])]; id value = [self valueForKey:key]; [aCoder encodeObject:value forKey:key]; } free(ivars); } - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i++) { NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])]; id value = [self valueForKey:key]; [self setValue:value forKey:key]; } free(ivars); } return self; } @end 普通寫法每個類都要寫一次,使用runtime雖然每個類都要寫一次,但是代碼都是完全一樣的,可以直接粘貼復制, 既然都是一樣的,我們可以使用類別(分類)給NSObject增加這兩個方法,這樣就能簡化代碼 //該實現也實現了對父類屬性進行歸檔解檔的實現 #import "NSObject+Archive.h" #import <objc/runtime.h> @implementation NSObject (Archive) // 先對當前類進行編碼,然后對父類進行編碼,如果父類是NSObject就結束編碼 - (void)encode:(NSCoder *)aCoder { Class clazz = self.class; while (clazz && clazz != [NSObject class]) { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList(clazz, &outCount); for (int i = 0; i < outCount; i++) { NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])]; id value = [self valueForKey:key]; [aCoder encodeObject:value forKey:key]; } free(ivars); clazz = [clazz superclass]; } } - (void)decode:(NSCoder *)aDecoder { Class clazz = self.class; while (clazz && clazz != [NSObject class]) { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList(clazz, &outCount); for (int i = 0; i < outCount; i++) { NSString *key = [NSString stringWithUTF8String:ivar_getName(ivars[i])]; id value = [aDecoder decodeObjectForKey:key]; [self setValue:value forKey:key]; } free(ivars); clazz = [clazz superclass]; } } @end #import <Foundation/Foundation.h> #import "Person.h" // 繼承Person類 @interface Student : Person <NSCoding> @property (copy, nonatomic) NSString *name; @property (assign, nonatomic)double weight; @property (copy, nonatomic)NSArray *hobby; @property (copy, nonatomic)NSDictionary *others; @end #import "Student.h" #import "NSObject+Archive.h" #import <objc/runtime.h> @implementation Student - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { [self decode:aDecoder]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [self encode:aCoder]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[Student alloc] init]; student.name = @"小紅"; student.age = 25; student.weight = 100.5; student.hobby = @[@"吃", @"喝", @"玩", @"樂"]; student.others = @{@"phone": @"1234567890", @"wechat": @"123456"}; student.address = @"父類屬性address"; student.hight = 180.5; student.gender = YES; NSString *path = @"/Users/macmini/Documents/Test/Student.plist"; [NSKeyedArchiver archiveRootObject:student toFile:path]; Student *xiaoming = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; NSLog(@"xiaoming:%@", xiaoming); } return 0; } 該方式可以簡化每個類中歸檔和解檔的代碼量,但仍可以進行再簡化,就是將NSCoding的實現定義成宏 創建一個.h文件 #ifndef Coding_h #define Coding_h #import "NSObject+Archive.h" #define CodingImplemention \ - (instancetype)initWithCoder:(NSCoder *)aDecoder {\ if (self = [super init]) {\ [self decode:aDecoder];\ }\ return self;\ }\ \ - (void)encodeWithCoder:(NSCoder *)aCoder {\ [self encode:aCoder];\ } #endif /* Coding_h */ #import "Student.h" #import "Coding.h" @implementation Student CodingImplemention @end 在Student.m 文件中只需一個單詞即可實現歸檔解檔,可以看到已經達到至簡了 示例6:使用runtime將字典轉為模型 字典轉模型需要考慮三種特殊情況 1.當字典中的key和模型的屬性匹配不上 2.模型中嵌套模型 3.數組中的元素是模型 第一種情況分:當字典中字段多個類中的屬性時,不用做任何處理,因為runtime是獲取類中的所有 屬性并循環的,字典中多的就不用管了。當類的字段多于字典中的字段,根據該字段去屬性中獲取值如果獲取不到就繼續下次循環; 第二種情況:利用runtime的ivar_getTypeEncoding方法獲取實例變量的數據類型,如果是自定義的類也需要調用轉換 第三種情況:第二種情況能夠判斷數據類型,就可以知道是否為數組,但是我們不知道數組里面的數據類型,我們可以提供一個函數,讓用戶指定數組元素的數據類型 `@interface School : NSObject @property (copy, nonatomic)NSString * ID; @property (copy, nonatomic)NSString * name; @end #import "School.h" @implementation School @end #import <Foundation/Foundation.h> @interface Address : NSObject @property (copy, nonatomic)NSString * ID; @property (copy, nonatomic)NSString * address; @end #import "Address.h" @implementation Address @end #import <Foundation/Foundation.h> #import "School.h" @interface User : NSObject @property (copy, nonatomic)NSString * ID; @property (copy, nonatomic)NSString * name; @property (assign, nonatomic)int age; @property (strong, nonatomic)School *school; @property (copy, nonatomic)NSArray *address; @end #import "User.h" @implementation User - (NSDictionary *)eleTypeForArray { return @{@"address": @"Address"}; } @end #import <Foundation/Foundation.h> @interface NSObject (Dict2Model) - (void)setDict:(NSDictionary *)dict; + (instancetype)initWithDict:(NSDictionary *)dict; - (NSDictionary *)eleTypeForArray; @end #import "NSObject+Dict2Model.h" #import <objc/runtime.h> @implementation NSObject (Dict2Model) - (void)setDict:(NSDictionary *)dict { Class clazz = self.class; while (clazz && clazz != [NSObject class]) { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList(clazz, &outCount); for (int i = 0; i < outCount; i++) { Ivar ivar = ivars[i]; NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)]; key = [key substringFromIndex:1]; // 去掉實例變量中的下劃線 id value = dict[key]; NSLog(@"%d: key:%@ value:%@", i, key, value); // 1. 如果類的實例變量多于字典中key if (value == nil) { continue; } // 2. 實例變量類型以@開頭并且前綴不是NS開頭的(排除系統類) "@Student" NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; NSRange range = [type rangeOfString:@"@"]; if (range.location != NSNotFound) { type = [type substringWithRange:NSMakeRange(2, type.length - 3)]; if (![type hasPrefix:@"NS"]) { Class class = NSClassFromString(type); value = [class initWithDict:value]; } else if ([type isEqualToString:@"NSArray"]) { NSArray *array = (NSArray *)value; NSMutableArray *mArray = [NSMutableArray array]; id class; if ([self respondsToSelector:@selector(eleTypeForArray)]) { NSString *eleTypeStr = [[self eleTypeForArray] objectForKey:key]; class = NSClassFromString(eleTypeStr); } else { NSLog(@"數組類型不明確!"); return; } for (int i = 0; i < array.count; i++) { id obj = [class initWithDict:value[i]]; [mArray addObject:obj]; } value = mArray; } } [self setValue:value forKeyPath:key]; } free(ivars); clazz = [clazz superclass]; } } + (instancetype)initWithDict:(NSDictionary *)dict { NSObject *obj = [[self alloc] init]; [obj setDict:dict]; return obj; } @end Student.json { "name" : "Tom", "age" : 20, "weight" : "181", "school":{ "ID":1, "name":"北京大學" }, "address" : [ { "ID":1, "address":"上海市" }, { "ID":2, "address":"北京市" } ] } int main(int argc, const char * argv[]) { @autoreleasepool { NSData *jsonData = [NSData dataWithContentsOfFile:@"/Users/macmini/Documents/Test/RuntimeWidget/RuntimeWidget/Student.json"]; NSDictionary *userDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:NULL]; User *user = (User *)[User initWithDict:userDict]; School *school = user.school; Address *address = user.address[0]; NSLog(@"User: %@, %d, %@, %@", user.name, user.age, school.name, address.address); } return 0; }`
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看