Permalink
Browse files

Working version

  • Loading branch information...
1 parent c951994 commit 96ee9fffb7f2b89715f65b0dec1027c35f243f03 Krzysztof Zabłocki committed Mar 28, 2012
Showing with 2,128 additions and 0 deletions.
  1. +10 −0 SFObservers/NSNotificationCenter+SFObservers.h
  2. +221 −0 SFObservers/NSNotificationCenter+SFObservers.m
  3. +10 −0 SFObservers/NSObject+SFObservers.h
  4. +222 −0 SFObservers/NSObject+SFObservers.m
  5. +76 −0 SFObservers/SFExecuteOnDealloc/NSObject+SFExecuteOnDealloc.h
  6. +72 −0 SFObservers/SFExecuteOnDealloc/NSObject+SFExecuteOnDealloc.m
  7. +59 −0 SFObservers/SFObservers.h
  8. +474 −0 Sample ARC/Sample ARC.xcodeproj/project.pbxproj
  9. +7 −0 Sample ARC/Sample ARC.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  10. +15 −0 Sample ARC/Sample ARC/SFAppDelegate.h
  11. +51 −0 Sample ARC/Sample ARC/SFAppDelegate.m
  12. +45 −0 Sample ARC/Sample ARC/Sample ARC-Info.plist
  13. +14 −0 Sample ARC/Sample ARC/Sample ARC-Prefix.pch
  14. +2 −0 Sample ARC/Sample ARC/en.lproj/InfoPlist.strings
  15. +18 −0 Sample ARC/Sample ARC/main.m
  16. +22 −0 Sample ARC/Sample ARCTests/Sample ARCTests-Info.plist
  17. +13 −0 Sample ARC/Sample ARCTests/Sample_ARCTests.h
  18. +31 −0 Sample ARC/Sample ARCTests/Sample_ARCTests.m
  19. +2 −0 Sample ARC/Sample ARCTests/en.lproj/InfoPlist.strings
  20. +467 −0 Sample Project/Sample Project.xcodeproj/project.pbxproj
  21. +7 −0 Sample Project/Sample Project.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  22. +15 −0 Sample Project/Sample Project/SFAppDelegate.h
  23. +57 −0 Sample Project/Sample Project/SFAppDelegate.m
  24. +45 −0 Sample Project/Sample Project/Sample Project-Info.plist
  25. +16 −0 Sample Project/Sample Project/Sample Project-Prefix.pch
  26. +2 −0 Sample Project/Sample Project/en.lproj/InfoPlist.strings
  27. +17 −0 Sample Project/Sample Project/main.m
  28. +22 −0 Sample Project/Sample ProjectTests/Sample ProjectTests-Info.plist
  29. +13 −0 Sample Project/Sample ProjectTests/Sample_ProjectTests.h
  30. +101 −0 Sample Project/Sample ProjectTests/Sample_ProjectTests.m
  31. +2 −0 Sample Project/Sample ProjectTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,10 @@
