參考文章:
1、Objctive-C Runtime
2、梧雨北辰
3概而、jackyshan
4、人仙兒a
本文主要是參考梧雨北辰的文章囱修,并在該作者的文章之上添加自己理解的內(nèi)容赎瑰。侵權(quán)必刪。
1破镰、方法魔法(Method Swizzling)
實現(xiàn)動態(tài)方法交換(Method Swizzling )是Runtime中最具盛名的應用場景餐曼,其原理是:通過Runtime獲取到方法實現(xiàn)的地址,進而動態(tài)交換兩個方法的功能鲜漩。使用到關鍵方法如下:
///獲取類方法的Mthod
Method _Nullable class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
///獲取實例對象方法的Mthod
Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
///交換兩個方法的實現(xiàn)
void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
1.1 動態(tài)方法交換示例
- (void)printA{
NSLog(@"打印A......");
}
- (void)printB{
NSLog(@"打印B......");
}
//交換方法的實現(xiàn)源譬,并測試打印
Method methodA = class_getInstanceMethod([self class], @selector(printA));
Method methodB = class_getInstanceMethod([self class], @selector(printB));
method_exchangeImplementations(methodA, methodB);
[self printA]; ///打印B......
[self printB]; ///打印A......
1.2 攔截并替換系統(tǒng)方法
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(jkviewDidLoad);
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
//judge the method named swizzledMethod is already existed.
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// if swizzledMethod is already existed.
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)jkviewDidLoad {
NSLog(@"替換的方法");
[self jkviewDidLoad];
}
- (void)viewDidLoad {
NSLog(@"自帶的方法");
[super viewDidLoad];
}
@end
swizzling
應該只在+load
中完成。 在 Objective-C 的運行時中孕似,每個類有兩個方法都會自動調(diào)用踩娘。+load
是在一個類被初始裝載時調(diào)用,+initialize
是在應用第一次調(diào)用該類的類方法或?qū)嵗椒ㄇ罢{(diào)用的喉祭。兩個方法都是可選的养渴,并且只有在方法被實現(xiàn)的情況下才會被調(diào)用。
swizzling
應該只在dispatch_once
中完成,由于swizzling
改變了全局的狀態(tài)泛烙,所以我們需要確保每個預防措施在運行時都是可用的理卑。原子操作就是這樣一個用于確保代碼只會被執(zhí)行一次的預防措施,就算是在不同的線程中也能確保代碼只執(zhí)行一次胶惰。Grand Central Dispatch
的 dispatch_once
滿足了所需要的需求傻工,并且應該被當做使用swizzling
的初始化單例方法的標準。
1.3 KVO實現(xiàn)
全稱是Key-value observing
孵滞,翻譯成鍵值觀察中捆。提供了一種當其它對象屬性被修改的時候能通知當前對象的機制。再MVC大行其道的Cocoa中坊饶,KVO機制很適合實現(xiàn)model
和controller
類之間的通訊泄伪。
KVO的實現(xiàn)依賴于 Objective-C 強大的 Runtime,當觀察某對象 A 時匿级,KVO 機制動態(tài)創(chuàng)建一個對象A當前類的子類蟋滴,并為這個新的子類重寫了被觀察屬性 keyPath
的 setter
方法。setter
方法隨后負責通知觀察對象屬性的改變狀況痘绎。
Apple 使用了 isa-swizzling
來實現(xiàn) KVO 津函。當觀察對象A時,KVO機制動態(tài)創(chuàng)建一個新的名為:NSKVONotifying_A
的新類孤页,該類繼承自對象A的本類尔苦,且 KVO 為 NSKVONotifying_A
重寫觀察屬性的 setter
方法,setter
方法會負責在調(diào)用原 setter
方法之前和之后,通知所有觀察對象屬性值的更改情況允坚。
我們通過例子來驗證一下魂那,首先我們檢測一個類的
NSLog(@"kvo之前 self -> isa: %@",object_getClass(runtime));
NSLog(@"kvo之前 self class : %@",[runtime class]);
測試代碼如下:
RuntimeTestManager *runtime = [[RuntimeTestManager alloc] init];
runtime.name = @"zhangsan";
NSLog(@"kvo之前 self -> isa: %@",object_getClass(runtime));
NSLog(@"kvo之前 self class : %@",[runtime class]);
[runtime addObserver:self forKeyPath:@"_name" options:NSKeyValueObservingOptionInitial context:nil];
_runtimeManager = runtime;
NSLog(@"kvo之后 self -> isa: %@",object_getClass(runtime));
NSLog(@"kvo之后 self class : %@",[runtime class]);
測試結(jié)果為:
2018-11-27 17:19:43.221175+0800 Runtime[7499:2757536] kvo之前 self -> isa: RuntimeTestManager
2018-11-27 17:19:43.221217+0800 Runtime[7499:2757536] kvo之前 self class : RuntimeTestManager
2018-11-27 17:19:43.221441+0800 Runtime[7499:2757536] kvo之后 self -> isa: NSKVONotifying_RuntimeTestManager
2018-11-27 17:19:43.221460+0800 Runtime[7499:2757536] kvo之后 self class : RuntimeTestManager
在這個過程,被觀察對象的 isa 指針從指向原來的 RuntimeTestManager
類稠项,被KVO 機制修改為指向系統(tǒng)新創(chuàng)建的子類NSKVONotifying_ RuntimeTestManager
類涯雅,來實現(xiàn)當前類屬性值改變的監(jiān)聽;
所以當我們從應用層面上看來展运,完全沒有意識到有新的類出現(xiàn)活逆,這是系統(tǒng)“隱瞞”了對 KVO 的底層實現(xiàn)過程,讓我們誤以為還是原來的類乐疆。但是此時如果我們創(chuàng)建一個新的名為NSKVONotifying_ RuntimeTestManager
的類划乖,就會發(fā)現(xiàn)系統(tǒng)運行到注冊 KVO 的那段代碼時程序就崩潰,因為系統(tǒng)在注冊監(jiān)聽的時候動態(tài)創(chuàng)建了名為 NSKVONotifying_ RuntimeTestManager
的中間類挤土,并指向這個中間類了琴庵。
子類setter方法剖析
KVO 的鍵值觀察通知依賴于 NSObject 的兩個法:willChangeValueForKey:
和 didChangeValueForKey:
,在存取數(shù)值的前后分別調(diào)用 2 個方法:被觀察屬性發(fā)生改變之前仰美,willChangeValueForKey:
被調(diào)用迷殿,通知系統(tǒng)該 keyPath
的屬性值即將變更;當改變發(fā)生后咖杂, didChangeValueForKey:
被調(diào)用庆寺,通知系統(tǒng)該keyPath
的屬性值已經(jīng)變更;之后诉字,observeValueForKey:ofObject:change:context:
也會被調(diào)用懦尝。且重寫觀察屬性的setter 方法這種繼承方式的注入是在運行時而不是編譯時實現(xiàn)的。KVO 為子類的觀察者屬性重寫調(diào)用存取方法的工作原理在代碼中相當于:
- (void)setName:(NSString *)newName {
[self willChangeValueForKey:@"name"]; //KVO 在調(diào)用存取方法之前總調(diào)用
[super setValue:newName forKey:@"name"]; //調(diào)用父類的存取方法
[self didChangeValueForKey:@"name"]; //KVO 在調(diào)用存取方法之后總調(diào)用
}
2壤圃、類目添加新屬性
在我們的日常開發(fā)中陵霉,分類可以為原有類擴展功能,復寫原有類方法伍绳。但是分類不支持添加成員變量踊挠。盡管我們可以在分類中直接聲明屬性,但是由于不能生成成員變量冲杀,所以直接調(diào)用這些屬性還會造成崩潰效床。為了實現(xiàn)分類添加屬性,Runtime為我們添加了關聯(lián)對象方法。它能夠幫助我們在運行階段將任意的屬性關聯(lián)到一個對象上权谁。方法如下:
/**
1.給對象設置關聯(lián)屬性
@param object 需要設置關聯(lián)屬性的對象剩檀,即給哪個對象關聯(lián)屬性
@param key 關聯(lián)屬性對應的key,可通過key獲取這個屬性旺芽,
@param value 給關聯(lián)屬性設置的值
@param policy 關聯(lián)屬性的存儲策略(對應Property屬性中的assign,copy谨朝,retain等)
OBJC_ASSOCIATION_ASSIGN @property(assign)卤妒。
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property(strong, nonatomic)。
OBJC_ASSOCIATION_COPY_NONATOMIC @property(copy, nonatomic)字币。
OBJC_ASSOCIATION_RETAIN @property(strong,atomic)。
OBJC_ASSOCIATION_COPY @property(copy, atomic)共缕。
*/
void objc_setAssociatedObject(id _Nonnull object,
const void * _Nonnull key,
id _Nullable value,
objc_AssociationPolicy policy)
/**
2.通過key獲取關聯(lián)的屬性
@param object 從哪個對象中獲取關聯(lián)屬性
@param key 關聯(lián)屬性對應的key
@return 返回關聯(lián)屬性的值
*/
id _Nullable objc_getAssociatedObject(id _Nonnull object,
const void * _Nonnull key)
/**
3.移除對象所關聯(lián)的屬性
@param object 移除某個對象的所有關聯(lián)屬性
*/
void objc_removeAssociatedObjects(id _Nonnull object)
接下來我用一個例子說明:
#import <UIKit/UIKit.h>
@interface UIViewController (custome)
@property (nonatomic, copy) NSString *age;
@end
#import "UIViewController+custome.h"
#import <objc/runtime.h>
@implementation UIViewController (custome)
- (void)setAge:(NSString *)age {
///添加成員變量
objc_setAssociatedObject(self, @selector(setAge:), age, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)age {
///獲取成員變量
return objc_getAssociatedObject(self, @selector(setAge:));
}
@end
注意:key與關聯(lián)屬性一一對應洗出,我們必須確保其全局唯一性,常用我們使用@selector(methodName)作為key图谷。
3翩活、獲取類的詳細信息
1.1 獲取屬性列表
unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i = 0; i<count; i++) {
const char *propertyName = property_getName(propertyList[i]);
NSLog(@"PropertyName(%d): %@",i,[NSString stringWithUTF8String:propertyName]);
}
free(propertyList);
1.2 獲取所有成員變量
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i= 0; i<count; i++) {
Ivar ivar = ivarList[i];
const char *ivarName = ivar_getName(ivar);
NSLog(@"Ivar(%d): %@", i, [NSString stringWithUTF8String:ivarName]);
}
free(ivarList);
1.3 獲取所有方法
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i = 0; i<count; i++) {
Method method = methodList[i];
SEL mthodName = method_getName(method);
NSLog(@"MethodName(%d): %@",i,NSStringFromSelector(mthodName));
}
free(methodList);
1.4 獲取當前遵循的所有協(xié)議
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
for (int i=0; i<count; i++) {
Protocol *protocal = protocolList[i];
const char *protocolName = protocol_getName(protocal);
NSLog(@"protocol(%d): %@",i, [NSString stringWithUTF8String:protocolName]);
}
free(propertyList);
注意:C語言中使用Copy操作的方法,要注意釋放指針便贵,防止內(nèi)存泄漏
4菠镇、解決同一方法高頻率調(diào)用的效率問題
5、方法動態(tài)解析與消息轉(zhuǎn)發(fā)
方法的動態(tài)解析和消息轉(zhuǎn)發(fā),詳細內(nèi)容請參考上篇文章承璃。
5.1 動態(tài)方法解析:動態(tài)添加方法
Runtime足夠強大利耍,能夠讓我們在運行時動態(tài)添加一個未實現(xiàn)的方法,這個功能主要有兩個應用場景:
場景1:動態(tài)添加未實現(xiàn)方法盔粹,解決代碼中因為方法未找到而報錯的問題隘梨;
場景2:利用懶加載思路,若一個類有很多個方法舷嗡,同時加載到內(nèi)存中會耗費資源轴猎,可以使用動態(tài)解析添加方法。方法動態(tài)解析主要用到的方法如下:
//OC方法:
//類方法未找到時調(diào)起进萄,可于此添加類方法實現(xiàn)
+ (BOOL)resolveClassMethod:(SEL)sel
//實例方法未找到時調(diào)起捻脖,可于此添加實例方法實現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
//Runtime方法:
/**
運行時方法:向指定類中添加特定方法實現(xiàn)的操作
@param cls 被添加方法的類
@param name selector方法名
@param imp 指向?qū)崿F(xiàn)方法的函數(shù)指針
@param types imp函數(shù)實現(xiàn)的返回值與參數(shù)類型
@return 添加方法是否成功
*/
BOOL class_addMethod(Class _Nullable cls,
SEL _Nonnull name,
IMP _Nonnull imp,
const char * _Nullable types)
5.2 解決方法無響應崩潰問題
執(zhí)行OC方法其實就是一個發(fā)送消息的過程,若方法未實現(xiàn)中鼠,我們可以利用方法動態(tài)解析與消息轉(zhuǎn)發(fā)來避免程序崩潰可婶,這主要涉及下面一個處理未實現(xiàn)消息的過程:其他相關方法如下:
///重定向類方法的消息接收者,返回一個類
- (id)forwardingTargetForSelector:(SEL)aSelector
///重定向?qū)嵗椒ǖ南⒔邮苷叨等洌祷匾粋€實例對象
- (id)forwardingTargetForSelector:(SEL)aSelector
///消息重定向
- (void)forwardInvocation:(NSInvocation *)anInvocation扰肌;
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector;
6、動態(tài)操作屬性
6.1 動態(tài)修改屬性變量
現(xiàn)在假設這樣一個情況:我們使用第三方框架里的Person
類熊杨,在特殊需求下想要更改其私有屬性name
曙旭,這樣的操作我們就可以使用Runtime可以動態(tài)修改對象屬性。
基本思路:首先使用Runtime獲取Peson
對象的所有屬性晶府,找到name
桂躏,然后使用ivar的方法修改其值。具體的代碼示例如下:
Person *per = [[Person alloc] init];
per.name = @"zhagnsan";
NSLog(@"======== %@",per.name);
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([per class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
const char *ivarName = ivar_getName(ivar);
NSString *propertyName = [NSString stringWithUTF8String:ivarName];
if ([propertyName isEqualToString:@"_name"]) {
object_setIvar(per, ivar, @"李四");
}
}
free(ivarList); //釋放指針
NSLog(@"------- %@",per.name);
執(zhí)行結(jié)果為:
2018-11-29 11:17:06.050754+0800 Runtime[34451:175705] ======== zhagnsan
2018-11-29 11:17:06.050844+0800 Runtime[34451:175705] ------- 李四
6.2 實現(xiàn) NSCoding 的自動歸檔和解檔
歸檔是一種常用的輕量型文件存儲方式川陆,但是它有個弊端:在歸檔過程中剂习,若一個Model有多個屬性,我們不得不對每個屬性進行處理,非常繁瑣鳞绕。歸檔操作主要涉及兩個方法:encodeObject
和 decodeObjectForKey
失仁,現(xiàn)在,我們可以利用Runtime來改進它們们何,關鍵的代碼示例如下:
//原理:使用Runtime動態(tài)獲取所有屬性
//解檔操作
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
const char *ivarName = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:ivarName];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivarList); //釋放指針
}
return self;
}
//歸檔操作
- (void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (NSInteger i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivarList); //釋放指針
}
測試如下:
//--測試歸檔
Person *per = [[Person alloc] init];
per.name = @"zhagnsan";
per.age = 18;
NSString *temp = NSTemporaryDirectory();
NSString *fileTemp = [temp stringByAppendingString:@"person.archive"];
[NSKeyedArchiver archiveRootObject:ps toFile:fileTemp];
//--測試解檔
NSString *temp = NSTemporaryDirectory();
NSString *fileTemp = [temp stringByAppendingString:@"person.henry"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:fileTemp];
NSLog(@"person-name:%@萄焦,person-age:%ld",person.name,person.age);
//person-name:zhagnsan,person-age:18
6.3 實現(xiàn)字典與模型的轉(zhuǎn)換
字典數(shù)據(jù)轉(zhuǎn)模型的操作在項目開發(fā)中很常見冤竹,通常我們會選擇第三方如YYModel拂封;其實我們也可以自己來實現(xiàn)這一功能,主要的思路有兩種:KVC鹦蠕、Runtime冒签,總結(jié)字典轉(zhuǎn)化模型過程中需要解決的問題如下:
現(xiàn)在,我們使用Runtime來實現(xiàn)字典轉(zhuǎn)模型的操作钟病,大致的思路是這樣:
借助Runtime可以動態(tài)獲取成員列表的特性萧恕,遍歷模型中所有屬性,然后以獲取到的屬性名為key档悠,在JSON字典中尋找對應的值value廊鸥;再將每一個對應Value賦值給模型,就完成了字典轉(zhuǎn)模型的目的辖所。
首先準備下面的JSON數(shù)據(jù)用于測試:
{
"id":"2462079046",
"name": "梧雨北辰",
"age":"18",
"weight":140,
"address":{
"country":"中國",
"province": "河南"
},
"courses":[{
"name":"Chinese",
"desc":"語文課"
},{
"name":"Math",
"desc":"數(shù)學課"
},{
"name":"English",
"desc":"英語課"
}
]
}
具體的代碼實現(xiàn)流程如下:
步驟1:創(chuàng)建NSObject的類目NSObject+ZSModel惰说,用于實現(xiàn)字典轉(zhuǎn)模型
@interface NSObject (ZSModel)
+ (instancetype)zs_modelWithDictionary:(NSDictionary *)dictionary;
@end
//ZSModel協(xié)議,協(xié)議方法可以返回一個字典缘回,表明特殊字段的處理規(guī)則
@protocol ZSModel<NSObject>
@optional
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
@end;
#import "NSObject+ZSModel.h"
#import <objc/runtime.h>
@implementation NSObject (ZSModel)
+ (instancetype)zs_modelWithDictionary:(NSDictionary *)dictionary{
//創(chuàng)建當前模型對象
id object = [[self alloc] init];
//1.獲取當前對象的成員變量列表
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
//2.遍歷ivarList中所有成員變量吆视,以其屬性名為key,在字典中查找Value
for (int i= 0; i<count; i++) {
//2.1獲取成員屬性
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)] ;
//2.2截取成員變量名:去掉成員變量前面的"_"號
NSString *propertyName = [ivarName substringFromIndex:1];
//2.3以屬性名為key酥宴,在字典中查找value
id value = dictionary[propertyName];
//3.獲取成員變量類型, 因為ivar_getTypeEncoding獲取的類型是"@\"NSString\""的形式
//所以我們要做以下的替換
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];// 替換:
//3.1去除轉(zhuǎn)義字符:@\"name\" -> @"name"
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
//3.2去除@符號
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
//4.對特殊成員變量進行處理:
//判斷當前類是否實現(xiàn)了協(xié)議方法啦吧,獲取協(xié)議方法中規(guī)定的特殊變量的處理方式
NSDictionary *perpertyTypeDic;
if([self respondsToSelector:@selector(modelContainerPropertyGenericClass)]){
perpertyTypeDic = [self performSelector:@selector(modelContainerPropertyGenericClass) withObject:nil];
}
//4.1處理:字典的key與模型屬性不匹配的問題,如id->uid
id anotherName = perpertyTypeDic[propertyName];
if(anotherName && [anotherName isKindOfClass:[NSString class]]){
value = dictionary[anotherName];
}
//4.2.處理:模型嵌套模型
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
Class modelClass = NSClassFromString(ivarType);
if (modelClass != nil) {
//將被嵌套字典數(shù)據(jù)也轉(zhuǎn)化成Model
value = [modelClass zs_modelWithDictionary:value];
}
}
//4.3處理:模型嵌套模型數(shù)組
//判斷當前Vaue是一個數(shù)組拙寡,而且存在協(xié)議方法返回了perpertyTypeDic
if ([value isKindOfClass:[NSArray class]] && perpertyTypeDic) {
Class itemModelClass = perpertyTypeDic[propertyName];
//封裝數(shù)組:將每一個子數(shù)據(jù)轉(zhuǎn)化為Model
NSMutableArray *itemArray = @[].mutableCopy;
for (NSDictionary *itemDic in value) {
id model = [itemModelClass zs_modelWithDictionary:itemDic];
[itemArray addObject:model];
}
value = itemArray;
}
//5.使用KVC方法將Vlue更新到object中
if (value != nil) {
[object setValue:value forKey:propertyName];
}
}
free(ivarList); //釋放C指針
return object;
}
@end
步驟2:分別創(chuàng)建各個數(shù)據(jù)模型Student授滓、Address、Course
Student類:
//Student.h文件
#import "NSObject+ZSModel.h"
#import "AddressModel.h"
#import "CourseModel.h"
@interface StudentModel : NSObject<ZSModel> //遵循協(xié)議
//普通屬性
@property (nonatomic, copy) NSString *uid;
@property(nonatomic,copy)NSString *name;
@property (nonatomic, assign) NSInteger age;
//嵌套模型
@property (nonatomic, strong) AddressModel *address;
//嵌套模型數(shù)組
@property (nonatomic, strong) NSArray *courses;
@end
#import "StudentModel.h"
@implementation StudentModel
+ (NSDictionary *)modelContainerPropertyGenericClass {
//需要特別處理的屬性
return @{@"courses" : [CourseModel class],@"uid":@"id"};
}
@end
Address類:
//AddressModel.h文件
@interface AddressModel : NSObject
@property (nonatomic, copy) NSString *country; //國籍
@property (nonatomic, copy) NSString *province; //省份
@property (nonatomic, copy) NSString *city; //城市
@end
//-----------------優(yōu)美的分割線------------------------
//AddressModel.m文件
#import "AddressModel.h"
@implementation AddressModel
@end
Course類:
//讀取JSON數(shù)據(jù)
NSDictionary *jsonData = [FileTools getDictionaryFromJsonFile:@"Student"];
NSLog(@"%@",jsonData);
//字典轉(zhuǎn)模型
StudentModel *student = [StudentModel zs_modelWithDictionary:jsonData];
CourseModel *courseModel = student.courses[0];
NSLog(@"%@",courseModel.name);
步驟4:測試字典轉(zhuǎn)模型操作
//讀取JSON數(shù)據(jù)
NSDictionary *jsonData = [FileTools getDictionaryFromJsonFile:@"Student"];
NSLog(@"%@",jsonData);
//字典轉(zhuǎn)模型
StudentModel *student = [StudentModel zs_modelWithDictionary:jsonData];
CourseModel *courseModel = student.courses[0];
NSLog(@"%@",courseModel.name);
效果如下: