轉(zhuǎn)發(fā)自:MFExtension使用
一、MJExtension第三方框架
我們在iOS開發(fā)過程中,我們常常需要將字典數(shù)據(jù)(也就是JSON數(shù)據(jù))與Model模型之間的轉(zhuǎn)化推姻,例如網(wǎng)絡(luò)請求返回的微博數(shù)據(jù)训枢、等等,如果我們自己全部手動(dòng)去創(chuàng)建模型并賦值挺勿,都是一些毫無技術(shù)含量的代碼,費(fèi)時(shí)費(fèi)力喂柒,而且還可能會(huì)賦值出錯(cuò)不瓶,讓我們很頭疼。
MJExtension框架就是為了解決這個(gè)問題而設(shè)計(jì)得第三方開源庫灾杰。這個(gè)開源庫是之前傳智博客的講師李明杰老師寫的蚊丐,現(xiàn)在他自己出來做了,我iOS入門都是看李明杰老師的培訓(xùn)視頻學(xué)習(xí)的艳吠,他講得非常好麦备,我非常喜歡他,他也算是我的老師了昭娩,他的作品我還是要學(xué)習(xí)下的凛篙。
提供了以下的一些方法實(shí)現(xiàn):
簡單的字典 --> 模型
JSON字符串 --> 模型
復(fù)雜的字典 --> 模型 (模型里面包含了模型)
復(fù)雜的字典 --> 模型 (模型的數(shù)組屬性里面又裝著模型)
復(fù)雜的字典 --> 模型(模型屬性名和字典的key不一樣)
字典數(shù)組 --> 模型數(shù)組
模型 --> 字典
模型數(shù)組 --> 字典數(shù)組
字典 --> CoreData模型
歸檔與解檔NSCoding
過濾字典的值
MJExtension框架是利用Obj-C的運(yùn)行時(shí)機(jī)制編寫的,現(xiàn)在iOS開發(fā)語言往Swift語言發(fā)展栏渺,我不太清楚Swift語言是否也有這種特性呛梆,該框架以后會(huì)不會(huì)在Swift語言上也發(fā)展下去不得而知,不過這個(gè)框架很輕量級迈嘹,非常適合初級開發(fā)者去看它的源碼削彬,對理解Obj-C的運(yùn)行時(shí)機(jī)制有非常大的幫助。
二秀仲、Runtime運(yùn)行時(shí)機(jī)制簡單了解
Runtime簡稱運(yùn)行時(shí)融痛,就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制神僵。
OC的函數(shù)調(diào)用類似于消息發(fā)送雁刷,屬于動(dòng)態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)保礼。事實(shí)證明沛励,在編譯階段责语,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn)目派,只要申明過就不會(huì)報(bào)錯(cuò)坤候。而C語言在編譯階段就會(huì)報(bào)錯(cuò)。只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用企蹭。
例如白筹,下面的這個(gè)代碼在編譯時(shí)會(huì)被轉(zhuǎn)化:
/* OC方法調(diào)用 */[obj makeTest];/* 編譯時(shí)Runtime會(huì)將上面的代碼轉(zhuǎn)為下面的消息發(fā)送 */objc_msgSend(obj, @selector(makeText));
iOS的頂層基類NSObject含有一個(gè)指向objc_class結(jié)構(gòu)體的isa指針:
@interface NSObject{? ? Class isa;};typedefstructobjc_class*Class;structobjc_class{Class isa;// 指向metaclass,也就是靜態(tài)的ClassClass super_class ;// 指向其父類constchar*name ;// 類名longversion ;// 類的版本信息谅摄,初始化默認(rèn)為0/* 一些標(biāo)識信息徒河,如CLS_CLASS(0x1L)表示該類為普通class;
? ? ? CLS_META(0x2L)表示該類為metaclass */longinfo;longinstance_size ;// 該類的實(shí)例變量大小(包括從父類繼承下來的實(shí)例變量);structobjc_ivar_list*ivars;// 用于存儲(chǔ)每個(gè)成員變量的地址/* 與info的一些標(biāo)志位有關(guān),如是普通class則存儲(chǔ)對象方法,如是metaclass則存儲(chǔ)類方法; */structobjc_method_list**methodLists;structobjc_cache*cache;// 指向最近使用的方法的指針送漠,用于提升效率顽照;structobjc_protocol_list*protocols;// 存儲(chǔ)該類遵守的協(xié)議};
need-to-insert-img
在objc_msgSend函數(shù)的調(diào)用過程:
首先通過obj的isa指針找到obj對應(yīng)的Class。
在Class中先去cache中通過SEL查找對應(yīng)函數(shù)method
若cache中未找到闽寡,再去methodLists中查找
若methodLists中未找到代兵,則進(jìn)入superClass按前面的步驟進(jìn)行遞歸查找
若找到method,則將method加入到cache中下隧,以方便下次查找奢人,并通過method中的函數(shù)指針跳轉(zhuǎn)到對應(yīng)的函數(shù)中去執(zhí)行谓媒。
如果一直查找到NSObject還沒查找到淆院,則會(huì)進(jìn)入消息動(dòng)態(tài)處理流程。
消息動(dòng)態(tài)處理流程:
/* 1. 時(shí)機(jī)處理之一句惯,在這個(gè)方法中我們可以利用runtime的特性動(dòng)態(tài)添加方法來處理 */+ (BOOL)resolveInstanceMethod:(SEL)sel;/* 2. 時(shí)機(jī)處理之二土辩,在這個(gè)方法中看代理能不能處理,如果代理對象能處理抢野,則轉(zhuǎn)接給代理對象 */- (id)forwardingTargetForSelector:(SEL)aSelector;/* 3. 消息轉(zhuǎn)發(fā)之一拷淘,該方法返回方法簽名,如果返回nil指孤,則轉(zhuǎn)發(fā)流程終止启涯,拋出異常 */- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector;/* 4. 消息轉(zhuǎn)發(fā)之二,在該方法中我們可以對調(diào)用方法進(jìn)行重定向 */- (void)forwardInvocation:(NSInvocation*)anInvocation;
need-to-insert-img
所以使用Runtime機(jī)制我們就可以動(dòng)態(tài)向類添加方法或?qū)傩裕?/p>
/* 動(dòng)態(tài)向一個(gè)類添加屬性 */class_addIvar(kclass,"expression", size, alignment,"*");/* 動(dòng)態(tài)向一個(gè)類添加方法 */class_addMethod(kclass,@selector(setExpressionFormula:), (IMP)setExpressionFormula,"v@:@");class_addMethod(kclass,@selector(getExpressionFormula), (IMP)getExpressionFormula,"@@:");staticvoidsetExpressionFormula(idself, SEL cmd,idvalue){NSLog(@"call setExpressionFormula");? }staticidgetExpressionFormula(idself, SEL cmd)? {NSLog(@"call getExpressionFormula");returnnil;}
v表示void恃轩,@表示id類型结洼,:表示SEL類型
"v@:@":表示返回值為void,接受一個(gè)id類型叉跛、一個(gè)SEL類型松忍、一個(gè)id類型的方法
"@@:":表示返回值為id類型,接受一個(gè)id類型和一個(gè)SEL類型參數(shù)的方法
具體Runtime運(yùn)行時(shí)使用細(xì)節(jié)筷厘,這里就不細(xì)講鸣峭,只是簡單了解下Runtime是可以做到動(dòng)態(tài)向類添加屬性和方法就行宏所。
三、MJExtension使用
MJExtension的大部分方法實(shí)現(xiàn)都集成到了分類上摊溶,不需要使用新的類爬骤,只需要包含頭文件MJExtension.h即可。MJExtension在github上的使用說明已經(jīng)寫得十分明白了莫换。
1. 簡單的字典 --> 模型
模型類User定義:
typedefenum{? ? SexMale,? ? SexFemale} Sex;@interfaceUser:NSObject@property(copy,nonatomic)NSString*name;/* 姓名 */@property(copy,nonatomic)NSString*icon;/* 頭像 */@property(assign,nonatomic)unsignedintage;/* 年齡 */@property(copy,nonatomic)NSString*height;/* 身高 */@property(strong,nonatomic)NSNumber*money;/* 資產(chǎn) */@property(assign,nonatomic) Sex sex;/* 性別 */@property(assign,nonatomic,getter=isGay)BOOLgay;/* 是否是同性戀 */@end
使用實(shí)例:
NSDictionary *dict = @{? ? @"name": @"Jack",? ? @"icon": @"lufy.png",? ? @"age": @20,? ? @"height": @"1.55",? ? @"money": @100.9,? ? @"sex": @(SexFemale),/* 枚舉需要使用NSNumber包裝 */@"gay": @"NO"};//字典轉(zhuǎn)模型盖腕,使用的是mj_objectWithKeyValues:方法User *user = [Usermj_objectWithKeyValues:dict];
2. JSON字符串 --> 模型
使用實(shí)例:
// 定義一個(gè)JSON字符串NSString*jsonString =@"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}";// JSON字符串轉(zhuǎn)模型User *user = [User mj_objectWithKeyValues:jsonString];
3. 復(fù)雜的字典 --> 模型 (模型里面包含了模型)
模型類Status定義:
@interfaceStatus:NSObject@property(copy,nonatomic)NSString*text;@property(strong,nonatomic) User *user;/* 其他模型類型 */@property(strong,nonatomic) Status *retweetedStatus;/* 自我模型類型 */@end
使用實(shí)例:
NSDictionary*dict = @{@"text":@"Agree!Nice weather!",@"user": @{@"name":@"Jack",@"icon":@"lufy.png"},@"retweetedStatus": @{@"text":@"Nice weather!",@"user": @{@"name":@"Rose",@"icon":@"nami.png"}? ? }};//字典轉(zhuǎn)模型,模型里面含有模型Status *status = [Status mj_objectWithKeyValues:dict];NSString*text = status.text;NSString*name = status.user.name;NSString*icon = status.user.icon;NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);// text=Agree!Nice weather!, name=Jack, icon=lufy.pngNSString*text2 = status.retweetedStatus.text;NSString*name2 = status.retweetedStatus.user.name;NSString*icon2 = status.retweetedStatus.user.icon;NSLog(@"text2=%@, name2=%@, icon2=%@", text2, name2, icon2);// text2=Nice weather!, name2=Rose, icon2=nami.png
4. 復(fù)雜的字典 --> 模型 (模型的數(shù)組屬性里面又裝著模型)
模型類Ad和StatusResult定義:
@interfaceAd:NSObject@property(copy,nonatomic)NSString*image;@property(copy,nonatomic)NSString*url;@end@interfaceStatusResult:NSObject/** 數(shù)組中存儲(chǔ)模型Status類型數(shù)據(jù) */@property(strong,nonatomic)NSMutableArray*statuses;/** 數(shù)組中存儲(chǔ)模型Ad類型數(shù)據(jù) */@property(strong,nonatomic)NSArray*ads;@property(strong,nonatomic)NSNumber*totalNumber;@end#import"MJExtension.h"/* 數(shù)組中存儲(chǔ)模型數(shù)據(jù)浓镜,需要說明數(shù)組中存儲(chǔ)的模型數(shù)據(jù)類型 */@implementationStatusResult/* 實(shí)現(xiàn)該方法溃列,說明數(shù)組中存儲(chǔ)的模型數(shù)據(jù)類型 */+ (NSDictionary*)mj_ objectClassInArray{return@{@"statuses":@"Status",@"ads":@"Ad"};}@end
使用實(shí)例:
NSDictionary*dict = @{@"statuses": @[? ? ? ? ? ? ? ? ? ? ? @{@"text":@"Nice weather!",@"user": @{@"name":@"Rose",@"icon":@"nami.png"}? ? ? ? ? ? ? ? ? ? ? },? ? ? ? ? ? ? ? ? ? ? @{@"text":@"Go camping tomorrow!",@"user": @{@"name":@"Jack",@"icon":@"lufy.png"}? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ],@"ads": @[? ? ? ? ? ? ? ? @{@"image":@"ad01.png",@"url":@"http://www.ad01.com"},? ? ? ? ? ? ? ? @{@"image":@"ad02.png",@"url":@"http://www.ad02.com"}? ? ? ? ? ? ],@"totalNumber":@"2014"};//字典轉(zhuǎn)模型,支持模型的數(shù)組屬性里面又裝著模型StatusResult *result = [StatusResult mj_objectWithKeyValues:dict];//打印博主信息for(Status *statusinresult.statuses) {NSString*text = status.text;NSString*name = status.user.name;NSString*icon = status.user.icon;NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);}// text=Nice weather!, name=Rose, icon=nami.png// text=Go camping tomorrow!, name=Jack, icon=lufy.png//打印廣告for(Ad *adinresult.ads) {NSLog(@"image=%@, url=%@", ad.image, ad.url);}// image=ad01.png, url=http://www.ad01.com// image=ad02.png, url=http://www.ad02.com
5. 復(fù)雜的字典 --> 模型(模型屬性名和字典的key不一樣)
模型類Bag和Student定義:
@interfaceBag:NSObject@property(copy,nonatomic)NSString*name;@property(assign,nonatomic)doubleprice;@end@interfaceStudent:NSObject@property(copy,nonatomic)NSString*ID;@property(copy,nonatomic)NSString*desc;@property(copy,nonatomic)NSString*nowName;@property(copy,nonatomic)NSString*oldName;@property(copy,nonatomic)NSString*nameChangedTime;@property(strong,nonatomic) Bag *bag;@end#import"MJExtension.h"@implementation/* 設(shè)置模型屬性名和字典key之間的映射關(guān)系 */+ (NSDictionary*)mj_replacedKeyFromPropertyName{/* 返回的字典膛薛,key為模型屬性名听隐,value為轉(zhuǎn)化的字典的多級key */return@{@"ID":@"id",@"desc":@"desciption",@"oldName":@"name.oldName",@"nowName":@"name.newName",@"nameChangedTime":@"name.info[1].nameChangedTime",@"bag":@"other.bag"};}@end
使用實(shí)例:
NSDictionary*dict = @{@"id":@"20",@"desciption":@"kids",@"name": @{@"newName":@"lufy",@"oldName":@"kitty",@"info": @[@"test-data",? ? ? ? ? ? ? ? @{@"nameChangedTime":@"2013-08"}? ? ? ? ? ? ? ? ? ]? ? },@"other": @{@"bag": @{@"name":@"a red bag",@"price": @100.7}? ? }};//字典轉(zhuǎn)模型,支持多級映射Student *stu = [Student mj_objectWithKeyValues:dict];//打印NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@",? ? ? stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime);// ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08NSLog(@"bagName=%@, bagPrice=%f", stu.bag.name, stu.bag.price);// bagName=a red bag, bagPrice=100.700000
6. 字典數(shù)組 --> 模型數(shù)組
使用實(shí)例:
NSArray*dictArray = @[? ? ? ? ? ? ? ? ? ? ? ? @{@"name":@"Jack",@"icon":@"lufy.png"},? ? ? ? ? ? ? ? ? ? ? ? @{@"name":@"Rose",@"icon":@"nami.png"}? ? ? ? ? ? ? ? ? ? ];//字典數(shù)組轉(zhuǎn)模型數(shù)組哄啄,使用的是mj_objectArrayWithKeyValuesArray:方法NSArray*userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];//打印for(User *userinuserArray) {NSLog(@"name=%@, icon=%@", user.name, user.icon);}// name=Jack, icon=lufy.png// name=Rose, icon=nami.png
7. 模型 --> 字典
使用實(shí)例:
//創(chuàng)建一個(gè)模型對象User *user = [[User alloc] init];user.name =@"Jack";user.icon =@"lufy.png";Status *status = [[Status alloc] init];status.user = user;status.text =@"Nice mood!";//模型轉(zhuǎn)字典雅任,使用的是mj_keyValues屬性NSDictionary*statusDict = status.mj_keyValues;NSLog(@"%@", statusDict);/*
{
text = "Nice mood!";
user =? ? {
icon = "lufy.png";
name = Jack;
};
}
*/
8. 模型數(shù)組 --> 字典數(shù)組
使用實(shí)例:
//創(chuàng)建模型數(shù)組User *user1 = [[User alloc] init];user1.name =@"Jack";user1.icon =@"lufy.png";User *user2 = [[User alloc] init];user2.name =@"Rose";user2.icon =@"nami.png";NSArray*userArray = @[user1, user2];//模型數(shù)組轉(zhuǎn)字典數(shù)組,使用的是mj_keyValuesArrayWithObjectArray:方法NSArray*dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];NSLog(@"%@", dictArray);/*
(
{
icon = "lufy.png";
name = Jack;
},
{
icon = "nami.png";
name = Rose;
}
)
*/
9. 字典 --> CoreData模型
使用實(shí)例:
NSDictionary*dict = @{@"name":@"Jack",@"icon":@"lufy.png",@"age": @20,@"height": @1.55,@"money":@"100.9",@"sex": @(SexFemale),@"gay":@"true"};//字典轉(zhuǎn)為CoreData模型NSManagedObjectContext*context =nil;User *user = [User mj_objectWithKeyValues:dict? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? context:context];[context save:nil];
10. 歸檔與解檔NSCoding
模型類Bag添加實(shí)現(xiàn):
@interfaceBag:NSObject@property(copy,nonatomic)NSString*name;@property(assign,nonatomic)doubleprice;@end#import"MJExtension.h"@implementationBag//添加了下面的宏定義MJExtensionCodingImplementation/* 實(shí)現(xiàn)下面的方法咨跌,說明哪些屬性不需要?dú)w檔和解檔 */+ (NSArray*)mj_ignoredCodingPropertyNames{return@[@"name"];}@end
使用實(shí)例:
//創(chuàng)建模型Bag *bag = [[Bag alloc] init];bag.name =@"Red bag";bag.price =200.8;//獲取歸檔路徑NSString*file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"];//歸檔[NSKeyedArchiverarchiveRootObject:bag toFile:file];//解檔Bag *decodedBag = [NSKeyedUnarchiverunarchiveObjectWithFile:file];NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);// name=(null), price=200.800000
11. 過濾字典的值
模型類Book實(shí)現(xiàn):
@interfaceBook:NSObject@property(copy,nonatomic)NSString*name;@property(strong,nonatomic)NSDate*publishedTime;@end#import"MJExtension.h"@implementationBook/* 轉(zhuǎn)化過程中對字典的值進(jìn)行過濾和進(jìn)一步轉(zhuǎn)化 */- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property{if([property.name isEqualToString:@"publisher"]) {if(oldValue ==nil) {return@"";? ? ? ? }? ? ? ? ? ? }elseif(property.type.typeClass == [NSDateclass]) {NSDateFormatter*fmt = [[NSDateFormatteralloc] init];? ? ? ? fmt.dateFormat =@"yyyy-MM-dd";return[fmt dateFromString:oldValue];? ? }returnoldValue;}@end
使用實(shí)例:
NSDictionary*dict = @{@"name":@"5分鐘突破iOS開發(fā)",@"publishedTime":@"2011-09-10"};//字典轉(zhuǎn)模型沪么,過濾name為nil的情況,把NSString轉(zhuǎn)為NSDateBook *book = [Book mj_objectWithKeyValues:dict];//打印NSLog(@"name=%@, publishedTime=%@", book.name, book.publishedTime);
MJExtension的github地址點(diǎn)這里:CoderMJLee/MJExtension