Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Last active August 29, 2015 14:06
Show Gist options
  • Save fjolnir/eb95e20a4fb7af2f1c32 to your computer and use it in GitHub Desktop.
Save fjolnir/eb95e20a4fb7af2f1c32 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
@interface NSObject (KVOAutoRemoval)
- (void)cy_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
@end
@interface _CYDeallocHandler : NSObject
@property(nonatomic, copy) void (^block)();
@end
@implementation _CYDeallocHandler
- (void)dealloc { _block(); }
@end
@implementation NSObject (KVOAutoRemoval)
- (void)cy_addObserver:(NSObject *)aObserver forKeyPath:(NSString *)aKeyPath options:(NSKeyValueObservingOptions)aOptions context:(void *)aContext
{
[self addObserver:aObserver forKeyPath:aKeyPath options:aOptions context:aContext];
__block void *selfPtr = (__bridge void *)self;
__unsafe_unretained id unsafeObserver = aObserver;
void (^block)() = ^{
void * const observed = selfPtr;
if(observed && OSAtomicCompareAndSwapPtr(observed, nil, &selfPtr))
[(__bridge id)observed removeObserver:unsafeObserver forKeyPath:aKeyPath context:aContext];
};
_CYDeallocHandler *handlerA = [_CYDeallocHandler new];
_CYDeallocHandler *handlerB = [_CYDeallocHandler new];
handlerA.block = block;
handlerB.block = block;
objc_setAssociatedObject(self,
(void *)((uintptr_t)(__bridge void *)aObserver ^ (uintptr_t)(__bridge void *)aKeyPath ^ (uintptr_t)aContext),
handlerA, OBJC_ASSOCIATION_RETAIN);
objc_setAssociatedObject(aObserver,
(void *)((uintptr_t)(__bridge void *)self ^ (uintptr_t)(__bridge void *)aKeyPath ^ (uintptr_t)aContext),
handlerB, OBJC_ASSOCIATION_RETAIN);
}
@end
// --------------------------
@interface Foo : NSObject
@property NSString *test;
@end
@implementation Foo
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@">>> %@ %@ -> %@", keyPath, object, [object valueForKeyPath:keyPath]);
}
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
Foo *a = [Foo new];
Foo *b = [Foo new];
[a cy_addObserver:b forKeyPath:@"test" options:0 context:NULL];
a.test = @"asdf";
b = nil;
a.test = @"foo";
a = nil;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment