研读源码可以学学其中的框架和格局,FBKVOController的基本代码一共4个类

自释放

FBKVOController通过自释放的编制来贯彻observer的自发性移除,具体来说就是给observer添加一个FBKVOController的成员变量,比如:

#import "RJViewController.h"
#import "KVOController.h"

@interface RJViewController ()

@property (nonatomic, strong) FBKVOController *kvoController;

@end

@implementation RJViewController

- (instancetype)init
{
  self = [super init];
  if (nil != self) {
      _kvoController = [FBKVOController controllerWithObserver:self];
  }
  return self;
}

观察者RJViewController概念了一个FBKVOController的分子变量kvoController,
RJViewController放活后,其成员变量kvoController也会相应释放,FBKVO自动移除观察者的trick就是在FBKVOControllerdealloc里面做remove
observer的操作。

FBKVOController

巡回引用

大家领会,使用block的时候极易并发循环引用,平时使用方须要在block内部协调声圣元个weak化的self来幸免那些标题。这有没有办法省去这一步呢?是的,FBKVO在接口设计的时候也考虑到了这一个标题,解决措施是在block回调接口增添一个observer参数,而这个observer在FBKVOController内部做了weak化处理,在地点示例中,id observer不怕观察者(即RJViewController *observer),也就是发轫化接口时传出的self,
所以在block内部一向利用[observer doSomething]来代替[self doSomething]即可幸免循环引用的标题。

FBKVO防止循环引用的布署性真正很精致,大家来接着看上边这一个情况:

[self.KVOController observe:self.photos                        
                    keyPath:@"count"
                    options:NSKeyValueObservingOptionNew
                      block:^(id observer, id object, NSDictionary *change) {
    // observer -> RJViewController -> __weak self
    // object   -> self.photos    
    // [self doSomething] -> [observer doSomething]
    [self.KVOController unobserve:self.photos keyPath:@"count"]
}];

这里在block里面用self调用了unobserve格局,根据大家事先的明亮,那那边肯定会见世循环引用了,应该改成:

[observer.KVOController unobserve:observer.photos keyPath:@"count"]

但事实是在那个场所下,即选用self也不会挑起循环引用,那是怎么吗?原因是做了unobserve操作后,存储KVO信息的_FBKVOInfo会被放飞掉,这样它所针对的方今那几个block也会被置为nil,
这样block引用self以此链端就被打破了,也就不会出现循环引用的难题了。所以打破循环引用除了在block内使用weakSelf外,也得以在事件处理完后将近来的block置为nil来实现。

FBKVOController.m一共可以分成3部分:

出于近期早就有那个有关FBKVOController源码分析的博文,本文子禽尝试从其余一个角度,以提炼和分析具体知识点的法门来总括FBKVOController中大家可以借鉴和上学的地点。

再来看一下首先个API函数。即使object,
keyPath或者block为空,则直接重返。接着依据传入的参数开首化_FBKVOInfo这些里面数据结构,然后调用[self
_observe:object info:info]。

线程锁

FBKVO使用pthread_mutex_t用作线程锁,关于iOS各类线程锁的介绍可以参照这篇博文。大家一贯来看下FBKVO使用的内部一个地点:

- (void)_unobserveAll
{
  // lock
  pthread_mutex_lock(&_lock);

  NSMapTable *objectInfoMaps = [_objectInfosMap copy];

  // clear table and map
  [_objectInfosMap removeAllObjects];

  // unlock
  pthread_mutex_unlock(&_lock);

  _FBKVOSharedController *shareController = [_FBKVOSharedController sharedController];

  for (id object in objectInfoMaps) {
    // unobserve each registered object and infos
    NSSet *infos = [objectInfoMaps objectForKey:object];
    [shareController unobserve:object infos:infos];
  }
}

锁的基本尺度是颇具对公共数据拍卖的地方都亟需加锁,下边的代码中_objectInfosMap为全局的NSMapTable,
对其修改操作(Add/Remove)都亟需加锁,
FBKVO那边的操作很值得借鉴,先拷贝一份临时变量,然后将_objectInfosMap清空,这一步在锁中间操作,之后被拷贝出的那份非全局或者说非共享变量再去做相应的继承操作。

