-
Notifications
You must be signed in to change notification settings - Fork 450
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
119 changed files
with
7,925 additions
and
478 deletions.
There are no files selected for viewing
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// BQMVVM.h | ||
// MVVMFramework | ||
// | ||
// Created by yuantao on 16/1/21. | ||
// Copyright © 2016年 momo. All rights reserved. | ||
// | ||
|
||
#ifndef BQMVVM_h | ||
#define BQMVVM_h | ||
|
||
#import "MVVMBaseViewModel.h" | ||
#import "MVVMBaseViewManger.h" | ||
#import "MVVMTableDataDelegate.h" | ||
#import "MVVMCollectionDataDelegate.H" | ||
#import "MVVMExtend.h" | ||
#import "MVVMConstant.h" | ||
#import "MVVMSingleton.h" | ||
#import "SUIUtils.h" | ||
#import "UITableView+FDTemplateLayoutCell.h" | ||
#import "PMKVObserver.h" | ||
|
||
#endif /* BQMVVM_h */ |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
71 changes: 71 additions & 0 deletions
71
SUIMVVMDemo/SUIMVVMDemo/SUIMVVMKit/Vender/PMKVObserver/KVObserver.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// | ||
// KVObserver.h | ||
// PMKVObserver | ||
// | ||
// Created by Kevin Ballard on 11/18/15. | ||
// Copyright © 2015 Postmates. All rights reserved. | ||
// | ||
// Licensed under the MIT license <LICENSE or | ||
// http://opensource.org/licenses/MIT>. This file may not be copied, modified, | ||
// or distributed except according to those terms. | ||
// | ||
|
||
@import Foundation; | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
/// A class that manages a single KVO observation. | ||
/// | ||
/// This class does not retain the observed object, and if the observed object deallocates, | ||
/// this class automatically unregisters the KVO. | ||
/// An observing object may also be provided. If it is, it is also not retained, and if the | ||
/// observering object deallocates, this class automatically unregisters the KVO. | ||
/// | ||
/// In Swift, the initializers and callbacks provide a strongly-typed access to the observed object. | ||
/// In Objective-C, the initializers and callbacks use <tt>id</tt>. | ||
/// | ||
/// This class is thread-safe and observation and deregistration may occur on any thread. | ||
/// Deregistration may also occur within the initial callback when using | ||
/// <tt>NSKeyValueObservingOptions.Initial</tt>. It's safe for the callback block to retain | ||
/// the <tt>KVObserver</tt>, but because it's awkward to set that up, the callback is provided | ||
/// with the \c KVObserver object as well. However, the callback should not retain \c object or | ||
/// \c observer (if provided). | ||
/// | ||
/// If the observing object does not want to unregister the KVO (until either it or the observed | ||
/// object deallocates), it may freely discard this class instance. It only needs to hold onto | ||
/// this class instance if it wants to unregister manually. | ||
/// | ||
/// @note If observing a key path with multiple components, and one of those components is a | ||
/// \c weak reference, if the referenced object deallocates without sending any KVO events for | ||
/// the property, then this will likely result in an exception. This is due to how KVO works and | ||
/// cannot be handled by <tt>KVObserver</tt>. | ||
/// | ||
/// @note Any observed object must support weak references, and when using the optional observing | ||
/// object support, it also must support weak references. | ||
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) | ||
__attribute__((objc_subclassing_restricted)) | ||
#endif | ||
@interface PMKVObserver : NSObject | ||
/// Establishes a KVO relationship to <tt>object</tt>. The KVO will be active until \c object deallocates or | ||
/// until the \c cancel() method is invoked. | ||
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo))block NS_SWIFT_UNAVAILABLE("use init(object:keyPath:options:block:)"); | ||
|
||
/// Establishes a KVO relationship to <tt>object</tt>. The KVO will be active until either \c object or | ||
/// \c observer deallocates or until the \c cancel() method is invoked. | ||
+ (instancetype)observeObject:(id)object observer:(id)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(id observer, id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo))block NS_SWIFT_UNAVAILABLE("use init(observer:object:keyPath:options:block:)"); | ||
|
||
/// Establishes a KVO relationship to <tt>object</tt>. The KVO will be active until \c object deallocates or | ||
/// until the \c cancel() method is invoked. | ||
- (instancetype)initWithObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo))block NS_DESIGNATED_INITIALIZER NS_REFINED_FOR_SWIFT; | ||
|
||
/// Establishes a KVO relationship to <tt>object</tt>. The KVO will be active until either \c object or | ||
/// \c observer deallocates or until the \c cancel() method is invoked. | ||
- (instancetype)initWithObserver:(id)observer object:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(id observer, id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo))block NS_DESIGNATED_INITIALIZER NS_REFINED_FOR_SWIFT; | ||
|
||
- (instancetype)init NS_UNAVAILABLE; | ||
|
||
/// Unregisters the KVO. This can be called multiple times and can be called from any thread. | ||
- (void)cancel; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
264 changes: 264 additions & 0 deletions
264
SUIMVVMDemo/SUIMVVMDemo/SUIMVVMKit/Vender/PMKVObserver/KVObserver.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
// | ||
// PMKVObserver.m | ||
// PMKVObserver | ||
// | ||
// Created by Kevin Ballard on 11/18/15. | ||
// Copyright © 2015 Postmates. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
// | ||
|
||
#import "KVObserver.h" | ||
#import <stdatomic.h> | ||
#import <objc/runtime.h> | ||
#import <pthread.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
static void *kContext = &kContext; | ||
|
||
@interface PMKVObserverDeallocSpy: NSObject | ||
- (instancetype)initWithObserver:(PMKVObserver *)observer shouldBlock:(BOOL)flag NS_DESIGNATED_INITIALIZER; | ||
- (instancetype)init NS_UNAVAILABLE; | ||
@end | ||
|
||
typedef NS_ENUM(uint_fast8_t, PMKVObserverState) { | ||
/// KVO has finished being setup and can now be cancelled | ||
PMKVObserverStateSetup = 1 << 0, | ||
/// -cancel has not yet been invoked | ||
PMKVObserverStateActive = 1 << 1, | ||
PMKVObserverStateCancellable = (PMKVObserverStateSetup | PMKVObserverStateActive), | ||
|
||
/// KVO has been successfully deregistered | ||
/// This lets us skip the semaphore for subsequent cancels | ||
PMKVObserverStateDeregistered = 1 << 2 | ||
}; | ||
|
||
typedef void (^Callback)(id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo); | ||
typedef void (^ObserverCallback)(id observer, id object, NSDictionary<NSString *,id> * _Nullable change, PMKVObserver *kvo); | ||
|
||
@implementation PMKVObserver { | ||
__weak id _Nullable _object; | ||
// if we cancel because the object started dealloc, our __weak ivar will be nil already, so we need a non-weak version | ||
// NB: _unsafeObject is ONLY accessed after init in -teardown and it's protected by the semaphore | ||
__unsafe_unretained id _Nullable _unsafeObject; | ||
__weak id _Nullable _observer; | ||
NSString *_keyPath; | ||
atomic_uint_fast8_t _state; | ||
BOOL _hasObserver; | ||
|
||
// use an activity count for _callback. We need to nil it out once cancelled, but since it's reference-counted we | ||
// can't just use an atomic pointer. We can't use a spinlock either because that's unsafe on iOS, so instead we | ||
// keep our own reference count of when it's being used (either by the callback or -teardown) and nil it out once | ||
// the count hits zero. 16 bits ought to be enough. | ||
atomic_uint_fast16_t _activityCount; | ||
id _Nullable _callback; | ||
|
||
// we need to be able to block in -teardown until KVO is deregistered (both to preserve the guarantee of -cancel | ||
// and to ensure the KVO is deregistered before the object deallocates). Can't use a spinlock because it's unsafe | ||
// on iOS, so we settle for a mutex. | ||
pthread_mutex_t _mutex; | ||
} | ||
|
||
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(Callback)block { | ||
return [[self alloc] initWithObject:object keyPath:keyPath options:options block:block]; | ||
} | ||
|
||
+ (instancetype)observeObject:(id)object observer:(id)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(ObserverCallback)block { | ||
return [[self alloc] initWithObserver:observer object:object keyPath:keyPath options:options block:block]; | ||
} | ||
|
||
- (instancetype)initWithObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(Callback)block { | ||
if ((self = [super init])) { | ||
setup(self, nil, object, keyPath, options, (id)[block copy]); | ||
} | ||
return self; | ||
} | ||
|
||
- (instancetype)initWithObserver:(id)observer object:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(ObserverCallback)block { | ||
if ((self = [super init])) { | ||
setup(self, observer, object, keyPath, options, (id)[block copy]); | ||
} | ||
return self; | ||
} | ||
|
||
static void setup(PMKVObserver *self, id _Nullable NS_VALID_UNTIL_END_OF_SCOPE observer, id NS_VALID_UNTIL_END_OF_SCOPE object, NSString *keyPath, NSKeyValueObservingOptions options, id callback) { | ||
// NS_VALID_UNTIL_END_OF_SCOPE ensures the object/observer stay alive until we've finished the observation process | ||
// We can't have either one of them dealloc before we finish with -addObserver:forKeyPath:options:context: because | ||
// we can't unregister KVO until that method finishes, and we can't let the object dealloc before KVO is unregistered. | ||
|
||
self->_observer = observer; | ||
self->_hasObserver = observer != nil; | ||
|
||
self->_object = object; | ||
self->_unsafeObject = object; | ||
self->_keyPath = [keyPath copy]; | ||
atomic_init(&self->_activityCount, 1); | ||
self->_callback = callback; | ||
int retval; | ||
while ((retval = pthread_mutex_init(&self->_mutex, NULL))) { | ||
NSCAssert(retval == EAGAIN, @"pthread_mutex_init: %s", strerror(retval)); | ||
} | ||
atomic_init(&self->_state, PMKVObserverStateActive); | ||
[self installDeallocSpiesForObject:object observer:observer]; | ||
[object addObserver:self forKeyPath:self->_keyPath options:options context:kContext]; | ||
if ((atomic_fetch_or_explicit(&self->_state, PMKVObserverStateSetup, memory_order_release) & PMKVObserverStateActive) == 0) { | ||
// we cancelled during init, shut it down | ||
[self teardown]; | ||
} | ||
} | ||
|
||
- (instancetype)init { | ||
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"-[PMKVObserver init] is not available" userInfo:nil]; | ||
} | ||
|
||
- (void)dealloc { | ||
int retval = pthread_mutex_destroy(&_mutex); | ||
if (__builtin_expect(retval, 0) != 0) { | ||
NSLog(@"PMKVObserver: pthread_mutex_destroy: %s", strerror(retval)); | ||
} | ||
} | ||
|
||
- (BOOL)isCancelled { | ||
return (atomic_load_explicit(&_state, memory_order_relaxed) & PMKVObserverStateActive) == 0; | ||
} | ||
|
||
- (void)cancel { | ||
[self cancel:NO]; | ||
} | ||
|
||
- (void)cancel:(BOOL)shouldBlock { | ||
// When read load the state, if it says it's Cancellable, this means we can see the Setup flag from init. | ||
// But we need a synchronizes-with edge to guarantee we can also see the KVO state. | ||
// We only need that edge if it's been Setup (and not Deregistered), so defer the fence until then. | ||
uint_fast8_t oldState = atomic_fetch_and_explicit(&_state, ~PMKVObserverStateActive, memory_order_relaxed); | ||
if ((oldState & PMKVObserverStateDeregistered) != 0) { | ||
// if we've already deregistered the KVO there's no reason to block | ||
return; | ||
} | ||
// If we're cancelling in response to the object deallocating, we MUST block. Otherwise, we don't have to block because the | ||
// _state flag ensures the callback cannot be invoked again even though we haven't actually unregistered yet. | ||
if (shouldBlock || (oldState & PMKVObserverStateCancellable) == PMKVObserverStateCancellable) { | ||
atomic_thread_fence(memory_order_acquire); | ||
[self teardown]; | ||
} | ||
} | ||
|
||
- (void)teardown { | ||
int retval = pthread_mutex_lock(&_mutex); | ||
NSAssert(__builtin_expect(retval, 0) == 0, @"pthread_mutex_lock: %s", strerror(retval)); | ||
@try { | ||
if (_unsafeObject == nil) { | ||
// we must have already cleared it in a concurrent teardown | ||
return; | ||
} | ||
[_unsafeObject removeObserver:self forKeyPath:_keyPath context:kContext]; | ||
_unsafeObject = nil; | ||
} | ||
@finally { | ||
retval = pthread_mutex_unlock(&_mutex); | ||
NSAssert(__builtin_expect(retval, 0) == 0, @"pthread_mutex_unlock: %s", strerror(retval)); | ||
} | ||
atomic_fetch_or_explicit(&_state, PMKVObserverStateDeregistered, memory_order_relaxed); | ||
// only one caller can ever make it to this point | ||
// "release" our callback. A simple decrement suffices. | ||
if (atomic_fetch_sub_explicit(&_activityCount, 1, memory_order_relaxed) == 1) { | ||
_callback = nil; | ||
} | ||
[self clearDeallocSpies]; | ||
} | ||
|
||
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *,id> *)change context:(nullable void *)context { | ||
if (context != kContext) { | ||
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; | ||
} | ||
if (keyPath == nil || object == nil) { | ||
// I don't know how this is even possible, but the declaration has them as nullable. Bail out if this happens. | ||
return; | ||
} | ||
if ((atomic_load_explicit(&_state, memory_order_relaxed) & PMKVObserverStateActive) == 0) { | ||
// we must have cancelled on another thread at the same time. Skip the callback. | ||
return; | ||
} | ||
// We need to "retain" our callback, unless it's already at zero. | ||
uint_fast16_t count = atomic_load_explicit(&_activityCount, memory_order_relaxed); | ||
do { | ||
if (count == 0) { | ||
// callback was fully released and should be treated as nil | ||
return; | ||
} | ||
NSAssert(__builtin_expect(count != UINT_FAST8_MAX, 0), @"callback activity count hit UINT_FAST8_MAX"); | ||
} while (!atomic_compare_exchange_weak_explicit(&_activityCount, &count, count+1, memory_order_relaxed, memory_order_relaxed)); | ||
// we've now "retained" it and it's safe to access | ||
id callback = _callback; | ||
// now "release" it | ||
if (atomic_fetch_sub_explicit(&_activityCount, 1, memory_order_relaxed) == 1) { | ||
_callback = nil; | ||
// we were cancelled during the preceding code | ||
return; | ||
} | ||
if (_hasObserver) { | ||
id observer = _observer; | ||
if (!observer) { | ||
// our observer is deallocating. Skip the callback. | ||
return; | ||
} | ||
((ObserverCallback)callback)(observer, object, change, self); | ||
} else { | ||
((Callback)callback)(object, change, self); | ||
} | ||
} | ||
|
||
- (void)installDeallocSpiesForObject:(id)object observer:(nullable id)observer { | ||
PMKVObserverDeallocSpy *objectSpy = [[PMKVObserverDeallocSpy alloc] initWithObserver:self shouldBlock:YES]; | ||
void * const key = [self deallocSpyAssociatedObjectKey]; | ||
objc_setAssociatedObject(object, key, objectSpy, OBJC_ASSOCIATION_RETAIN); | ||
if (observer && observer != object) { | ||
objectSpy = [[PMKVObserverDeallocSpy alloc] initWithObserver:self shouldBlock:NO]; | ||
objc_setAssociatedObject(observer, key, objectSpy, OBJC_ASSOCIATION_RETAIN); | ||
} | ||
} | ||
|
||
- (void)clearDeallocSpies { | ||
id object = _object; | ||
void * const key = [self deallocSpyAssociatedObjectKey]; | ||
if (object) { | ||
objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN); | ||
} | ||
id observer = _observer; | ||
if (observer) { | ||
objc_setAssociatedObject(observer, key, nil, OBJC_ASSOCIATION_RETAIN); | ||
} | ||
} | ||
|
||
- (void *)deallocSpyAssociatedObjectKey { | ||
// We could return `self`, but that runs the risk of client code also trying to use us as a key | ||
// (though that's rather unlikely). | ||
// So instead lets return a pointer to one of our ivars. Doesn't really matter which one, so | ||
// we'll go with the first one. | ||
return &_object; | ||
} | ||
@end | ||
|
||
@implementation PMKVObserverDeallocSpy { | ||
PMKVObserver * _Nonnull _observer; | ||
BOOL _shouldBlock; | ||
} | ||
- (instancetype)initWithObserver:(PMKVObserver *)observer shouldBlock:(BOOL)flag { | ||
if ((self = [super init])) { | ||
_observer = observer; | ||
_shouldBlock = flag; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)dealloc { | ||
[_observer cancel:_shouldBlock]; | ||
} | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
21 changes: 21 additions & 0 deletions
21
SUIMVVMDemo/SUIMVVMDemo/SUIMVVMKit/Vender/PMKVObserver/PMKVObserver.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// PMKVObserver.h | ||
// PMKVObserver | ||
// | ||
// Created by Kevin Ballard on 11/18/15. | ||
// Copyright © 2015 Postmates. All rights reserved. | ||
// | ||
// Licensed under the MIT license <LICENSE or | ||
// http://opensource.org/licenses/MIT>. This file may not be copied, modified, | ||
// or distributed except according to those terms. | ||
// | ||
|
||
@import Foundation; | ||
|
||
//! Project version number for PMKVObserver. | ||
FOUNDATION_EXPORT double PMKVObserverVersionNumber; | ||
|
||
//! Project version string for PMKVObserver. | ||
FOUNDATION_EXPORT const unsigned char PMKVObserverVersionString[]; | ||
|
||
#import "KVObserver.h" |
Oops, something went wrong.