在我們將JSON
數(shù)據(jù)轉(zhuǎn)換為Model
過程中婿失,我們常常會使用MJExtension或者JSONModel等框架,那他們的實(shí)現(xiàn)和在runtime中都是怎么去實(shí)現(xiàn)的呢辆憔?
首先渣蜗,我們做一個簡化版的JSON
轉(zhuǎn)Model
。
// 定義Model
@interface Product : NSObject
@property (nonatomic, copy) NSString *productId;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double price;
@end
@implementation Product
- (NSString *)description {
return [NSString stringWithFormat:@"productId: %@, name: %@, price: %f", _productId, _name, _price];
}
@end
// 實(shí)現(xiàn)最基本的JSON轉(zhuǎn)Model县遣,不考慮類型匹配等問題
- (void)json2Model:(NSDictionary *)dict {
Product *product = [[Product alloc] init];
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList([Product class], &propertyCount);
for (int idx = 0; idx < propertyCount; idx ++) {
objc_property_t property = properties[idx];
const char *name = property_getName(property);
id value = dict[@(name)];
[product setValue:value forKey:@(name)];
}
NSLog(@"product : %@", product);
}
我們從上面的代碼中糜颠,可以找到實(shí)現(xiàn)這個功能的兩和個方法class_copyPropertyList
和property_getName
,下面我們從class_copyPropertyList
開始學(xué)習(xí)屬性相關(guān)的內(nèi)容萧求。
class_copyPropertyList
接下來其兴,我們看一下class_copyPropertyList
的實(shí)現(xiàn)
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
checkIsKnownClass(cls);
assert(cls->isRealized());
// 從類中的定義中找到相關(guān)數(shù)據(jù),`class->data()`會返回函數(shù)夸政、變量元旬、協(xié)議等信息
auto rw = cls->data();
property_t **result = nil;
// 獲取變量數(shù)量
unsigned int count = rw->properties.count();
if (count > 0) {
// 分配內(nèi)存空間
result = (property_t **)malloc((count + 1) * sizeof(property_t *));
count = 0;
// 遍歷變量,存儲到結(jié)果數(shù)據(jù)數(shù)組中
for (auto& prop : rw->properties) {
result[count++] = ∝
}
result[count] = nil;
}
if (outCount) *outCount = count;
return (objc_property_t *)result;
}
上面注釋中我們給幾個關(guān)鍵節(jié)點(diǎn)添加了注釋,我們可以清晰的看到函數(shù)
匀归、變量
坑资、協(xié)議
等信息都是由cls->data()
這個函數(shù)返回的,我們進(jìn)一步的去了解變量在類結(jié)構(gòu)中是如何存儲的穆端。
在objc_class
中盐茎,我們可以看到變量等都是使用bits.data()
獲取相關(guān)內(nèi)容。
struct objc_class : objc_object {
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
....
}
從上述描述中我們可以了解到變量的存儲結(jié)構(gòu)objc_class.bits.data()->properties徙赢。
在了解class_copyPropertyList
之后字柠,我們再來看看其他與變量相關(guān)的方法。
property_getName 和 property_getAttributes
property_getName
和property_getAttributes
從命名上我們可以看出狡赐,這兩個方法是用于獲取變量的名字和類型窑业。
我們接下來先看一下property_t
的定義,這個結(jié)構(gòu)中只有name
和attributes
兩個屬性枕屉,分別存儲了名字和類型常柄。
struct property_t {
const char *name;
const char *attributes;
};
然后我們使用property_getName
和property_getAttributes
兩個方法就可以輕松的獲取到變量的信息,具體實(shí)現(xiàn)如下:
const char *name = property_getName(property);
const char *attr = property_getAttributes(property);
property_copyAttributeList
這個方法并不是我們常用的方法搀擂,我們只需要大致了解一下他的實(shí)現(xiàn)即可西潘。
// 外部調(diào)用方法
objc_property_attribute_t *property_copyAttributeList(objc_property_t prop,
unsigned int *outCount)
{
if (!prop) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(runtimeLock);
return copyPropertyAttributeList(prop->attributes,outCount);
}
// 內(nèi)部實(shí)現(xiàn)方法
objc_property_attribute_t *
copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
{
if (!attrs) {
if (outCount) *outCount = 0;
return nil;
}
// Result size:
// number of commas plus 1 for the attributes (upper bound)
// plus another attribute for the attribute array terminator
// plus strlen(attrs) for name/value string data (upper bound)
// plus count*2 for the name/value string terminators (upper bound)
unsigned int attrcount = 1;
const char *s;
for (s = attrs; s && *s; s++) {
if (*s == ',') attrcount++;
}
size_t size =
attrcount * sizeof(objc_property_attribute_t) +
sizeof(objc_property_attribute_t) +
strlen(attrs) +
attrcount * 2;
objc_property_attribute_t *result = (objc_property_attribute_t *)
calloc(size, 1);
objc_property_attribute_t *ra = result;
char *rs = (char *)(ra+attrcount+1);
attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
assert((uint8_t *)rs <= (uint8_t *)result+size);
if (attrcount == 0) {
free(result);
result = nil;
}
if (outCount) *outCount = attrcount;
return result;
}
從上面的代碼我們可以看到在實(shí)現(xiàn)文件里面將屬性拆分為T
, C
, N
, V
,分別對應(yīng)屬性的類型,copy
, nonatomic
, 屬性名稱
哨颂, 并輸出位一個objc_property_attribute_t *
數(shù)組保存屬性信息喷市。
property_copyAttributeValue
property_copyAttributeValue
也不是一個常用的方法,我們可以通過T
, C
, N
, V
獲取屬性中對應(yīng)的值威恼。
char *copyPropertyAttributeValue(const char *attrs, const char *name)
{
char *result = nil;
iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result);
return result;
}
class_addProperty 和 class_replaceProperty
這兩個方法用于修改和替換屬性品姓,最后都使用_class_addProperty
方法進(jìn)行操作,通過bool replace
區(qū)分是添加變量或者修改變量箫措。
_class_addProperty(Class cls, const char *name,
const objc_property_attribute_t *attrs, unsigned int count,
bool replace)
{
if (!cls) return NO;
if (!name) return NO;
// 判斷屬性是否存在腹备,如果屬性存在則無法添加
property_t *prop = class_getProperty(cls, name);
if (prop && !replace) {
// already exists, refuse to replace
return NO;
}
else if (prop) {
// replace existing
mutex_locker_t lock(runtimeLock);
try_free(prop->attributes);
// 通過`copyPropertyAttributeString`方法生成變量替換的變量
prop->attributes = copyPropertyAttributeString(attrs, count);
return YES;
}
else {
mutex_locker_t lock(runtimeLock);
assert(cls->isRealized());
property_list_t *proplist = (property_list_t *)
malloc(sizeof(*proplist));
proplist->count = 1;
proplist->entsizeAndFlags = sizeof(proplist->first);
proplist->first.name = strdupIfMutable(name);
// 通過`copyPropertyAttributeString`方法生成變量替換的變量
proplist->first.attributes = copyPropertyAttributeString(attrs, count);
cls->data()->properties.attachLists(&proplist, 1);
return YES;
}
}
總結(jié)
property_t
可以幫助我們動態(tài)的獲取和修改類的變量,最常是用的就是在JSON與Model互轉(zhuǎn)斤蔓,比較知名的就有JSONModel和MJExtension植酥,我們可以通過閱讀這些成熟的框架來了解更多關(guān)于property_t
的使用。
更好的閱讀體驗可以參考個人網(wǎng)站:https://zevwings.com