那样的话尽管有多少个线程访问_unobserveAll也不会有其余难点,因为唯有首先个线程会访问到_objectInfosMap,
首个线程等解锁后再去拜访时_objectInfosMap早就为空了,拷贝的靶子objectInfoMaps也自然为空,所以锁并不要求加满整个_unobserveAll函数范围。

假设急需采纳互斥类型的pthread_mutex_t锁,比如在递归函数中加锁,那必要将pthread_mutex_t开头化为互斥类型:

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex, &attr);
pthread_mutexattr_destroy(&attr);

锁使用完后记得要在dealloc里面销毁掉:

- (void)dealloc {
    pthread_mutex_destroy(&_mutex);
}

图片 1

FBKVOController是非死不可开源的接口设计优雅的KVO框架。作者研读之后确实受益匪浅,本着学以致用的条件,小编借鉴其接口设计的点子贯彻了一套完整的小红点(推送信息)解决方案RJBadgeKit,
有趣味的同校可以参见一下。

– (void)_observe:info:.png

即使说书籍是人类前行的阶梯,那么完美的开源代码就是程序员进步的桥梁。研读源码可以学学其中的框架和格局,
代码技巧,
算法等,然后不断计算运用,最后那些会变成自己的事物,编程水平自然也增强了。

FBKVOController.png

DEBUG描述

DEBUG描述(debugDescription)其实和description是同一的法力,只是debugDescription是在Xcode控制台里使用po命令的时候调用呈现的。如果没有兑现debugDescription方法,那么打印该目的的时候只是突显内存地址,而不会来得该对象的顺序属性值。

- (NSString *)debugDescription
{
  NSMutableString *s = [NSMutableString stringWithFormat:@"<%@:%p keyPath:%@", NSStringFromClass([self class]), self, _keyPath];
  if (0 != _options) {
    [s appendFormat:@" options:%@", describe_options(_options)];
  }
  if (NULL != _action) {
    [s appendFormat:@" action:%@", NSStringFromSelector(_action)];
  }
  if (NULL != _context) {
    [s appendFormat:@" context:%p", _context];
  }
  if (NULL != _block) {
    [s appendFormat:@" block:%p", _block];
  }
  [s appendString:@">"];
  return s;
}

上面是FBKVO实现的_FBKVOInfo的debugDescription,
将相继属性值拼接成字符串突显出来。那假设某个对象有N五个特性,那样一个个拼接会分外麻烦,那种状态下得以选取runtime来动态获取属性并赶回:

- (NSString *)debugDescription // prefer super class
{ 
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

    // fetch class's all properties
    uint count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    // loop to get each property via KVC
    for (int i = 0; i<count; i++) {
        objc_property_t property = properties[I];
        NSString *name = @(property_getName(property));
        id value = [self valueForKey:name]?:@"nil"; // default nil string
        [dictionary setObject:value forKey:name]; // add to dicionary
    }
    free(properties);

    return [NSString stringWithFormat:@"<%@-%p> -- %@",[self class],self,dictionary];
}

_FBKVOSharedController

数据结构

KVOController是框架的对外接口类,作为KVO的经营管理者,其兼具了脚下阅览者对象和被阅览者的KVO新闻。
观看者对象以weak特性存储在_observer中,而_objectInfosMap校官被观看者以key举行仓储,
value则存储了相应的_ FBKVOInfo会见(图片引用自Draveness的博文)。

KVOController

FBKVO为每一个被observe的靶子都生成了一个_ FBKVOInfo对象,该目的存储了独具与KVO相关的音信,包含路径,回调等等。

FBKVOInfo

FBKVO的调用流程如下图所示,
FBKVOController的功用只是丰裕相应的被寓目者记录,以及变化对应的FBKVOInfo音信,最后会由FBKVOSharedController
那几个单例来调用系统KVO方法已毕对品质的监听,并且在回调方法准将事件分发给
KVO 的观望者。

调用流程

FBKVO中还有一个比较有趣的地点是用_来分别内部接口和外部接口:

- (void)_unobserve:(id)object info:(_FBKVOInfo *)info // -> private method
- (void)unobserve:(nullable id)object keyPath:(NSString *)keyPath // -> public method

席卷类名也是那般:

@interface FBKVOController : NSObject        // -> public 
@interface _FBKVOInfo : NSObject             // -> internal 
@interface _FBKVOSharedController : NSObject // -> internal 

