iOS FBKVOController 源碼分析

FBKVOController 源碼分析

---------摘取源碼重要知識點-----------

1.有意思的宏定義


/**
 This macro ensures that key path exists at compile time.
 Given a real receiver with a key path as you would call it, it verifies at compile time that the key path exists, without calling it.

 For example:

 FBKVOKeyPath(string.length) => @"length"

 Or even the complex case:

 FBKVOKeyPath(string.lowercaseString.length) => @"lowercaseString.length".
 */
#define FBKVOKeyPath(KEYPATH) \
@(((void)(NO && ((void)KEYPATH, NO)), \
({ const char *fbkvokeypath = strchr(#KEYPATH, '.'); NSCAssert(fbkvokeypath, @"Provided key path is invalid."); fbkvokeypath + 1; })))

上面這個宏定義什么意思呢票编?我們看注釋大概能明白要表達的意思方妖,就是取出,要觀察的對象的屬性值或者類的屬性值枣申,(void)(NO && ((void)KEYPATH, NO)) 這個的意思就是快速返回 NO还棱,減少運算撤摸,因為 NO&&任何都為NO,這里要注意温眉,這兩個 NO缸匪,目的就是為了讓KEYPATH不進行運算,因為有可能執(zhí)行g(shù)et方法类溢,因為目的只是做檢測凌蔬,所以不能讓他進行運算w,后面(void)KEYPATH這個是為了檢查編譯闯冷,是為了檢查KEYPATH砂心,接著 const char *fbkvokeypath = strchr(#KEYPATH, '.'); 搞了一個指針 fbkvokeypath , #KEYPATH 的意思是將傳進來的 KEYPATH 轉(zhuǎn)化為字符串,意思就是 test.name 轉(zhuǎn)化為 "test.name",因為轉(zhuǎn)化為字符串就可以方便操作了蛇耀,然后進行 strchr(#KEYPATH, '.')是為了截取. 后的字符串辩诞,也就是 string.length->.length,string.lowercaseString.length->.lowercaseString.length, NSCAssert 檢查字符串是否有效,最后 fbkvokeypath + 1 纺涤,也就是說 fbkvokeypath 的指針地址 +1译暂,也就是下一個地址,目的就是去掉 . , string.length->.length ->length撩炊,這就是最終目的外永,最終目的就是監(jiān)聽傳進來的對象或者類的屬性,也就是keyPath衰抑,有點意思象迎。

2. 有意思的 debug 打印



static NSString *describe_option(NSKeyValueObservingOptions option)
{
  switch (option) {
    case NSKeyValueObservingOptionNew:
      return @"NSKeyValueObservingOptionNew";
      break;
    case NSKeyValueObservingOptionOld:
      return @"NSKeyValueObservingOptionOld";
      break;
    case NSKeyValueObservingOptionInitial:
      return @"NSKeyValueObservingOptionInitial";
      break;
    case NSKeyValueObservingOptionPrior:
      return @"NSKeyValueObservingOptionPrior";
      break;
    default:
      NSCAssert(NO, @"unexpected option %tu", option);
      break;
  }
  return nil;
}

static void append_option_description(NSMutableString *s, NSUInteger option)
{
  if (0 == s.length) {
    [s appendString:describe_option(option)];
  } else {
    [s appendString:@"|"];
    [s appendString:describe_option(option)];
  }
}
//https://blog.csdn.net/weixin_33674976/article/details/91478135
static NSUInteger enumerate_flags(NSUInteger *ptrFlags)
{
  NSCAssert(ptrFlags, @"expected ptrFlags");
  if (!ptrFlags) {
    return 0;
  }

  NSUInteger flags = *ptrFlags;
  if (!flags) {
    return 0;
  }

  NSUInteger flag = 1 << __builtin_ctzl(flags);
  flags &= ~flag;
  *ptrFlags = flags;
  return flag;
}

static NSString *describe_options(NSKeyValueObservingOptions options)
{
  NSMutableString *s = [NSMutableString string];
  NSUInteger option;
  while (0 != (option = enumerate_flags(&options))) {
    append_option_description(s, option);
  }
  NSLog(@"%@",s);
  return s;
}


首先會調(diào)用 describe_options 方法,這個方法的意思就是呛踊,循環(huán)打印每個枚舉的信息砾淌,因為枚舉是位運算,經(jīng)過位運算之后谭网,最后會形成一個值汪厨,拿著這個值去打印每個的信息,什么意思呢愉择?比如


SCXEnum1 = 1<<0,
SCXEnum2 = 1<<1,
SCXEnum3 = 1<<2,

然后我們最后需要穿的值為 SCXEnum2 | SCXEnum3 劫乱,那么就是最后的值就是6,這個6包含了兩個值锥涕,二進制數(shù)據(jù)為 0110,當(dāng)我們調(diào)用 describe_options 這個方法的時候衷戈,會調(diào)用 enumerate_flags 這個方法,這個方法什么意思呢层坠?__builtin_ctzl 是找到二進制后右邊第一個不為1的位置殖妇,比如6也就是0110,穿進去返回的是1破花,也就是第一個1出現(xiàn)的位置谦趣,然后 1<<1,這個值不就是SCXEnum2疲吸,然后 flags &= ~flag;
*ptrFlags = flags;,就是將這個值從原來的總和里給去除前鹅,然后繼續(xù)while循環(huán)摘悴,挨個去除我們設(shè)置的值,也就是說把6分成了 SCXEnum2 和 SCXEnum3舰绘,是不是很巧妙蹂喻,學(xué)會了嗎?坐下除盏,以后再打印枚舉的值是不是有騷操作了叉橱?反正我以前是不會。

3.NSDictionary NSHashTable NSMapTable

3.1NSDictionary

在了解 NSHashTable 之前者蠕,讓我們先了解下 NSSet和NSDictionary窃祝,

1. 對于 object 都是強引用
2. NSDIctionary 的 key 需要實現(xiàn) NSCopying 協(xié)議,不實現(xiàn)比較麻煩
3. 使用 hash 獲取 hash 值踱侣,通過 isEqual 判斷是否相等粪小,如果hash相等

NSDictionary 要求 key 不能變,因為NSDIctionary中存儲的object的位置是由key來索引的抡句,并且要求key盡量小探膊,否則key的copy比較耗時,所以NSDIctionary的key不適合我們的自定義對象待榔,所以適用于 key->object 的映射

3.2NSHashTable

而我們 NSHashTable


1. 只有可變的逞壁,沒有不可變
2. 可以對加入的對象弱引用
3. 可以對加入的對象 copy
4. 可以包含任意指針,可以使用指針去判等

1.  NSHashTableStrongMemory = NSPointerFunctionsStrongMemory : 強引用對象
2. NSHashTableCopyIn=NSPointerFunctionsCopyIn:加入之前 copy 一份
3. NSHashTableObjectPointerPersonality=NSPointerFunctionsObjectPointerPersonality:使用指針進行isEqual:和 hash锐锣。
4. NSHashTableWeakMemory=NSPointerFunctionsWeakMemory:弱引用腌闯,對象銷毀時,自動銷毀


我們可以把他理解為我們的數(shù)組的高級版雕憔,可以存儲弱引用對象姿骏。

3.3NSMapTable

NSMapTable 是為了解決對象到對象的映射

NSMapTableStrongMemory : 強引用
NSMapTableWeakMemory :弱引用
NSPointerFunctionsObjectPersonality: isEqual和hash比較的是-description方法的值
NSPointerFunctionsObjectPointerPersonality : isEqual和hash比較的是指針的地址
NSMapTableCopyIn :copy

比如小明愛吃糖,小紅愛吃火鍋斤彼,如果是以前分瘦,用 NSDIctionary ,可以我們設(shè)計的是琉苇,

NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  [dic setObject:@"糖" forKey:@"xiaoming"];
  [dic setObject:@"火鍋" forKey:@"xiaohong"];

但如果我們以后加需求嘲玫,需要存儲每個人,和每個人的所有愛好并扇,如果以后我們想查某個人趁冈,及其這個人所有的愛好,NSDIctionary就滿足不了需求,所以我們可以用 NSMapTable


[dic setObject:person(人對象渗勘,里面存儲姓名年齡等) forKey:愛好(愛好對象,存儲所有愛好)];

hashTable 和 mapTable俩莽,都可以弱引用對象旺坠,比如將一個 obj,添加進去之后扮超,然后 obj = nil取刃,那么map中存儲的對象也會被移除,mapTable 無論是key或者value被移除,相應(yīng)的值都會被移除

4 源碼分析

上面給大家總結(jié)了一些關(guān)鍵知識點和一些有意思的代碼出刷,剩下的我想大家看源碼都能看懂璧疗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市馁龟,隨后出現(xiàn)的幾起案子崩侠,更是在濱河造成了極大的恐慌,老刑警劉巖坷檩,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件却音,死亡現(xiàn)場離奇詭異,居然都是意外死亡矢炼,警方通過查閱死者的電腦和手機系瓢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來句灌,“玉大人夷陋,你說我怎么就攤上這事∫刃浚” “怎么了骗绕?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長匕荸。 經(jīng)常有香客問我爹谭,道長,這世上最難降的妖魔是什么榛搔? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任诺凡,我火速辦了婚禮,結(jié)果婚禮上践惑,老公的妹妹穿的比我還像新娘腹泌。我一直安慰自己,他們只是感情好尔觉,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布凉袱。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪专甩。 梳的紋絲不亂的頭發(fā)上钟鸵,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天,我揣著相機與錄音涤躲,去河邊找鬼棺耍。 笑死,一個胖子當(dāng)著我的面吹牛种樱,可吹牛的內(nèi)容都是我干的蒙袍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嫩挤,長吁一口氣:“原來是場噩夢啊……” “哼害幅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岂昭,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤以现,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后佩抹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叼风,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年棍苹,在試婚紗的時候發(fā)現(xiàn)自己被綠了无宿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡枢里,死狀恐怖孽鸡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栏豺,我是刑警寧澤彬碱,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站奥洼,受9級特大地震影響巷疼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灵奖,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一嚼沿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓷患,春花似錦骡尽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箫踩。三九已至,卻和暖如春谭贪,著一層夾襖步出監(jiān)牢的瞬間境钟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工故河, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吱韭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓鱼的,卻偏偏與公主長得像,于是被迫代替她去往敵國和親痘煤。 傳聞我的和親對象是個殘疾皇子凑阶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容