前言好久沒有寫東西了,忙啊,。 前段時間參加了一下我們?nèi)A科聯(lián)創(chuàng)的HackDay(本人在讀研=,。=,,目前在阿里實習(xí)),,作品是一款 實時在線對戰(zhàn)游戲 - 波波攢 ,(介紹請看 知乎 ) 從iOS游戲客戶端(用的 SpriteKit )到后臺( PHP CI + Node + SocketIO + MySQL )全是自己一個人倒騰出來的,,做了一把真正的全棧工程師,,爽啊~ 后面會完善整個游戲,增加角色,、優(yōu)化啥的,,過上一段時間會上線的哈~ 回到正文,本文主要介紹了怎么用Runtime的手段遍歷任意NSObject對象的所有property,,檢查其值是否是nil,,是的話根據(jù)其類型為其填充一個默認(rèn)值。 Runtime畢竟是個“危險”的技術(shù),,本文的代碼只是個初步的嘗試,。 初衷在做項目的過程中,總是會寫一大堆if、else語句去檢查對象的Property是否是nil,,如從服務(wù)器返回的JSON中缺少屬性,,導(dǎo)致Entity的某些值為空;或者創(chuàng)建的對象沒有對所有屬性做初始化等等,。寫多了覺得好煩啊=,。=所以想到本文的方法,嗯,,程序員總是懶的,。 解決步驟
RuntimeOC是一門“動態(tài)”、“基于消息”的語言,,而Runtime就是利用OC的動態(tài)特性,,在運行時對程序做出“調(diào)整”的技術(shù)。有關(guān)Runtime的官方文檔,、網(wǎng)上的資料很多,,大家自學(xué)哈~ 本文主要用了如下幾個Runtime的函數(shù): // 獲取類的所有Property 1. objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) // 獲取一個Property的變量名 2. const char *property_getName(objc_property_t property) // 獲取一個Property的詳細(xì)類型表達(dá)字符串 3. const char *property_getAttributes(objc_property_t property) 示例不好一塊一塊拆開說,直接上代碼: /** * 解析Property的Attributed字符串,,參考Stackoverflow */ static const char *getPropertyType(objc_property_t property) { const char *attributes = property_getAttributes(property); NSLog(@"%s", attributes); char buffer[1 + strlen(attributes)]; strcpy(buffer, attributes); char *state = buffer, *attribute; while ((attribute = strsep(&state, ",")) != NULL) { // 非對象類型 if (attribute[0] == 'T' && attribute[1] != '@') { // 利用NSData復(fù)制一份字符串 return (const char *) [[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes]; // 純id類型 } else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) { return "id"; // 對象類型 } else if (attribute[0] == 'T' && attribute[1] == '@') { return (const char *) [[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes]; } } return ""; } /** * 給對象的屬性設(shè)置默認(rèn)值 */ void checkEntity(NSObject *object) { // 不同類型的字符串表示,,目前只是簡單檢查字符串、數(shù)字,、數(shù)組 static const char *CLASS_NAME_NSSTRING; static const char *CLASS_NAME_NSNUMBER; static const char *CLASS_NAME_NSARRAY; // 初始化類型常量 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // "NSString" CLASS_NAME_NSSTRING = NSStringFromClass([NSString class]).UTF8String; // "NSNumber CLASS_NAME_NSNUMBER = NSStringFromClass([NSNumber class]).UTF8String; // "NSArray" CLASS_NAME_NSARRAY = NSStringFromClass([NSArray class]).UTF8String; }); @try { unsigned int outCount, i; // 包含所有Property的數(shù)組 objc_property_t *properties = class_copyPropertyList([object class], &outCount); // 遍歷每個Property for (i = 0; i < outCount; i++) { // 取出對應(yīng)Property objc_property_t property = properties[i]; // 獲取Property對應(yīng)的變量名 NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; // 獲取Property的類型名 const char *propertyTypeName = getPropertyType(property); // 獲取Property的值 id propertyValue = [object valueForKey:propertyName]; // 值為空,,才設(shè)置默認(rèn)值 if (!propertyValue) { // NSString if (strncmp(CLASS_NAME_NSSTRING, propertyTypeName, strlen(CLASS_NAME_NSSTRING)) == 0) { [object setValue:@"" forKey:propertyName]; } // NSNumber if (strncmp(CLASS_NAME_NSNUMBER, propertyTypeName, strlen(CLASS_NAME_NSNUMBER)) == 0) { [object setValue:@0 forKey:propertyName]; } // NSArray if (strncmp(CLASS_NAME_NSARRAY, propertyTypeName, strlen(CLASS_NAME_NSARRAY)) == 0) { [object setValue:@[] forKey:propertyName]; } } } // 別忘了釋放數(shù)組 free(properties); } @catch (NSException *exception) { NSLog(@"Check Entity Exception: %@", [exception description]); } } 重點 - 解析property_getAttributes函數(shù)的結(jié)果在整個處理過程中,property_getAttributes函數(shù)是關(guān)鍵,,因為我們要首先確定Property的類型,,才能根據(jù)類型賦初值,但是property_getAttributes函數(shù)返回的字符串比較“晦澀難懂”: 如下定義的Property: @property (copy, nonatomic) NSString *name; @property (strong, nonatomic) NSNumber *number; @property (strong, nonatomic) NSArray *array; @property (assign, nonatomic) NSInteger i; @property (assign, nonatomic) CGFloat f; @property (assign, nonatomic) char *cStr; 依次通過property_getAttributes獲取的結(jié)果是: T@"NSString",C,N,V_name T@"NSNumber",&,N,V_number T@"NSArray",&,N,V_array Tq,N,V_i Td,N,V_f T*,N,V_cStr 參考 Declared Properties of Objective-C Runtime Programming Guide 我們大概可以知道,,T表示Type,,后面跟著@表示Cocoa對象類型,后面的表示Property的屬性,,如Copy,、strong等,然后就是變量名,。 所以getPropertyType函數(shù)的工作就是純粹的解析字符串,,獲取T@后面的類型名。 效果例如我們有如下對象: @interface UserEntity : NSObject @property (copy, nonatomic) NSString *name; @property (strong, nonatomic) NSNumber *number; @property (strong, nonatomic) NSArray *array; @end 設(shè)置默認(rèn)值: UserEntity *userEntity = [UserEntity new]; // 檢查屬性,設(shè)置默認(rèn)值,。 checkEntity(userEntity); // 使用... NSLog(@"name: %@", userEntity.name); NSLog(@"number: %@", userEntity.number); NSLog(@"array: %@", userEntity.array); 輸出: 2015-07-11 18:17:25.918 Common[6939:270543] name: 2015-07-11 18:17:25.918 Common[6939:270543] number: 0 2015-07-11 18:17:25.918 Common[6939:270543] array: ( ) 這樣,,一個對象的所有Property都有了初值。 總結(jié)上面的例子只是個粗略的版本,,只是檢查了字符串,、數(shù)字、數(shù)組,,其實完全可以擴(kuò)展出很多功能,,如針對不同的類型,根據(jù)對象的類型,,設(shè)置不同的默認(rèn)初值等,,靠讀者你了~ Runtime是個好東西,但是別亂用啊=,。= |
|