FBKVO的代码量纵然不多,但其框架流程,接口设计和代码中使用到的微小技术点确实极具水平,希望本文统计和提炼的各样姿势可以让我们有一部分赢得,也欢迎咱们留言探讨。完。

里头NSMapTable和NSDictionary类似,不过两者又有分别。

初始化

俺们先来探视FBKVOController是何许提供伊始化接口的:

+ (instancetype)controllerWithObserver:(nullable id)observer;
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObserver:(nullable id)observer;

统计有3个早先化函数,其中首个和第七个为便宜开始化函数(convenience
initializer),
第三个添加了NS_DESIGNATED_INITIALIZER宏,为指定开始化函数(designated
initializer).

早先化接口的条条框框:
a) 指定开头化方法必须调用父类的指定早先化方法
b)
便利先河化方法必须调用其余的早先化方法,直到最后指向指定早先化方法
c) 具有指定早先化方法的子类必须兑现所有父类的指定开头化方法

知情了自释放的法则,开始化的平整就很鲜明了,FBKVOController必须作为observer的成员变量存在。那如若使用方忽视了那些规则或者不想这样繁琐,有没有更简便的方法啊?有!大家来看下FBKVO是怎么提供最简化convenience
initializer的:

@interface NSObject (FBKVOController)

@property (nonatomic, strong) FBKVOController *KVOController;
@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;

@end

FBKVOController成立了NSObject的Category,
通过AssociateObject给NSObject提供一个Retain和nonRetain的KVOController(那里实在也是在成员变量KVOController的Get函数里面调用了+ controllerWithObserver主意)。所以任意observer都可以一向调用observer.KVOController来动态变化一个FBKVOController对象,非凡有利!

到这时候看起来初阶化接口已经很完整了,但好像还有个难点,万一使用方不按套路直接来个系统默许的起初化函数[[FBKVOController alloc] init]或者[FBKVOController new]那Observer岂不是就从未有过了。怎么做才能提醒使用方不要调用系统的早先化函数呢?

/**
 @abstract Allocates memory and initializes a new instance into it.
 @warning This method is unavaialble. Please use `controllerWithObserver:` instead.
 */
- (instancetype)init NS_UNAVAILABLE;

+ (instancetype)new NS_UNAVAILABLE;

答案就是在那多少个默许开端化函数前边加上NS_UNAVAILABLE宏,这样借使使用方误用了系统默许的起始化函数时会给出警告,提示她应该运用模块指定的初步化接口方法。

作者:alvin_wang

宏定义

普普通通在增加观看者的时候都急需指定一个考察路径(keyPath),
那个路子是一贯以字符串的艺术提供的,比如大家有个类RJPhoto的对象photo,
要求考察它的name路径:

[self.KVOController observe:photo keyPath:@"name"];

如若字符串拼写错误,或者被observe的对象没有name本条特性,编译器并不会报错,唯有等到运行时才会意识难点。我们来看下FBKVOController是怎么通过宏定义来解决这些标题标:

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

