這里是我學(xué)習(xí)過程中找到的一條探究思路公你,希望可以開啟你的新世紀(jì)大門,少走點(diǎn)彎路吧假瞬,書籍推薦《Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理》陕靠,這里面有通過GNUStep源碼分析了OC的內(nèi)存管理方面的原理。gnustep-base-1.26.0 源碼注釋筆記脱茉。KVC底層剪芥,KVC底層原理,KVC本質(zhì)芦劣,KVC原理,GNUstep
前半部分是結(jié)論说榆,后半部分是驗(yàn)證虚吟,重點(diǎn)!G┎啤串慰!
KVC
KVC全稱Key Value Coding(鍵值編碼),是可以通過對(duì)象屬性名稱直接對(duì)屬性值進(jìn)行編碼(賦值及訪問)唱蒸。而不需要調(diào)用明確的存取方法邦鲫。這樣就可以運(yùn)行時(shí)動(dòng)態(tài)訪問和修改對(duì)象的屬性,而不是在編譯時(shí)確定。
賦值 setValue:forKey:的原理
- 按照setKey:庆捺、_setKey:的順序查找方法
- 如果找到了方法就傳遞參數(shù)古今,調(diào)用方法
- 如果找不到,就去檢查是否可以直接訪問實(shí)例變量(+accessInstanceVariablesDirectly)滔以,如果NO就是不可以捉腥,那么就調(diào)用setValue:forUndefineKey:并拋出異常NSUnknownKeyException
- 如果+accessInstanceVariablesDirectly返回YES,就按照_key,_isKey,key,_isKey的順序查找成員變量
- 如果找到了成員變量你画,就直接賦值抵碟。
- 如果 _key、_isKey坏匪、key拟逮、isKey的順序沒有查找到成員變量就調(diào)用setValue:forUndefinedKey: 并拋出異常 NSUnknownKeyException
核心:setValue:forKey:->setKey、_setKey->accessInstanceVariablesDirectly->_key适滓、_isKey敦迄、key、isKey
取值 valueForKey:的原理
- 按照getKey,key,_isKey,_key的順序查找方法
- 如果找到了就傳遞參數(shù)粒竖,調(diào)用方法
- 如果沒找到颅崩,就檢查是否可以直接訪問實(shí)例變量(+accessInstanceVariablesDirectly),如果不可以蕊苗,就調(diào)用setValue:forUndefineKey:并拋出異常NSUnknownKeyException
- 如果+accessInstanceVariablesDirectly返回YES沿后,就按照_key、_isKey朽砰、key尖滚、isKey的順序查找成員變量
- 如果找到了成員變量,就直接取值
- 如果 _key瞧柔、_isKey漆弄、key、isKey的順序沒有查找到成員變量就調(diào)用valueForUndefinedKey:并拋出異常NSUnknownKeyException
核心:valueForKey:->getKe造锅、key撼唾、_isKey、_key->accessInstanceVariablesDirectly->_key哥蔚、_isKey倒谷、key、isKey
看到這里不要著急糙箍,精彩在后面2吵睢!深夯!
GNUstep是Cocoa框架的互換框架抖格。也就是說,GNUstep的源代碼雖不能說與蘋果的Cocoa實(shí)現(xiàn)完全相同,但是從使用者的角度來看雹拄,兩者的行為和實(shí)現(xiàn)是一樣的收奔,或者說非常相似。理解了GNUstep源代碼也就是相當(dāng)于理解了蘋果的Cocoa實(shí)現(xiàn)办桨。
static void
SetValueForKey(NSObject *self, id anObject, const char *key, unsigned size)
{
SEL sel = 0;
const char *type = 0;
int off = 0;
if (size > 0)
{
const char *name;
char buf[size + 6];
char lo;
char hi;
//C 庫(kù)函數(shù) char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串復(fù)制到 dest筹淫,最多復(fù)制 n 個(gè)字符。當(dāng) src 的長(zhǎng)度小于 n 時(shí)呢撞,dest 的剩余部分將用空字節(jié)填充损姜。
strncpy(buf, "_set", 4);
strncpy(&buf[4], key, size);
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
buf[size + 4] = ':';
buf[size + 5] = '\0';
//到這時(shí)buf里面存的字符串是_setKey:,下面遇到的 &buf[1]殊霞,buf分別代表setKey:摧阅,_setKey:
name = &buf[1]; // setKey:
type = NULL;
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = buf; // _setKey:
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
//此處判斷:是否可以直接訪問實(shí)例變量,英語(yǔ)重要性
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
//清0 buf绷蹲,lo = key
buf[size + 4] = '\0';
buf[3] = '_';
buf[4] = lo;
name = &buf[3]; // _key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
//hi = Key
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
buf[1] = '_';
name = &buf[1]; // _isKey
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = lo;
name = &buf[4]; // key
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
GSObjCFindVariable(self,
name, &type, &size, &off);
}
}
}
}
//其中任何一次找到key棒卷,都會(huì)調(diào)用GSObjCFindVariable,實(shí)現(xiàn)賦值操作祝钢。
}
else
{
GSOnceFLog(@"Key-value access using _setKey: is deprecated:");
}
}
}
GSObjCSetVal(self, key, anObject, sel, type, size, off);
}
//下面這段代碼是KVC取值操作原理
static id ValueForKey(NSObject *self, const char *key, unsigned size)
{
SEL sel = 0;
int off = 0;
const char *type = NULL;
if (size > 0)
{
const char *name;
char buf[size + 5];
char lo;
char hi;
strncpy(buf, "_get", 4);
strncpy(&buf[4], key, size);
buf[size + 4] = '\0';
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
name = &buf[1]; // getKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = lo;
name = &buf[4]; // key
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
if (sel == 0 && [[self class] accessInstanceVariablesDirectly] == YES)
{
buf[4] = hi;
name = buf; // _getKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[3]; // _key
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
if (sel == 0)
{
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
buf[1] = '_';
name = &buf[1]; // _isKey
if (!GSObjCFindVariable(self, name, &type, &size, &off))
{
buf[4] = lo;
name = &buf[4]; // key
if (!GSObjCFindVariable(self, name, &type, &size, &off))
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
}
}
}
}
return GSObjCGetVal(self, key, sel, type, size, off);
}
賦值setValue:forKeyPath原理
- (void) setValue: (id)anObject forKeyPath: (NSString*)aKey
{
//以.切割keyPath比规,遞歸調(diào)用setValue:forKeyPath:方法逐步遞歸取值,知道最后不能再遞歸就賦值
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
#ifdef WANT_DEPRECATED_KVC_COMPAT
IMP o = [self methodForSelector: @selector(takeValue:forKeyPath:)];
setupCompat();
if (o != takePath && o != takePathKVO)
{
(*o)(self, @selector(takeValue:forKeyPath:), anObject, aKey);
return;
}
#endif
if (r.length == 0)
{
[self setValue: anObject forKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
[[self valueForKey: key] setValue: anObject forKeyPath: path];
}
}
上面截取的代碼是KVC賦值與取值的操作拦英,主要解讀了下賦值的操作蜒什。有人會(huì)覺得畢竟兩個(gè)框架不一樣,需要驗(yàn)證下:這里我只描述下如何驗(yàn)證原理疤估,網(wǎng)上很多博客KVC底層原理的分析灾常,都只是驗(yàn)證,我這里是配合類源碼分析了下铃拇。
場(chǎng)景:對(duì)一個(gè)Person類中的name屬性進(jìn)行KVC賦值與取值钞瀑。賦值,我們?cè)谶@個(gè)Person類中慷荔,使用賦值操作原理中提到的方法和成員變量覆蓋掉Person類隱藏的雕什,然后逐個(gè)驗(yàn)證。