前言:YYModel是由ibireme開(kāi)發(fā)的一套小而精美的模型轉(zhuǎn)換框架,采用分類(lèi)的形式,無(wú)需繼承框架的某個(gè)基類(lèi)就可以方便地完成模型的轉(zhuǎn)換,且內(nèi)部做了自動(dòng)類(lèi)型轉(zhuǎn)換和安全處理,可以有效地防止因模型類(lèi)型和后臺(tái)給的數(shù)據(jù)類(lèi)型不一樣而產(chǎn)生的崩潰問(wèn)題。
近些天抽空拜讀了一下其源碼,果然是思維嚴(yán)謹(jǐn),考慮的一些細(xì)節(jié)也很到位,讓人自嘆弗如。雖然作者說(shuō)這個(gè)框架是花了兩個(gè)周末的時(shí)間完成的,但是其代碼質(zhì)量還是非常讓人驚艷的,值得仔細(xì)閱讀。本文章就其中的一個(gè)對(duì)象的拷貝方法-modelCopy進(jìn)行學(xué)習(xí)。
一、modelCopy拷貝原理
modelCopy的拷貝原理是:將已有對(duì)象獲取其類(lèi)名 通過(guò) [obj.class. new]重新開(kāi)辟一塊新地址。存放新對(duì)象。但是新對(duì)象與舊對(duì)象的具體屬性,比如說(shuō)NSString? *name; name 的地址卻是同一個(gè)。再對(duì)新對(duì)象進(jìn)行改變的時(shí)候(即重新賦值),只要把新對(duì)象的屬性name指向新值的內(nèi)存區(qū)域即可。這樣對(duì)象與拷貝對(duì)象的屬性具體值在被修改時(shí)兩者互不影響。Ps:為什么要這樣寫(xiě)呢,因?yàn)槿绻惆褜?duì)象p1 = p2這樣賦值的話,改變p2里邊的某個(gè)屬性的值,p1的這個(gè)屬性值也被改變了。
接下來(lái)我們就用圖來(lái)說(shuō)明一下:
首先我先設(shè)定一個(gè)類(lèi) Person (這個(gè)類(lèi)已經(jīng)被教師用爛了,我這兒也用一下)

原有的一個(gè)對(duì)象 personTom? {湯姆,NO,18}? 地址為0x6d1,現(xiàn)在要將personTom克隆出一個(gè)來(lái)。那么被克隆出來(lái)的personJerry{湯姆,NO,18}? 地址為0x6E3。從其具體的某個(gè)屬性Name的值來(lái)看,兩個(gè)對(duì)象name所存放的地址 都是0Xeff1。所以利用modelCopy拷貝出來(lái)的某個(gè)屬性具體值是一樣的。 那么問(wèn)題就來(lái)了,怎么改變新對(duì)象personJerry的name呢。 只需要將@“jerry"在堆中的地址0xedd2 給personJerry的name即可(圖中綠線的指向)。