#define FBKVOClassKeyPath(CLASS, KEYPATH) \
@(((void)(NO && ((void)((CLASS *)(nil)).KEYPATH, NO)), #KEYPATH))

有了这八个宏,被观望者的keyPath可以经过宏传入,其利益在于该宏会进行编译检查和代码提醒,如若keyPath不设有或者拼写错误,会唤起错误。

[self.KVOController observe:photo keyPath:FBKVOKeyPath(photo.name)];
[self.KVOController observe:photo keyPath:FBKVOClassKeyPath(RJPhoto, name)];

下面的宏是怎么完毕编译检查和代码提醒的呢?大家先分析第四个相对相比较复杂的宏FBKVOClassKeyPath,
其完全是一个C语言的逗号表达式,逗号表明式的格式: e.g.
int a = (b, c);逗号表明式取后边的值,故而a将被赋值成c,
此时b在赋值运算中就被忽略了,没有被应用,所以编译器会付给警告,为了破除那几个warning大家须求在b前边加上(void)做个档次强转操作。

逗号表达式的前项和NO进行了与操作,这一个至关重倘诺为了让编译器忽略第四个值,因为大家实在赋值的是表明式前边的值。预编译的时候看见了NO,
就会疾速的跳过判断标准。我猜你看到这儿肯定会奇怪了,既然要不经意,那为什么还要用个逗号表达式呢,直接赋值不就好了?

此处紧倘若对传播的率先个参数CLASS的靶子(CLASS *)(nil)和首个正要输入的KEYPATH做了.操作,那也正是为什么输入第四个参数时编辑器会提交正确的代码提醒(只如果作为表明式的一部分,
Xcode自动会提醒)。若是传入的KEYPATH不是CLASS对象的习性,那么(CLASS *)(nil).KEYPATH就不是一个法定的表明式,所以本来编译就不会通过了。

FBKVOKeyPath经受一个参数,前半段和地方是相同的,不一致的是逗号表明式的后一段strchr(# photo.name, '.') + 1,
函数strchar是C语言中的函数,用来探寻某字符在字符串中首次面世的地方,那里用来在photo.name(注意前方加了#字符串化)中检索.并发的职分,再添加1就是回来.后面keyPath的地点了。也就是strchr('photo.name', '.')归来的是一个C字符串,这一个字符串从找到'photo.name'中为'.'的字符初阶将来,即'name'.

这边还用到了断言宏NSCAssert(x, y), xBOOL值, y为字符串类型,
xNO时暴发断言退出并打印y字符串内容.
需求小心的是NSCAssert在C语言函数下利用,
NSAssert则是Objective-C函数下行使

关于宏定义的详尽解释以及地点所述的好像宏定义的行使和剖析,可以参考作者的博文Hello,
宏定义魔法世界

图片 2

NSHashTable & NSMapTable

NSHashTable可以清楚为更宽泛意义上的NSMutableSet,
与后者比较NSMapTable主要有如下特点:

  • NSHashTable是可变的, 没有不可变版本
  • 可以弱引用所蕴藏的要素, 当元素释放后会自动被移除
  • 可以在添日币素的时候复制元素后再存放

与NSMutableSet相同之处(与NSMutableArray分裂之处)则是:

  • 要素都是无序存放的
  • 根据hashisEqual来对元素进行比较
  • 不会存放相同的元素

有关相比,大家要先区分==运算符和isEqual方法:

UIColor *color1 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
UIColor *color2 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];

在地方的示范中color1 == color2返回false,
[color1 isEqual:color2]却是再次来到true,
原因在于==是一贯的指针比较,分明color1和color2的地点是例外的,而isEqual则是判定其颜色内容是或不是相同。

接近的还蕴涵NSString isEqualToString / NSDate isEqualToDate

回去NSHashTable中来,
NSHashTable可以任意的存储指针并且使用指针的唯一性来进行hash同一性检查(检查成员元素是不是有再度)和对待操作(isEqual),
当然大家也可以重写hash/isEqual方法来设定元素相比较和杰出的平整(其实isEqual是NSObject定义的Protocol).
我们来看上面那么些示例:

@interface Person : NSObject

@property (nonatomic,   copy) NSString *name;
@property (nonatomic, strong) NSDate   *birthday;

+ (instancetype)personWithName:(NSString *)name birthday:(NSDate *)date;

@end

咱俩定义一个Person类,其包含name和birthday多个属性,在我们的健康认知下,如若三个Persion对象的那五个特性是一样的,那么她们即使同一个人,所以类似上边UIColor的例子,大家需要重写下Person的isEqual函数:

- (BOOL)isEqual:(id)object
 {
    if (nil == object) {
        return NO;
    }
    if (self == object) {
        return YES;
    }
    if (![object isKindOfClass:[Person class]]) {
        return NO;
    }    
    return [self isEqualToPerson:(Person *)object];
}

- (BOOL)isEqualToPerson:(Person *)person
 {
    if (!person) return NO;

    BOOL haveEqualNames     = (!self.name && !person.name) || [self.name isEqualToString:person.name];
    BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];

    return haveEqualNames && haveEqualBirthdays;
}

这里的isEqual函数的贯彻分为四步,也是大家引进的best pratice:

  1. 判定目的是不是为空
  2. 看清是还是不是一致对象(内存地址是还是不是等于)
  3. 要是或不是同一个class那一定不是同等对象
  4. 判断目的的各属性值是不是等于

Person *person1 = [Person personWithName:@"Ryan Jin" birthday:self.date];
Person *person2 = [Person personWithName:@"Ryan Jin" birthday:self.date];

现行一经判断[person1 isEqual person2]就是回来true了,然而怎么感觉缺了点什么,
hash好像还没用到呀,难道不要求重写hash方法呢?

答案当然是急需,当成员被投入到NSHashTable(也囊括NSSet)中时,会被分配一个hash值,以标识该成员在联谊中的位置,通过这些职位标识可以极大的升迁成员查找的效用(这也是为啥NSHashTable
查找元素的进程会快于NSArray).

出于NSHashTable/NSSet在添新币素的时候会就行判等操作,当某个元素已经存在时不会重新添加,这几个判等的操作包蕴两步:

  1. 七个成员的hash值是还是不是等于,如不相等则立时判断为分歧因素
  2. 若hash值相等,则再判断isEqual是不是重返一致

唯有八个因素的hashisEqual都为同一的气象下才看清为同一对象。好了,通晓了那个规格,我们来重写下Person的hash方法:

- (NSUInteger)hash {
    return [self.name hash] ^ [self.birthday hash]; // best practice
}

是因为系统的NSString和NSDate在内容相同的事态下会回来相同的hash值,所以那边的超级实践是回去各属性的位或运算。那边必要专注的是无法大致的回到[super hash],
因为默许的hash值为该目的的内存地址,所以地点的person1person2它们的[super hash]是区其他,所以会被判定为分歧的要素,而大家想要已毕的是当Person的各属性一致的时候它们即为同一元素。

NSHashTable/NSSet在添比索素和判断某个元素是还是不是存在(member:/containsObject:)时会调用hash方法,
其它NSDictionary在检索key时(key为非字符串对象),
也会使用hash值来抓好查找效用

咱俩来看下FBKVO里面用到NSHashTable地点:

NSHashTable *infos = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality capacity:0];

那边初叶化了一个NSHashTable,
存放类型为NSPointerFunctionsWeakMemory即弱持有成员元素且当元素释放后活动从NSHashTable移除。其余,判等类型为NSPointerFunctionsObjectPointerPersonality即直接利用指针地址是或不是等于来判断。假若类型设置为NSPointerFunctionsObjectPersonality则选拔地点所描述hash和isEqual来判断。

FBKVO这边使用直接指针地址进行元素比较的案由是单例_FBKVOSharedControllerinfos内部所存放的_FBKVOInfo都是从FBKVOController流传过来的,已经因而了判等操作,不会冒出同样的对象,所以_infos处理这么些_FBKVOInfo要素直接用指针相比较就好了,没须要再去调用hashisEqual方法。另外NSPointerFunctionsWeakMemory安装是为着在_FBKVOInfo出狱后自行从_infos内部移除它,
_FBKVOInfo都不设有了,放在中间也没意义了。从那边可以看出FBKVO设计的真的很密切。

NSMapTable能够知晓为更常见意义上的NSMutableDictionary,
其各项特征和NSHashTable基本相同:

  • NSMapTable是可变的, 没有不可变版本
  • 可以弱引用持有keys和values, 当key或value释放后存储的实业会被移除
  • NSMapTable可以在加上value的时候对value举办复制

NSMapTable *keyToObjectMapping = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn
                                                       valueOptions:NSMapTableStrongMemory];

