iOS 專案常將 JSON 反序列化為模型物件。若伺服器回傳欄位缺失或為 null,對應的 NSString* 屬性會保持 nil,後續直接使用易導致崩潰或邏輯異常。需一個通用方法:掃描任意物件的所有屬性,針對型別為 NSString 且值為 nil 者,自動賦值為 @""。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface GMCommonHelper : NSObject
- (void)fixNilData:(id)object;
@end
@implementation GMCommonHelper
- (void)fixNilData:(id)object {
unsigned int count = 0;
objc_property_t *props = class_copyPropertyList([object class], &count);
for (unsigned int i = 0; i < count; ++i) {
objc_property_t prop = props[i];
const char *name = property_getName(prop);
const char *attrs = property_getAttributes(prop);
NSString *propName = [NSString stringWithUTF8String:name];
NSString *propAttrs = [NSString stringWithUTF8String:attrs];
// 屬性型別編碼以 "T@" 開頭,NSString 具體為 T@"NSString"
if (![object valueForKey:propName] && [propAttrs hasPrefix:@"T@\"NSString\""]) {
[object setValue:@"" forKey:propName];
}
}
free(props);
}
@end
class_copyPropertyList 取得屬性清單,務必在最後 free(props) 釋放記憶體。property_getAttributes 回傳型別編碼字串,T@"NSString" 代表 NSString 物件型別。valueForKey:/setValue:forKey: 動態存取屬性值。!value 涵蓋 nil、NULL、0,對 NSString* 等同於 nil 檢查。- (void)releaseProperties {
unsigned int count = 0;
objc_property_t *props = class_copyPropertyList([self class], &count);
for (unsigned int i = 0; i < count; ++i) {
objc_property_t prop = props[i];
NSString *name = [NSString stringWithUTF8String:property_getName(prop)];
NSString *attrs = [NSString stringWithUTF8String:property_getAttributes(prop)];
// T@ 開頭表示物件型別,且無 ",R," 標記代表非 readonly
if ([attrs hasPrefix:@"T@"] && [attrs rangeOfString:@",R,"].location == NSNotFound) {
[self setValue:nil forKey:name];
}
}
free(props);
}
| 編碼 | 含義 |
|---|---|
| c | char |
| i | int |
| s | short |
| l | long |
| q | long long |
| f | float |
| d | double |
| @ | Objective-C 物件 (id) |
| # | Class |
| : | SEL |
| [陣列型別] | 陣列 |
| {結構體名=成員...} | 結構體 |
更多編碼細節請參考 Apple 官方文件 Objective-C Runtime Programming Guide > Type Encodings。
透過 Objective-C Runtime 的反射能力,僅需十幾行程式碼即可實現「任意模型、任意屬性、自動修復 nil 字串」的通用工具,極大減少手動防禦性程式碼,提升專案健壯性。