项目里面经常看到有小伙伴使用ReactiveObjC,确实很方便,但是大家往往不清楚背后的原理,今天我们来学习下这背后的原理,我们从项目里一个简单的Api开始看起来:
[RACObserve(self, username) subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}];
我们简历理解下这段代码:监听self的username属性变化。
我们想到了什么?KVO对吗?还记得KVO复杂的用法吗?感受到这个的好了吧?
那我们就来看看这个如何实现的呢?
我们先来看下
RACObserve(self, username)
#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)
#define _RACObserve(TARGET, KEYPATH) \
({ \
__weak id target_ = (TARGET); \
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
})
接下来我们是不是弄懂这个宏就行了
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
我们到具体的类里面去看。
/// Creates a signal to observe the value at the given key path.
///
/// The initial value is sent on subscription, the subsequent values are sent
/// from whichever thread the change occured on, even if it doesn't have a valid
/// scheduler.
///
/// Returns a signal that immediately sends the receiver's current value at the
/// given keypath, then any changes thereafter.
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer {
return [[[self
rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
map:^(RACTuple *value) {
// -map: because it doesn't require the block trampoline that -reduceEach: uses
return value[0];
}]
setNameWithFormat:@"RACObserve(%@, %@)", RACDescription(self), keyPath];
}r;
接下来我们来看
/// Creates a signal to observe the changes of the given key path.
///
/// The initial value is sent on subscription if `NSKeyValueObservingOptionInitial` is set.
/// The subsequent values are sent from whichever thread the change occured on,
/// even if it doesn't have a valid scheduler.
///
/// Returns a signal that sends tuples containing the current value at the key
/// path and the change dictionary for each KVO callback.
- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver {
NSObject *strongObserver = weakObserver;
keyPath = [keyPath copy]; //预防外面改变
//递归锁
NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
objectLock.name = @"org.reactivecocoa.ReactiveObjC.NSObjectRACPropertySubscribing";
__weak NSObject *weakSelf = self;
//生成一个deallocSignal,zip打包俩个dealloc的信号伟一个,确保这个变量为的释放。主要目的就是变量释放的时候解锁递归所。后面我们再来看看zip是如何实现的
RACSignal *deallocSignal = [[RACSignal
zip:@[
self.rac_willDeallocSignal,
strongObserver.rac_willDeallocSignal ?: [RACSignal never]
]]
doCompleted:^{
// Forces deallocation to wait if the object variables are currently
// being read on another thread.
[objectLock lock];
@onExit {
[objectLock unlock];
};
}];
//生成一个subscriber
return [[[RACSignal
createSignal:^ RACDisposable * (id subscriber) {
// Hold onto the lock the whole time we're setting up the KVO
// observation, because any resurrection that might be caused by our
// retaining below must be balanced out by the time -dealloc returns
// (if another thread is waiting on the lock above).
[objectLock lock];
@onExit {
[objectLock unlock];
};
//表明存储在某些局部变量中的值在优化时不应该被编译器强制释放,翻译官方:局部变量标记为id类型或者是指向ObjC对象类型的指针,以便存储在这些局部变量中的值在优化时不会被编译器强制释放。相反,这些值会在变量再次被赋值之前或者局部变量的作用域结束之前都会被保存。其中 `objc_precise_lifetime` 可在 [LLVM ARC](https://link.jianshu.com?t=https://clang.llvm.org/docs/AutomaticReferenceCounting.html) 可查
__strong NSObject *observer __attribute__((objc_precise_lifetime)) = weakObserver;
__strong NSObject *self __attribute__((objc_precise_lifetime)) = weakSelf;
if (self == nil) {
[subscriber sendCompleted];
return nil;
}
// 创建一个观察者,通过subscriber发送变化。到这里我们是不是可以这么理解了:
通过外面传过来的key observer监听。然后通过subscriber去发布消息。
return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
[subscriber sendNext:RACTuplePack(value, change)];
}];
}]
takeUntil:deallocSignal]
setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", RACDescription(self), keyPath, (unsigned long)options, RACDescription(strongObserver)];
}
接下来我们来看看是如何绑定keypath 和observer的
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver block:(void (^)(id, NSDictionary *, BOOL, BOOL))block {
NSCParameterAssert(block != nil);
NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0);
keyPath = [keyPath copy];
NSObject *strongObserver = weakObserver;
NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
NSString *keyPathHead = keyPathComponents[0];
NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
// The disposable that groups all disposal necessary to clean up the callbacks
// added to the value of the first key path component.
RACSerialDisposable *firstComponentSerialDisposable = [RACSerialDisposable serialDisposableWithDisposable:[RACCompoundDisposable compoundDisposable]];
RACCompoundDisposable * (^firstComponentDisposable)(void) = ^{
return (RACCompoundDisposable *)firstComponentSerialDisposable.disposable;
};
[disposable addDisposable:firstComponentSerialDisposable];
BOOL shouldAddDeallocObserver = NO;
objc_property_t property = class_getProperty(object_getClass(self), keyPathHead.UTF8String);
if (property != NULL) {
rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
if (attributes != NULL) {
@onExit {
free(attributes);
};
BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type;
BOOL isProtocol = attributes->objectClass == NSClassFromString(@"Protocol");
BOOL isBlock = strcmp(attributes->type, @encode(void(^)(void))) == 0;
BOOL isWeak = attributes->weak;
// If this property isn't actually an object (or is a Class object),
// no point in observing the deallocation of the wrapper returned by
// KVC.
//
// If this property is an object, but not declared `weak`, we
// don't need to watch for it spontaneously being set to nil.
//
// Attempting to observe non-weak properties will result in
// broken behavior for dynamic getters, so don't even try.
shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol;
}
}
// Adds the callback block to the value's deallocation. Also adds the logic to
// clean up the callback to the firstComponentDisposable.
void (^addDeallocObserverToPropertyValue)(NSObject *) = ^(NSObject *value) {
if (!shouldAddDeallocObserver) return;
// If a key path value is the observer, commonly when a key path begins
// with "self", we prevent deallocation triggered callbacks for any such key
// path components. Thus, the observer's deallocation is not considered a
// change to the key path.
if (value == weakObserver) return;
NSDictionary *change = @{
NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
NSKeyValueChangeNewKey: NSNull.null,
};
RACCompoundDisposable *valueDisposable = value.rac_deallocDisposable;
RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
block(nil, change, YES, keyPathHasOneComponent);
}];
[valueDisposable addDisposable:deallocDisposable];
[firstComponentDisposable() addDisposable:[RACDisposable disposableWithBlock:^{
[valueDisposable removeDisposable:deallocDisposable];
}]];
};
// Adds the callback block to the remaining path components on the value. Also
// adds the logic to clean up the callbacks to the firstComponentDisposable.
void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:weakObserver block:block];
[firstComponentDisposable() addDisposable:observerDisposable];
};
// Observe only the first key path component, when the value changes clean up
// the callbacks on the old value, add callbacks to the new value and call the
// callback block as needed.
//
// Note this does not use NSKeyValueObservingOptionInitial so this only
// handles changes to the value, callbacks to the initial value must be added
// separately.
NSKeyValueObservingOptions trampolineOptions = (options | NSKeyValueObservingOptionPrior) & ~NSKeyValueObservingOptionInitial;
RACKVOTrampoline *trampoline = [[RACKVOTrampoline alloc] initWithTarget:self observer:strongObserver keyPath:keyPathHead options:trampolineOptions block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) {
// If this is a prior notification, clean up all the callbacks added to the
// previous value and call the callback block. Everything else is deferred
// until after we get the notification after the change.
if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
[firstComponentDisposable() dispose];
if ((options & NSKeyValueObservingOptionPrior) != 0) {
block([trampolineTarget valueForKeyPath:keyPath], change, NO, keyPathHasOneComponent);
}
return;
}
// From here the notification is not prior.
NSObject *value = [trampolineTarget valueForKey:keyPathHead];
// If the value has changed but is nil, there is no need to add callbacks to
// it, just call the callback block.
if (value == nil) {
block(nil, change, NO, keyPathHasOneComponent);
return;
}
// From here the notification is not prior and the value is not nil.
// Create a new firstComponentDisposable while getting rid of the old one at
// the same time, in case this is being called concurrently.
RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]];
[oldFirstComponentDisposable dispose];
addDeallocObserverToPropertyValue(value);
// If there are no further key path components, there is no need to add the
// other callbacks, just call the callback block with the value itself.
if (keyPathHasOneComponent) {
block(value, change, NO, keyPathHasOneComponent);
return;
}
// The value has changed, is not nil, and there are more key path components
// to consider. Add the callbacks to the value for the remaining key path
// components and call the callback block with the current value of the full
// key path.
addObserverToValue(value);
block([value valueForKeyPath:keyPathTail], change, NO, keyPathHasOneComponent);
}];
// Stop the KVO observation when this one is disposed of.
[disposable addDisposable:trampoline];
// Add the callbacks to the initial value if needed.
NSObject *value = [self valueForKey:keyPathHead];
if (value != nil) {
addDeallocObserverToPropertyValue(value);
if (!keyPathHasOneComponent) {
addObserverToValue(value);
}
}
// Call the block with the initial value if needed.
if ((options & NSKeyValueObservingOptionInitial) != 0) {
id initialValue = [self valueForKeyPath:keyPath];
NSDictionary *initialChange = @{
NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
NSKeyValueChangeNewKey: initialValue ?: NSNull.null,
};
block(initialValue, initialChange, NO, keyPathHasOneComponent);
}
RACCompoundDisposable *observerDisposable = strongObserver.rac_deallocDisposable;
RACCompoundDisposable *selfDisposable = self.rac_deallocDisposable;
// Dispose of this observation if the receiver or the observer deallocate.
[observerDisposable addDisposable:disposable];
[selfDisposable addDisposable:disposable];
return [RACDisposable disposableWithBlock:^{
[disposable dispose];
[observerDisposable removeDisposable:disposable];
[selfDisposable removeDisposable:disposable];
}];
}
看到这个类RACKVOTrampoline了吗
这个类是用来绑定KVO的,让我们进到这个类里面来看一段熟悉的代码吧
[strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self];
这不就是KVO吗, 哈哈。
接下来我们来看下订阅
subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init];
subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
return subscriber;
}
- (void)sendNext:(id)value {
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
nextBlock(value);
}
}
这个过程是不是很简单;
注册好观察者->绑定nextblock-> KVO触发绑定的block。
其他的API应该都是差不多的逻辑。 接下来我们来分别学习这个框架的每个类。剖析作者写这个类的目的,作用。