就問此時此刻還有誰?45度仰望天空,該死!我這無處安放的魅力!git
- RxSwift(1)— 初探
- RxSwift(2)— 核心邏輯源碼分析
- RxSwift(3)— Observable序列的建立方式
- RxSwift(4)— 高階函數(上)
- RxSwift(5)— 高階函數(下)
- RxSwift(6)— 調度者-scheduler源碼解析(上)
- RxSwift(7)— 調度者-scheduler源碼解析(下)
- RxSwift(8)— KVO底層探索(上)
- RxSwift(9)— KVO底層探索(下)
- RxSwift(10)— 場景序列總結
- RxSwift(11)— dispose源碼解析
- RxSwift(12)— Subject即攻也守
- RxSwift(13)— 爬過的坑
- RxSwift(14)— MVVM雙向綁定
RxSwift 目錄直通車 --- 和諧學習,不急不躁!github
KVO
在咱們實際開發之中運用很是之多,不少開發者都知道原理!可是這些原理是如何來的,通常都是淺嘗輒止。這個篇章我會從Swift
入手分析,探索KVO
底層源碼.但願讓讀者真正掌握這一塊底層,知其然而知其因此然!面試
首先咱們從KVO
的三部曲開始swift
// 1: 添加觀察
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
// 2: 觀察響應回調
override func observeValue(forKeyPath keyPath:, of object:, change: , context:){} // 3: 移除觀察 person.removeObserver(self, forKeyPath: "name")
複製代碼
其實咱們也知道,就是平時在開發的時候,咱們也能夠經過計算型屬性也能夠直接觀察app
var name: String = ""{
willSet{
print(newValue)
}
didSet{
print(oldValue)
}
}
複製代碼
問題來了:這二者有什麼關係?ide
下面咱們開始分析,首先感謝蘋果開源精神,在Github能夠直接下載,咱們經過 Swift
源碼展開分析函數
public func willChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>) {
(self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath))
}
public func willChange<Value>(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath<Self, Value>) {
(self as! NSObject).willChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath))
}
public func willChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void {
(self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}
public func didChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>) {
(self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath))
}
public func didChange<Value>(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath<Self, Value>) {
(self as! NSObject).didChange(changeKind, valuesAt: indexes, forKey: _bridgeKeyPathToString(keyPath))
}
public func didChangeValue<Value>(for keyPath: __owned KeyPath<Self, Value>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void {
(self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}
複製代碼
willChangeValue
和 didChangeValue
做爲數據改變的兩個重要的方法class Target : NSObject, NSKeyValueObservingCustomization {
// This dynamic property is observed by KVO
@objc dynamic var objcValue: String
@objc dynamic var objcValue2: String {
willSet {
willChangeValue(for: \.objcValue2)
}
didSet {
didChangeValue(for: \.objcValue2)
}
}
}
複製代碼
NSObject
NSKeyValueObservingCustomization
的協議public protocol NSKeyValueObservingCustomization : NSObjectProtocol {
static func keyPathsAffectingValue(for key: AnyKeyPath) -> Set<AnyKeyPath>
static func automaticallyNotifiesObservers(for key: AnyKeyPath) -> Bool
}
複製代碼
keyPathsAffectingValue
可以創建 keyPath
的依賴,例如兩個屬性的變化同時影響一個重要屬性的改變:進度 = 下載量 / 總量
automaticallyNotifiesObservers
自動開關willSet
裏面就調用willChangeValue
,didSet
調用didChangeValue
,的確咱們計算型屬性是和咱們KVO相關方法是有所關聯,這裏也直接證實!KVO底層
!這裏說明一下,本篇章的貼出的源碼沒有給你們省略,目的是想讓你們認真閱讀,本身對照學習。固然可能中間我也忽略過一些細節,源碼直接貼出來方便本身理解源碼分析
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
複製代碼
如今咱們開始探索底層源碼:post
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
GSKVOInfo *info;
GSKVOReplacement *r;
NSKeyValueObservationForwarder *forwarder;
NSRange dot;
setup();
[kvoLock lock];
// Use the original class
r = replacementForClass([self class]); /* * Get the existing observation information, creating it (and changing * the receiver to start key-value-observing by switching its class) * if necessary. */ info = (GSKVOInfo*)[self observationInfo]; if (info == nil) {
info = [[GSKVOInfo alloc] initWithInstance: self];
[self setObservationInfo: info];
object_setClass(self, [r replacement]);
}
/* * Now add the observer. */
dot = [aPath rangeOfString:@"."];
if (dot.location != NSNotFound)
{
forwarder = [[NSKeyValueObservationForwarder alloc]
initWithKeyPath: aPath
ofObject: self
withTarget: anObserver
context: aContext];
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: forwarder];
}
else
{
[r overrideSetterFor: aPath];
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: aContext];
}
[kvoLock unlock];
}
複製代碼
中間replacementForClass
作了一些處理:學習
class、set、dealloc
方法isa
與動態isa
切換由原來的觀察者進行遷移到 GSKVOInfo
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
GSKVOPathInfo *pathInfo;
GSKVOObservation *observation;
unsigned count;
if ([anObserver respondsToSelector:
@selector(observeValueForKeyPath:ofObject:change:context:)] == NO)
{
return;
}
[iLock lock];
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
if (pathInfo == nil)
{
pathInfo = [GSKVOPathInfo new];
// use immutable object for map key
aPath = [aPath copy];
NSMapInsert(paths, (void*)aPath, (void*)pathInfo);
[pathInfo release];
[aPath release];
}
observation = nil;
pathInfo->allOptions = 0;
count = [pathInfo->observations count];
while (count-- > 0)
{
GSKVOObservation *o;
o = [pathInfo->observations objectAtIndex: count];
if (o->observer == anObserver)
{
o->context = aContext;
o->options = options;
observation = o;
}
pathInfo->allOptions |= o->options;
}
if (observation == nil)
{
observation = [GSKVOObservation new];
GSAssignZeroingWeakPointer((void**)&observation->observer,
(void*)anObserver);
observation->context = aContext;
observation->options = options;
[pathInfo->observations addObject: observation];
[observation release];
pathInfo->allOptions |= options;
}
if (options & NSKeyValueObservingOptionInitial)
{
/* If the NSKeyValueObservingOptionInitial option is set, * we must send an immediate notification containing the * existing value in the NSKeyValueChangeNewKey */
[pathInfo->change setObject: [NSNumber numberWithInt: 1]
forKey: NSKeyValueChangeKindKey];
if (options & NSKeyValueObservingOptionNew)
{
id value;
value = [instance valueForKeyPath: aPath];
if (value == nil)
{
value = null;
}
[pathInfo->change setObject: value
forKey: NSKeyValueChangeNewKey];
}
[anObserver observeValueForKeyPath: aPath
ofObject: instance
change: pathInfo->change
context: aContext];
}
[iLock unlock];
}
複製代碼
observeValueForKeyPath:ofObject:change:context:
方法。常規操做,沒有回調,響應就沒有什麼意義了!pathInfo
來保存KVO信息
context
&options
的處理數據NSKeyValueObservingOptionInitial
就會主動發起一次KVO
響應:observeValueForKeyPath
- (void) willChangeValueForKey: (NSString*)aKey
{
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info == nil)
{
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion++ == 0)
{
id old = [pathInfo->change objectForKey: NSKeyValueChangeNewKey];
if (old != nil)
{
/* We have set a value for this key already, so the value * we set must now be the old value and we don't need to * refetch it. */
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
[pathInfo->change removeObjectForKey: NSKeyValueChangeNewKey];
}
else if (pathInfo->allOptions & NSKeyValueObservingOptionOld)
{
/* We don't have an old value set, so we must fetch the * existing value because at least one observation wants it. */
old = [self valueForKey: aKey];
if (old == nil)
{
old = null;
}
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
}
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeSetting]
forKey: NSKeyValueChangeKindKey];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
[self willChangeValueForDependentsOfKey: aKey];
}
複製代碼
pathInfo
獲取回以前的舊值pathInfo->change
裏面的數據處理[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
開始發起響應通知- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f
{
unsigned count;
id oldValue;
id newValue;
if (f == YES)
{
if ((allOptions & NSKeyValueObservingOptionPrior) == 0)
{
return; // Nothing to do.
}
[change setObject: [NSNumber numberWithBool: YES]
forKey: NSKeyValueChangeNotificationIsPriorKey];
}
else
{
[change removeObjectForKey: NSKeyValueChangeNotificationIsPriorKey];
}
oldValue = [[change objectForKey: NSKeyValueChangeOldKey] retain];
if (oldValue == nil)
{
oldValue = null;
}
newValue = [[change objectForKey: NSKeyValueChangeNewKey] retain];
if (newValue == nil)
{
newValue = null;
}
/* Retain self so that we won't be deallocated during the * notification process. */
[self retain];
count = [observations count];
while (count-- > 0)
{
GSKVOObservation *o = [observations objectAtIndex: count];
if (f == YES)
{
if ((o->options & NSKeyValueObservingOptionPrior) == 0)
{
continue;
}
}
else
{
if (o->options & NSKeyValueObservingOptionNew)
{
[change setObject: newValue
forKey: NSKeyValueChangeNewKey];
}
}
if (o->options & NSKeyValueObservingOptionOld)
{
[change setObject: oldValue
forKey: NSKeyValueChangeOldKey];
}
[o->observer observeValueForKeyPath: aKey
ofObject: instance
change: change
context: o->context];
}
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
[oldValue release];
[change setObject: newValue forKey: NSKeyValueChangeNewKey];
[newValue release];
[self release];
}
複製代碼
[o->observer observeValueForKeyPath: aKey ofObject: instance change: change context: o->context];
移除觀察的流程相對來講,比較簡單了,可是優秀的我仍是願意和你們一塊兒探索
- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
{
GSKVOInfo *info;
id forwarder;
/* * Get the observation information and remove this observation. */
info = (GSKVOInfo*)[self observationInfo];
forwarder = [info contextForObserver: anObserver ofKeyPath: aPath];
[info removeObserver: anObserver forKeyPath: aPath];
if ([info isUnobserved] == YES)
{
/* * The instance is no longer being observed ... so we can * turn off key-value-observing for it. */
object_setClass(self, [self class]); IF_NO_GC(AUTORELEASE(info);) [self setObservationInfo: nil]; } if ([aPath rangeOfString:@"."].location != NSNotFound) [forwarder finalize]; } 複製代碼
observationInfo
就是咱們信息收集者NSMapRemove(paths, (void*)aPath)
移除isa
和原類的isa
切換回來info
置空OK 完美解析了KVO底層源碼!咱們在探索完KVO底層實現才能說是真正的掌握了,而不是經過面試寶典背下結論,那是沒有什麼意義! 在真正的高手對決間一眼就能看出,中間忽略了一些小細節,好比set的多種狀況,
setNumber
類型,setInt
類型,setLong
類型....我相信聰明的你同樣能夠解析讀懂!就問此時此刻還有誰?45度仰望天空,該死!我這無處安放的魅力!