二、源碼解析
- (id)modelCopy{
// 這是NSObject對(duì)象直接調(diào)用本方法,所以 self 就是將要被復(fù)制的對(duì)象.(-_-)雖然比較基礎(chǔ),但還是提一下。?
? if (self == (id)kCFNull) return self;//self是空,則返回self?
? _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];? ? //下劃線"_"開(kāi)頭是作者用于僅僅在sdk內(nèi)部使用,外部可以使用的類(lèi)作者沒(méi)有添加”_“? 只是一種編程習(xí)慣.? 這里 _YYModelMeta 簡(jiǎn)單理解就是一個(gè)類(lèi) 里邊有具體的成員變量和屬性? ? if(modelMeta->_nsType)return[selfcopy];? ?
/*作用:如果 對(duì)象類(lèi)型unknown, 則將淺拷貝self?? ?
modelMeta->_nsType? 就是訪問(wèn) modelMeta 里邊的成員變量 _nsType?? ?
*/? ?
NSObject *one = [self.class new];//獲取self的類(lèi)名 新new了一個(gè)對(duì)象? ? for(_YYModelPropertyMeta*propertyMetainmodelMeta->_allPropertyMetas) {?
? ? ? //遍歷self 對(duì)象的所有屬性 "modelMeta->_allPropertyMetas"存放了self對(duì)象的所有屬性名稱(chēng)? ? ? ? if(!propertyMeta->_getter|| !propertyMeta->_setter)continue;? ?
? ? //如果 propertyMeta 沒(méi)有g(shù)etter 或者 沒(méi)有setter方法 就跳過(guò)本次循環(huán),執(zhí)行下次循環(huán)? ? ? ? if(propertyMeta->_isCNumber) {?
//如果self的被遍歷的這個(gè)屬性 是一個(gè) 數(shù)值類(lèi)型? ? ? ?
? ? switch(propertyMeta->_type&YYEncodingTypeMask) {? ?? ? ? ? ? ? ? ? //switch的條件 是 枚舉值的二進(jìn)制按位與運(yùn)算 即1010 & 1111 = 1010? ? ? ? ?
? ? ? caseYYEncodingTypeBool: {? ? ? ? ? ? ? ? ? ?//YYEncodingTypeBool? c語(yǔ)言的bool類(lèi)型? ? ? ? ? ? ? ? ? ? boolnum = ((bool(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ?
? ? /*?? ? ? ? ? ? ? ? ? ? 整句話的意思就是一個(gè)Getter方法調(diào)用? 即獲取self 的這個(gè)bool類(lèi)型的屬性的值。類(lèi)似于這:?? ? ? ? ? ? ? ? ? ?
- (bool)getMethod{
?? ? ? ? ? ? ? ? ? ? ? return ture;?? ?
? ? ? ? ? ? ? ? }?? ? ? ?
? ? ? ? ? ? 分解理解:?? ? ? ? ? ? ? ? ? ?
bool num? ? ? 定義一個(gè)bool 變量num來(lái)接受getter返回的值?? ? ? ? ? ? ? ? ? ?
bool (*)? ? ? 修飾被調(diào)用方法的返回值類(lèi)型 返回一個(gè)bool類(lèi)型的值?? ? ? ? ? ? ? ? ? ?
id,? ? ? ? ? ? objc_msgSend方法的第一個(gè)參數(shù)的類(lèi)型,即任意對(duì)象類(lèi)型。?? ? ? ? ? ? ? ? ? ? SEL? ? ? ? ? ? obj_msgSend 第二個(gè)參數(shù) 即obj_msgSend使用的方法名稱(chēng)?? ? ? ? ? ? ? ? ? ? (void *)? ? ? 一般來(lái)說(shuō)這個(gè)修飾關(guān)鍵字可以省略, 筆者暫不理解作者為何這樣寫(xiě)。待深入學(xué)習(xí)補(bǔ)充?? ? ? ? ? ? ? ? ? ?
objc_msgSend? runtime中動(dòng)態(tài)獲取方法 的函數(shù),獲取后并且會(huì)執(zhí)行?? ? ? ? ? ? ? ? ? ? (id)self? ? ? objc_msgSend 第一個(gè)參數(shù) 即任意oc對(duì)象類(lèi)型 self 就是objc_msgSend將要操作的對(duì)象? ? ? ?
propertyMeta->_getter? ? ? 獲取 propertyMeta 的成員變量_getter 成員變量的值就是一個(gè)SEL 類(lèi)型的方法名稱(chēng)?? ? ? ? ? ? ? ? ? ?
*/? ? ? ? ? ? ? ? ?
? ((void(*)(id,SEL,bool))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? ? ? /*?? ? ? ? ? ? ? ? ? ? 整句話的意思就是一個(gè)Setter方法調(diào)用? 即 利用setter方法 給新對(duì)象one? 的bool類(lèi)型屬性 賦值num。類(lèi)似于這:?? ? ? ? ? ? ? ? ? ?
- (void) setMethod:(bool)num{?? ? ? ? ? ? ?
? ? ? ? _type = type;?? ? ? ? ? ? ? ? ? ?
}?? ? ? ? ? ? ? ? ? ?
分解理解:?? ? ? ? ? ? ? ? ? ?
void (*)? ? ? 修飾被調(diào)用方法的返回值類(lèi)型? 返回void?? ? ? ? ? ? ? ? ? ?
id,? ? ? ? ? ? objc_msgSend方法的第一個(gè)參數(shù)的類(lèi)型,即任意對(duì)象類(lèi)型。?? ? ? ? ? ? ? ? ? ? SEL? ? ? ? ? ? objc_msgSend 第二個(gè)參數(shù)的方法名稱(chēng)?? ? ? ? ? ? ? ? ? ?
bool? ? ? ? ? objc_msgSend 第三個(gè)參數(shù) 這個(gè)動(dòng)態(tài)方法傳入?yún)?shù)的值。即type?? ? ? ? ? ? ? ? ? ? (void *)? ? ? 一般來(lái)說(shuō)這個(gè)修飾關(guān)鍵字可以省略, 筆者暫不理解作者為何這樣寫(xiě)。待深入學(xué)習(xí)補(bǔ)充?? ? ? ? ? ? ? ? ? ?
objc_msgSend? runtime中動(dòng)態(tài)獲取方法 的函數(shù),獲取后并且會(huì)執(zhí)行?? ? ? ? ? ? ? ? ? ? (id)one? ? ? ? objc_msgSend 第一個(gè)參數(shù) 即任意oc對(duì)象類(lèi)型 one 就是objc_msgSend將要操作的對(duì)象?? ? ?
propertyMeta->_getter? ? ? ? 獲取 propertyMeta 的成員變量_setter 成員變量的值就是一個(gè)SEL 類(lèi)型的方法名稱(chēng)? ? ? ? ? ? ? ? ? ? ?
num? ? ? ? ? num 傳入setter的參數(shù)值?? ? ? ? ? ? ? ? ? ?
*/? ? ? ? ? ? ? ?
}break;? ? ? ?
? ? ? ? case YYEncodingTypeInt8:? ? ? ?
? ? ? ? caseYYEncodingTypeUInt8: {? ?
? ? ? ? ? ? ? ? //uint8_t 就是無(wú)符號(hào)字符類(lèi)型? 就是對(duì) unsigned char 重新命名 typedef? ? ? ? ? ? ? ? ? ? uint8_tnum = ((bool(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ? ?
((void(*)(id,SEL,uint8_t))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ? ? ?
? case YYEncodingTypeInt16:? ? ? ? ? ? ? ?
case YYEncodingTypeUInt16: {? ? ? ? ? ? ? ? ?
? // typedef unsigned short uint16_t;? ? ? ? ? ? ? ? ? ?
uint16_tnum = ((uint16_t(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ? ?
((void(*)(id,SEL,uint16_t))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ? ? ? ?
case YYEncodingTypeInt32:? ? ? ? ? ? ? ?
case YYEncodingTypeUInt32: {? ? ? ? ? ? ? ? ? ? //typedef unsigned int uint32_t;? ? ? ? ? ? ? ? ? ? uint32_tnum = ((uint32_t(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ? ?
((void(*)(id,SEL,uint32_t))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ?
? ? ? case YYEncodingTypeInt64:? ? ? ? ?
? ? ? case YYEncodingTypeUInt64: {? ? ? ? ? ? ? ? ? ? //typedef unsigned long long uint64_t;? ? ? ? ? ? ? ? ? ? uint64_tnum = ((uint64_t(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ? ?
((void(*)(id,SEL,uint64_t))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ? ? ? ?
caseYYEncodingTypeFloat: {? ? ? ? ? ? ? ? ?
? floatnum = ((float(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ?
? ((void(*)(id,SEL,float))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ? ? ? ?
case YYEncodingTypeDouble: {? ? ? ? ? ? ? ? ? ?
doublenum = ((double(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ?
? ((void(*)(id,SEL,double))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ? ? }break;? ? ? ? ?
? ? ? case YYEncodingTypeLongDouble: {? ? ? ? ? ? ? ? ?
? longdoublenum = ((longdouble(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ?
? ? ? ((void(*)(id,SEL,longdouble))(void*)objc_msgSend)((id)one, propertyMeta->_setter, num);? ? ? ? ? ? ?
? }// break; commented for code coverage in next line? ? ? ? ? ? ?
? default:break;? ? ? ?
? ? }? ? ? ?
}else{? ? ? ? ? ? //當(dāng)該屬性不是一個(gè)數(shù)值類(lèi)型時(shí)候? ? ? ? ? ?
switch(propertyMeta->_type&YYEncodingTypeMask) {? ? ? ? ?
? ? ? case YYEncodingTypeObject:? ? ? ? ?
? ? ? case YYEncodingTypeClass:? ? ? ? ?
? ? ? caseYYEncodingTypeBlock: {? ? ? ? ? ? ? ? ? ? //當(dāng)是對(duì)象類(lèi)型、類(lèi)、block時(shí)候用 id來(lái)獲取其值? ? ? ? ? ? ? ? ? ?
idvalue = ((id(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);? ? ? ? ? ? ? ? ? ? ((void(*)(id,SEL,id))(void*)objc_msgSend)((id)one, propertyMeta->_setter, value);? ? ? ? ? ? ? ? }break;? ? ? ? ? ?
? ? caseYYEncodingTypeSEL:? ? ? ?
? ? ? ? case YYEncodingTypePointer:? ? ? ?
? ? ? ? case YYEncodingTypeCString: {? ? ? ? ? ? ? ? ? ? //size_t 經(jīng)過(guò)代碼測(cè)試 就是將 SEL 、void* 、 char* 值的地址轉(zhuǎn)換成10進(jìn)制 傳給value? ? ? ? ? ?
? ? ? ? size_tvalue = ((size_t(*)(id,SEL))(void*)objc_msgSend)((id)self, propertyMeta->_getter);//這里getter獲取到就是一個(gè)地址的10進(jìn)制表示值。? size_t 可以理解為long、int? ? ? ? ? ?
? ? ? ? ((void(*)(id,SEL,size_t))(void*)objc_msgSend)((id)one, propertyMeta->_setter, value);? ? ? ? ?
? ? ? }break;? ? ? ? ? ? ?
? case YYEncodingTypeStruct:? ? ? ?
? ? ? ? caseYYEncodingTypeUnion: {? ? ? ? ? ? ? ? ?
? /*?? ? ? ? ? ? ? ? ? ? 結(jié)構(gòu)體、聯(lián)合體類(lèi)型?? ? ? ? ? ? ? ?
? ? 作者用了try catch 放置在轉(zhuǎn)換結(jié)構(gòu)體時(shí)候轉(zhuǎn)crash。完備性操作?? ? ? ?
? ? ? ? ? ? */? ? ? ? ? ? ? ? ?
? @try{? ? ? ? ? ? ? ? ? ?
? ? NSValue*value = [selfvalueForKey:NSStringFromSelector(propertyMeta->_getter)];? ? ? ? ? ? ? ? ? ? ? ? if(value) {? ? ? ? ? ? ? ? ? ? ?
? ? ? [onesetValue:valueforKey:propertyMeta->_name];? ? ? ? ? ? ? ?
? ? ? ? }? ? ? ? ? ? ? ?
? ? }@catch(NSException *exception) {}? ? ? ? ? ? ?
? }// break; commented for code coverage in next line? ? ? ? ?
? ? ? default:break;? ? ? ? ?
? }? ? ?
? }?
? }? ?
returnone;}
三、小結(jié)
很粗略地記錄下部分閱讀過(guò)程。YYModel有不少值得學(xué)習(xí)的地方,不管是代碼風(fēng)格還是考慮問(wèn)題的全面性,這些都需要通過(guò)閱讀源碼來(lái)了解。
如發(fā)現(xiàn)不當(dāng)之處,歡迎留言指出。代碼之路漫長(zhǎng),希望與你同行