只要按上边这么设置NSMapTable会和NSMutableDictionary用起来完全一样: 复制
key,
并对它的object引用计数加一。同样,我们也来看下FBKVO使用NSMapTable的地点:

- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
  self = [super init];
  if (nil != self) {
    _observer = observer;
    NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
    _objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
  }
  return self;
}

那边定义了一个_objectInfosMap: key为被寓目的靶子,
value则为寄放着_FBKVOInfo的NSMutableSet,
那边在伊始化的时候增添了retainObserved变量用来标记是还是不是将key强引用,具体调用示例如下:

[self.KVOController observe:self.photos                        
                    keyPath:@"count"
                    options:NSKeyValueObservingOptionNew
                      block:^(id observer, id object, NSDictionary *change) {
    // observer -> RJViewController -> __weak self
    // object   -> self.photos    
    // change   -> NSKeyValueChangeKey + FBKVONotificationKeyPathKey    
}];

默许景况下对key(被考察的目标)也就是那边的self.photos做强引用,不过如若大家observe的对象为self本身,那么是不可能做强引用持有的,否则就循环引用了。所以大家在这几个情形下须要retainObserved传入NO,
那也是为啥NSObject+FBKVOController会有一个KVOController和KVOControllerNonRetaining了。

至于_objectInfoMap的value,
因为是NSMutableSet所以直接使用默许的强引用持有,那边大家或许有难题,为何value值又接纳了NSMutableSet而不用NSHashTable呢?原因是此处并不需求弱引用持有各类_FBKVOInfo目标,而且一大半情景下行使NSMutableSet越发有利于,比如NSHashTable就从不enumerateObjectsUsingBlock的枚举方法。而NSSet相相比NSArray的界别是前者无序存放,且使用hash查找元素功用快,但是后者比前者添英镑素的快慢快很多,所以在甄选使用哪个容器的时候须要基于具体意况来摘取。

