Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Add KVOController for self-observing: KVOControllerForSelf #131

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion FBKVOController/FBKVOController.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ extern NSString *const FBKVONotificationKeyPathKey;
*/
typedef void (^FBKVONotificationBlock)(id _Nullable observer, id object, NSDictionary<NSKeyValueChangeKey, id> *change);

typedef NS_ENUM(NSUInteger, FBKVOControllerObjectStoreType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation for the ENUM and all constants here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

FBKVOControllerObjectStoreTypeStrong,
FBKVOControllerObjectStoreTypeWeak,
FBKVOControllerObjectStoreTypeAssign,
};

/**
@abstract FBKVOController makes Key-Value Observing simpler and safer.
@discussion FBKVOController adds support for handling key-value changes with blocks and custom actions, as well as the NSKeyValueObserving callback. Notification will never message a deallocated observer. Observer removal never throws exceptions, and observers are removed implicitly on controller deallocation. FBKVOController is also thread safe. When used in a concurrent environment, it protects observers from possible resurrection and avoids ensuing crash. By default, the controller maintains a strong reference to objects observed.
Expand All @@ -76,7 +82,7 @@ typedef void (^FBKVONotificationBlock)(id _Nullable observer, id object, NSDicti
@return The initialized KVO controller instance.
@discussion Use retainObserved = NO when a strong reference between controller and observee would create a retain loop. When not retaining observees, special care must be taken to remove observation info prior to observee dealloc.
*/
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObserver:(nullable id)observer storeType:(FBKVOControllerObjectStoreType)storeType NS_DESIGNATED_INITIALIZER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is quite important not to change the public API, since there might be folks still using it.
How about we deprecate the existing method, but still make it work and add this one as a new method?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


/**
@abstract Convenience initializer.
Expand Down
17 changes: 14 additions & 3 deletions FBKVOController/FBKVOController.m
Original file line number Diff line number Diff line change
Expand Up @@ -412,12 +412,23 @@ + (instancetype)controllerWithObserver:(nullable id)observer
return [[self alloc] initWithObserver:observer];
}

- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
- (instancetype)initWithObserver:(nullable id)observer storeType:(FBKVOControllerObjectStoreType)storeType
{
self = [super init];
if (nil != self) {
_observer = observer;
NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
NSPointerFunctionsOptions keyOptions = NSPointerFunctionsObjectPointerPersonality;
switch (storeType) {
case FBKVOControllerObjectStoreTypeStrong:
keyOptions |= NSPointerFunctionsStrongMemory;
break;
case FBKVOControllerObjectStoreTypeWeak:
keyOptions |= NSPointerFunctionsWeakMemory;
break;
case FBKVOControllerObjectStoreTypeAssign:
keyOptions |= NSPointerFunctionsOpaqueMemory;
break;
}
_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
pthread_mutex_init(&_lock, NULL);
}
Expand All @@ -426,7 +437,7 @@ - (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)reta

- (instancetype)initWithObserver:(nullable id)observer
{
return [self initWithObserver:observer retainObserved:YES];
return [self initWithObserver:observer storeType:FBKVOControllerObjectStoreTypeStrong];
}

- (void)dealloc
Expand Down
9 changes: 9 additions & 0 deletions FBKVOController/NSObject+FBKVOController.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;

/**
@abstract Lazy-loaded FBKVOController for use with self
@return FBKVOController associated with this object, creating one if necessary
@discussion This makes it convenient to simply create and forget a FBKVOController.
Use this version when a strong reference between controller and observed object would create a retain cycle.
When not retaining observed objects, special care must be taken to remove observation info prior to deallocation of the observed object.
*/
@property (nonatomic, strong) FBKVOController *KVOControllerForSelf;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great!


@end

NS_ASSUME_NONNULL_END
20 changes: 19 additions & 1 deletion FBKVOController/NSObject+FBKVOController.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

static void *NSObjectKVOControllerKey = &NSObjectKVOControllerKey;
static void *NSObjectKVOControllerNonRetainingKey = &NSObjectKVOControllerNonRetainingKey;
static void *NSObjectKVOControllerForSelfKey = &NSObjectKVOControllerForSelfKey;

@implementation NSObject (FBKVOController)

Expand All @@ -47,7 +48,7 @@ - (FBKVOController *)KVOControllerNonRetaining
id controller = objc_getAssociatedObject(self, NSObjectKVOControllerNonRetainingKey);

if (nil == controller) {
controller = [[FBKVOController alloc] initWithObserver:self retainObserved:NO];
controller = [[FBKVOController alloc] initWithObserver:self storeType:FBKVOControllerObjectStoreTypeWeak];
self.KVOControllerNonRetaining = controller;
}

Expand All @@ -59,6 +60,23 @@ - (void)setKVOControllerNonRetaining:(FBKVOController *)KVOControllerNonRetainin
objc_setAssociatedObject(self, NSObjectKVOControllerNonRetainingKey, KVOControllerNonRetaining, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (FBKVOController *)KVOControllerForSelf
{
id controller = objc_getAssociatedObject(self, NSObjectKVOControllerForSelfKey);

if (nil == controller) {
controller = [[FBKVOController alloc] initWithObserver:self storeType:FBKVOControllerObjectStoreTypeAssign];
self.KVOControllerForSelf = controller;
}

return controller;
}

- (void)setKVOControllerForSelf:(FBKVOController *)KVOControllerForSelf
{
objc_setAssociatedObject(self, NSObjectKVOControllerForSelfKey, KVOControllerForSelf, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end


Expand Down