+//
+// Created by krzysztof.zablocki on 3/23/12.
+//
+//
+//
+#import <Foundation/Foundation.h>
+#import "SFObservers.h"
+
+@interface NSNotificationCenter (SFObservers)
+@end
@@ -0,0 +1,221 @@
+//
+// Created by krzysztof.zablocki on 3/23/12.
+//
+//
+//
+
+#import "NSNotificationCenter+SFObservers.h"
+#import <objc/runtime.h>
+#import <objc/message.h>
+
+static NSString const *NSNotificationCenterSFObserversArrayKey = @"NSNotificationCenterSFObserversArrayKey";
+static NSString const *NSNotificationCenterSFObserversAllowMethodForwardingKey = @"NSNotificationCenterSFObserversAllowMethodForwardingKey";
+
+static NSString *NSNotificationCenterSFObserversAddSelector = @"sf_original_addObserver:selector:name:object:";
+static NSString *NSNotificationCenterSFObserversRemoveSelector = @"sf_original_removeObserver:";
+static NSString *NSNotificationCenterSFObserversRemoveSpecificSelector = @"sf_original_removeObserver:name:object:";
+
+@interface __SFObserversNotificationObserverInfo : NSObject
+@property(nonatomic, copy) NSString *name;
+@property(nonatomic, AH_WEAK) id object;
+@property(nonatomic, assign) void *blockKey;
+@end
+
+@implementation __SFObserversNotificationObserverInfo
+@synthesize name;
+@synthesize object;
+@synthesize blockKey;
+
+
+- (void)dealloc
+{
+ AH_RELEASE(name);
+ AH_SUPER_DEALLOC;
+}
+
+@end
+
+@implementation NSNotificationCenter (SFObservers)
+
++ (void)sf_swapSelector:(SEL)aOriginalSelector withSelector:(SEL)aSwappedSelector
+{
+ Method originalMethod = class_getInstanceMethod(self, aOriginalSelector);
+ Method swappedMethod = class_getInstanceMethod(self, aSwappedSelector);
+
+ SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"sf_original_%@", NSStringFromSelector(aOriginalSelector)]);
+ class_addMethod([self class], newSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
+ class_replaceMethod([self class], aOriginalSelector, method_getImplementation(swappedMethod), method_getTypeEncoding(swappedMethod));
+}
+
++ (void)initialize
+{
+ //! swap methods
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ @autoreleasepool {
+ [self sf_swapSelector:@selector(addObserver:selector:name:object:) withSelector:@selector(sf_addObserver:selector:name:object:)];
+ [self sf_swapSelector:@selector(removeObserver:) withSelector:@selector(sf_removeObserver:)];
+ [self sf_swapSelector:@selector(removeObserver:name:object:) withSelector:@selector(sf_removeObserver:name:object:)];
+ }
+ });
+}
+
+- (BOOL)allowMethodForwarding
+{
+ NSNumber *state = objc_getAssociatedObject(self, AH_BRIDGE(NSNotificationCenterSFObserversAllowMethodForwardingKey));
+ return [state boolValue];
+}
+
+- (void)setAllowMethodForwarding:(BOOL)allowForwarding
+{
+ objc_setAssociatedObject(self, AH_BRIDGE(NSNotificationCenterSFObserversAllowMethodForwardingKey), [NSNumber numberWithBool:allowForwarding], OBJC_ASSOCIATION_RETAIN);
+}
+
+- (void)sf_addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject
+{
+ //! store info into our observer structure
+ NSMutableDictionary *registeredNotifications = (NSMutableDictionary *)objc_getAssociatedObject(observer, AH_BRIDGE(NSNotificationCenterSFObserversArrayKey));
+ if (!registeredNotifications) {
+ registeredNotifications = [NSMutableDictionary dictionary];
+ objc_setAssociatedObject(observer, AH_BRIDGE(NSNotificationCenterSFObserversArrayKey), registeredNotifications, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ NSMutableArray *observerInfos = [registeredNotifications objectForKey:NSStringFromSelector(aSelector)];
+ if (!observerInfos) {
+ observerInfos = [NSMutableArray array];
+ [registeredNotifications setObject:observerInfos forKey:NSStringFromSelector(aSelector)];
+ }
+ __block __SFObserversNotificationObserverInfo *observerInfo = nil;
+
+ //! don't allow to add many times the same observer
+ [observerInfos enumerateObjectsUsingBlock:^void(id obj, NSUInteger idx, BOOL *stop) {
+ __SFObserversNotificationObserverInfo *info = obj;
+ if ([info.name isEqualToString:aName] && info.object == anObject) {
+ observerInfo = info;
+ *stop = YES;
+ }
+ }];
+
+ if (!observerInfo) {
+ observerInfo = [[__SFObserversNotificationObserverInfo alloc] init];
+ [observerInfos addObject:observerInfo];
+ AH_RELEASE(observerInfo);
+ } else {
+ //! don't register twice so skip this
+ NSAssert(NO, @"You shouldn't register twice for same notification, selector, name, object");
+ return;
+ }
+
+ observerInfo.name = aName;
+ observerInfo.object = anObject;
+
+ //! Add auto remove when observer is going to be deallocated
+ __unsafe_unretained __block id weakSelf = self;
+ __unsafe_unretained __block id weakObserver = observer;
+ __unsafe_unretained __block id weakObject = anObject;
+
+ void *key = [observer performBlockOnDealloc:^{
+ if ([weakSelf sf_removeObserver:weakObserver name:aName object:weakObject registeredNotifications:registeredNotifications]) {
+ [self setAllowMethodForwarding:YES];
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@ %@ %@", NSNotificationCenterSFObserversRemoveSpecificSelector, weakObserver, aName, weakObject);
+#endif
+ objc_msgSend(weakSelf, NSSelectorFromString(NSNotificationCenterSFObserversRemoveSpecificSelector), weakObserver, aName, weakObject);
+ [self setAllowMethodForwarding:NO];
+ }
+ }];
+
+ //! remember the block key
+ observerInfo.blockKey = key;
+
+ //! call originalMethod
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@ %@ %@ %@", NSNotificationCenterSFObserversAddSelector, observer, NSStringFromSelector(aSelector), aName, anObject);
+#endif
+ objc_msgSend(self, NSSelectorFromString(NSNotificationCenterSFObserversAddSelector), observer, aSelector, aName, anObject);
+}
+
+
+- (void)sf_removeObserver:(id)observer
+{
+ NSMutableDictionary *registeredNotifications = (NSMutableDictionary *)objc_getAssociatedObject(observer, AH_BRIDGE(NSNotificationCenterSFObserversArrayKey));
+ if ([self allowMethodForwarding] || [self sf_removeObserver:observer name:nil object:nil registeredNotifications:registeredNotifications]) {
+ if ([self allowMethodForwarding]) {
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@", NSNotificationCenterSFObserversRemoveSelector, observer);
+#endif
+ objc_msgSend(self, NSSelectorFromString(NSNotificationCenterSFObserversRemoveSelector), observer);
+ } else {
+ [self setAllowMethodForwarding:YES];
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@", NSNotificationCenterSFObserversRemoveSelector, observer);
+#endif
+ objc_msgSend(self, NSSelectorFromString(NSNotificationCenterSFObserversRemoveSelector), observer);
+ [self setAllowMethodForwarding:NO];
+ }
+ }
+}
+
+- (void)sf_removeObserver:(id)observer name:(NSString *)aName object:(id)anObject
+{
+ NSMutableDictionary *registeredNotifications = (NSMutableDictionary *)objc_getAssociatedObject(observer, AH_BRIDGE(NSNotificationCenterSFObserversArrayKey));
+ if ([self allowMethodForwarding] || [self sf_removeObserver:observer name:aName object:anObject registeredNotifications:registeredNotifications]) {
+ if ([self allowMethodForwarding]) {
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@ %@ %@", NSNotificationCenterSFObserversRemoveSpecificSelector, observer, aName, anObject);
+#endif
+ objc_msgSend(self, NSSelectorFromString(NSNotificationCenterSFObserversRemoveSpecificSelector), observer, aName, anObject);
+ } else {
+ [self setAllowMethodForwarding:YES];
+#if SF_OBSERVERS_LOG_ORIGINAL_METHODS
+ NSLog(@"Calling original method %@ with parameters %@ %@ %@", NSNotificationCenterSFObserversRemoveSpecificSelector, observer, aName, anObject);
+#endif
+ objc_msgSend(self, NSSelectorFromString(NSNotificationCenterSFObserversRemoveSpecificSelector), observer, aName, anObject);
+ [self setAllowMethodForwarding:NO];
+ }
+ }
+}
+
+- (BOOL)sf_removeObserver:(id)observer name:(NSString *)aName object:(id)anObject registeredNotifications:(NSMutableDictionary *)registeredNotifications
+{
+ __block BOOL result = NO;
+
+ if (aName == nil && anObject == nil) {
+ //! don't need to execute block on dealloc so cleanup
+ [registeredNotifications enumerateKeysAndObjectsUsingBlock:^void(id key, id obj, BOOL *stop) {
+ NSMutableArray *observerInfos = obj;
+ [observerInfos enumerateObjectsUsingBlock:^void(id innerObj, NSUInteger idx, BOOL *innerStop) {
+ __SFObserversNotificationObserverInfo *info = innerObj;
+ [observer cancelDeallocBlockWithKey:info.blockKey];
+ }];
+ }];
+ [registeredNotifications removeAllObjects];
+
+ return YES;
+ } else {
+ [registeredNotifications enumerateKeysAndObjectsUsingBlock:^void(id key, id obj, BOOL *stop) {
+ NSMutableArray *observerInfos = obj;
+ NSMutableArray *objectsToRemove = [NSMutableArray array];
+ [observerInfos enumerateObjectsUsingBlock:^void(id innerObj, NSUInteger idx, BOOL *innerStop) {
+ __SFObserversNotificationObserverInfo *info = innerObj;
+
+ if ((!aName || [aName isEqualToString:info.name]) && (!anObject || (anObject == info.object))) {
+ //! remove this info
+ [objectsToRemove addObject:innerObj];
+
+ //! cancel dealloc blocks
+ [innerObj cancelDeallocBlockWithKey:info.blockKey];
+ }
+ }];
+
+ //! remove all collected objects
+ if ([objectsToRemove count] > 0) {
+ [observerInfos removeObjectsInArray:objectsToRemove];
+ result = YES;
+ }
+ }];
+ }
+
+ return result;
+}
+@end
@@ -0,0 +1,10 @@
+//
+// Created by merowing2 on 3/25/12.
+//
+//
+//
+#import <Foundation/Foundation.h>
+#import "SFObservers.h"
+
+@interface NSObject (SFObservers)
+@end
Oops, something went wrong.

0 comments on commit 96ee9ff

Please sign in to comment.