FBKVO对_FBKVOInfo的hash和isEqual举办了重写,以keyPath来开展判等。所以那边对于value设置为NSPointerFunctionsObjectPersonality以hash/isEqual进行判定,而key值(被观望者)则设置为NSPointerFunctionsObjectPointerPersonality平素以指针地址做判断。

终极我们直接引用Mattt大神对于如几时候用NSMapTable何时用NSDictionary的印证来收尾这一小节:

As always, it’s important to remember that programming is not about
being clever: always approach a problem from the highest viable level
of abstraction. NSSet and NSDictionary are great classes. For 99% of
problems, they are undoubtedly the correct tool for the job. If,
however, your problem has any of the particular memory management
constraints described above, then NSHashTable & NSMapTable may be
worth a look.

紧接着看一下- (void)_observe:(id)object info:(_FBKVOInfo
*)info那一个办法。先开一个锁来检查被考察的object的NSMutableSet是或不是存在,若是存在,则赶回,解锁。假如不设有,为那么些object添加一个NSMutableSet,接着NSMutableSet添加所传颂的info,解锁。最终就是[[_FBKVOSharedController
sharedController] observe:object info:info]。

作品权归作者所有。商业转发请联系小编得到授权,非商业转发请评释出处。

來源:简书

observe:info:.png

FBKVOController.m

图片 3

observe.png

NSObject+FBKVOController.h

然后看开端化函数,对传播的observer举办weak持有,依照retainObserved来安装NSPointerFunctionOptions的值,开首化互斥锁。

图片 4

NSObject+FBKVOController.m是一个Category。通过AssociateObject给NSObject提供一个Retain和nonRetain的KVOController。

那随着来看下_FBKVOSharedController这个类。_FBKVOSharedController那个类类似于一个中间件的注册表。所有的洞察新闻都由_FBKVOSharedController这几个类注册,然后转向注册观察,并且更新info的state。

FBKVOController.h

FBKVOConroller是脸书开源的代表KVO的化解方案。它用block解决了原先使用KVO时代码散乱的缺点。

最后看一下observeValueForKeyPath:ofObject:change:context这一个法子。首先根据context上下文获取info新闻。接着判断info的controller和observer是还是不是留存(以前是weak持有)。依据info的block或者action活着override进行转发。

observeValueForKeyPath:ofObject:change:context.png

_FBKVOInfo

pthread_mutex_t是一个互斥锁,用来保管八线程安全。

图片 5

先来看一下FBKVOController那个类。首先该对象有2个特性,NSMapTable和pthread_mutex_t。

FBKVOController的要旨代码一共4个类:

链接:http://www.jianshu.com/p/1bf111d7d2de

NSObject+FBKVOController.m

NSDictionary提供了key-to-object的照耀。NSDictionary的object的职分是由key的hash值来索引的。由于目的存储在特定岗位,因而需求key的值不可以更改。因而,NSDictionary始终复制key到它的个人地方。那一个key的复制行为也是NSDictionary怎么着工作的功底,但那也有一个限量:你能够只行使Objective-C对象作为NSDictionary的key,如若它协助NSCopying商事。其余,key应该是小且高效的,以至于复制的时候不会对CPU和内存造成负担。而NSMapTable是一种一般意义上的照耀。不仅帮衬key-to-object,同时帮忙object-to-object映射。

相关文章