From 12eb13431810e39012e53b45790229114e315b0a Mon Sep 17 00:00:00 2001 From: Minya Date: Tue, 8 Nov 2016 10:09:46 +0800 Subject: [PATCH] Add MINotification category for NSObject to supply an easy way to use the notification --- Demo/MinyaDemo.xcodeproj/project.pbxproj | 11 +- Demo/MinyaDemo/PhotoDetailViewController.m | 2 + Demo/MinyaDemo/PhotoListViewController.m | 6 + Minya/Core/NSObject+MINotification.h | 43 +++++ Minya/Core/NSObject+MINotification.m | 187 +++++++++++++++++++++ Minya/Minya.h | 1 + 6 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 Minya/Core/NSObject+MINotification.h create mode 100644 Minya/Core/NSObject+MINotification.m diff --git a/Demo/MinyaDemo.xcodeproj/project.pbxproj b/Demo/MinyaDemo.xcodeproj/project.pbxproj index 088d440..6c00b40 100644 --- a/Demo/MinyaDemo.xcodeproj/project.pbxproj +++ b/Demo/MinyaDemo.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ E1C3D8181DAF8B0500044718 /* PhotoDetailStore.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C3D8171DAF8B0500044718 /* PhotoDetailStore.m */; }; E1C3D81B1DAF8B3A00044718 /* PhotoDetailPipeline.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C3D81A1DAF8B3A00044718 /* PhotoDetailPipeline.m */; }; E1D07B441DC21D4B00B8E840 /* MIUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = E1D07B431DC21D4B00B8E840 /* MIUtilities.m */; }; + E1EC48AA1DC9B3A200F221CF /* NSObject+MINotification.m in Sources */ = {isa = PBXBuildFile; fileRef = E1EC48A91DC9B3A200F221CF /* NSObject+MINotification.m */; }; E702B688D1DFD71E0680DE53 /* Pods_MinyaDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE42304832BEE7C82AC7AF08 /* Pods_MinyaDemo.framework */; }; /* End PBXBuildFile section */ @@ -183,6 +184,8 @@ E1C3D81A1DAF8B3A00044718 /* PhotoDetailPipeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoDetailPipeline.m; sourceTree = ""; }; E1D07B421DC21D4B00B8E840 /* MIUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIUtilities.h; sourceTree = ""; }; E1D07B431DC21D4B00B8E840 /* MIUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIUtilities.m; sourceTree = ""; }; + E1EC48A81DC9B3A200F221CF /* NSObject+MINotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MINotification.h"; sourceTree = ""; }; + E1EC48A91DC9B3A200F221CF /* NSObject+MINotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MINotification.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -320,6 +323,8 @@ E1798D2B1DB237CC00254295 /* NSObject+MIKVO.m */, E1798D2C1DB237CC00254295 /* NSObject+MISwizzle.h */, E1798D2D1DB237CC00254295 /* NSObject+MISwizzle.m */, + E1EC48A81DC9B3A200F221CF /* NSObject+MINotification.h */, + E1EC48A91DC9B3A200F221CF /* NSObject+MINotification.m */, ); path = Core; sourceTree = ""; @@ -519,6 +524,7 @@ TargetAttributes = { E13555AD1D9A714C001F8FA8 = { CreatedOnToolsVersion = 8.0; + DevelopmentTeam = TV929D9GDN; ProvisioningStyle = Automatic; }; E1798D751DB2391B00254295 = { @@ -639,6 +645,7 @@ E1798D641DB237CC00254295 /* EXTScope.m in Sources */, E1C3D81B1DAF8B3A00044718 /* PhotoDetailPipeline.m in Sources */, E119D5961DB0801700D2C551 /* InterestingnessPipeline.m in Sources */, + E1EC48AA1DC9B3A200F221CF /* NSObject+MINotification.m in Sources */, E1798D661DB237CC00254295 /* NSInvocation+EXT.m in Sources */, E13555B31D9A714C001F8FA8 /* main.m in Sources */, E119D5861DB0646D00D2C551 /* Photo.m in Sources */, @@ -795,7 +802,7 @@ baseConfigurationReference = B2CECFB3212332A6BD9C1987 /* Pods-MinyaDemo.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TV929D9GDN; INFOPLIST_FILE = MinyaDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -809,7 +816,7 @@ baseConfigurationReference = 24606CE93B31F7DB80D0E4EB /* Pods-MinyaDemo.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TV929D9GDN; INFOPLIST_FILE = MinyaDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/Demo/MinyaDemo/PhotoDetailViewController.m b/Demo/MinyaDemo/PhotoDetailViewController.m index fe119ea..aeae40a 100644 --- a/Demo/MinyaDemo/PhotoDetailViewController.m +++ b/Demo/MinyaDemo/PhotoDetailViewController.m @@ -22,6 +22,8 @@ - (void)viewDidLoad { self.view.backgroundColor = [UIColor whiteColor]; [self.store fetchData]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"Test" object:nil userInfo:nil]; } @end diff --git a/Demo/MinyaDemo/PhotoListViewController.m b/Demo/MinyaDemo/PhotoListViewController.m index 1f8cfd4..1337762 100644 --- a/Demo/MinyaDemo/PhotoListViewController.m +++ b/Demo/MinyaDemo/PhotoListViewController.m @@ -42,6 +42,12 @@ - (void)addObservers { @weakify(self) + [self observeNotification:@"Test" sender:nil handler:^(NSNotification *notification) { + + @strongify(self) + NSLog(@"Pipeline = %@", self.pipeline); + }]; + // When user select a cell in the tableview, it will push the next viewcontroller. // The view controller observe the action and then do the work. [MIObserve(self.pipeline, inputSelectedPhotoIndex) changed:^(id _Nonnull newValue) { diff --git a/Minya/Core/NSObject+MINotification.h b/Minya/Core/NSObject+MINotification.h new file mode 100644 index 0000000..d28bb2a --- /dev/null +++ b/Minya/Core/NSObject+MINotification.h @@ -0,0 +1,43 @@ +// +// NSObject+MINotification.h +// MinyaDemo +// +// Created by 00 on 2016/11/2. +// Copyright © 2016年 Minya. All rights reserved. +// + +#import + +/** + Callback for handling notification + + @param notification notification informations + */ +typedef void (^MINotificationBlock)(NSNotification *notification); + +#pragma mark - NSObject MINotification Category +/** + Category for NSObject's Notification + */ +@interface NSObject (MINotification) + +/** + Add current object as an observer for the special notification + + This method is an alternative method for NSNotificationCenter's `-addObserver:selector:name:object:` method. + the sender parameter is the same as `object` parameter. + + @param notificationName notification name + @param sender notification sender + @param handler handler callback + */ +- (void)observeNotification:(NSString *)notificationName sender:(id)sender handler:(MINotificationBlock)handler; + +/** + Remove notification which belong to current object for the special name. + + @param notificationName notification name + */ +- (void)removeNotification:(NSString *)notificationName; + +@end diff --git a/Minya/Core/NSObject+MINotification.m b/Minya/Core/NSObject+MINotification.m new file mode 100644 index 0000000..0547269 --- /dev/null +++ b/Minya/Core/NSObject+MINotification.m @@ -0,0 +1,187 @@ +// +// NSObject+MINotification.m +// MinyaDemo +// +// Created by 00 on 2016/11/2. +// Copyright © 2016年 Minya. All rights reserved. +// + +#import "NSObject+MINotification.h" +#import "NSObject+MIDealloc.h" +#import + +static const void *kMINotificationInfoKey = &kMINotificationInfoKey; + +#pragma mark - __MINotificationInfo + +/** + __MINotificationInfo class + + This is a private class that represent an notification. + */ +@interface __MINotificationInfo : NSObject + +@property (nonatomic, unsafe_unretained) id sender; +@property (nonatomic, copy) NSString *notificationName; +@property (nonatomic, copy) MINotificationBlock handler; + +@end + +@implementation __MINotificationInfo + +- (NSUInteger)hash { + NSString *target = [NSString stringWithFormat:@"%@_%@", [self.sender description], self.notificationName]; + return target.hash; +} + +- (BOOL)isEqual:(id)object { + + if (self == object) { + return YES; + } + + if (![self isKindOfClass:[object class]]) { + return NO; + } + + __MINotificationInfo *tempInfo = (__MINotificationInfo *)object; + + NSString *target = [NSString stringWithFormat:@"%@_%@", [self.sender description], self.notificationName]; + NSString *tempTarget = [NSString stringWithFormat:@"%@_%@", [tempInfo.sender description], self.notificationName]; + + return [target isEqualToString:tempTarget]; +} + +@end + +#pragma mark - NSObject MINotification Category + +@implementation NSObject (MINotification) + +- (void)observeNotification:(NSString *)notificationName sender:(id)sender handler:(MINotificationBlock)handler { + + NSParameterAssert(notificationName); + NSParameterAssert(handler); + + __MINotificationInfo *info = [self mi_createNotificationInfoWithNotification:notificationName + sender:sender + handler:handler]; + + NSMutableSet *infos = [self mi_notificationInfos]; + if ([infos containsObject:info]) { + return; + } + + [infos addObject:info]; + + __unsafe_unretained id unretainedSelf = self; + [self registerDeallocHandleWithKey:@"mi_notificationHandler" handle:^{ + [[NSNotificationCenter defaultCenter] removeObserver:unretainedSelf]; + }]; + + if (sender != self) { + __unsafe_unretained id unretainedSender = sender; + [sender registerDeallocHandleWithKey:@"mi_notificationHandler" handle:^{ + + [[unretainedSelf mi_notificationInfos] enumerateObjectsUsingBlock:^(__MINotificationInfo * _Nonnull info, BOOL * _Nonnull stop) { + + if (info.sender == unretainedSelf) { + [[NSNotificationCenter defaultCenter] removeObserver:unretainedSelf name:nil object:unretainedSender]; + } + }]; + }]; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(mi_handleNotification:) + name:notificationName + object:sender]; +} + +- (void)removeNotification:(NSString *)notificationName { + + NSMutableSet *infos = [self mi_notificationInfos]; + + __block __MINotificationInfo *needDeleteInfo = nil; + [infos enumerateObjectsUsingBlock:^(__MINotificationInfo * _Nonnull info, BOOL * _Nonnull stop) { + + if ([info.notificationName isEqualToString:notificationName]) { + [[NSNotificationCenter defaultCenter] removeObserver:self name:info.notificationName object:nil]; + needDeleteInfo = info; + *stop = YES; + } + }]; + + if (needDeleteInfo) { + [infos removeObject:needDeleteInfo]; + } +} + +#pragma mark - Private Methods + +/** + Create an __MINotificationInfo instance + + @param notificationName notification name + @param sender notification sender + @param handler handler callback + + @return __MINotificationInfo instance + */ +- (__MINotificationInfo *)mi_createNotificationInfoWithNotification:(NSString *)notificationName + sender:(id)sender + handler:(MINotificationBlock)handler { + + __MINotificationInfo *info = [[__MINotificationInfo alloc] init]; + info.sender = sender; + info.notificationName = notificationName; + info.handler = handler; + + return info; +} + +/** + Handle notification + + @param notification notification information + */ +- (void)mi_handleNotification:(NSNotification *)notification { + + NSSet *infos = [self mi_notificationInfos]; + [infos enumerateObjectsUsingBlock:^(__MINotificationInfo * _Nonnull info, BOOL * _Nonnull stop) { + if ([info.notificationName isEqualToString:notification.name]) { + if (!info.sender || info.sender == notification.object) { + info.handler(notification); + *stop = YES; + } + } + }]; +} + +/** + Get the notification informations of current object. + + @return a set that contains all the notification informations + */ +- (NSMutableSet *)mi_notificationInfos { + + NSMutableSet *infos = objc_getAssociatedObject(self, kMINotificationInfoKey); + if (!infos) { + infos = [[NSMutableSet alloc] init]; + [self mi_setNotificationInfos:infos]; + } + + return infos; +} + +/** + Set the notification informations of current object. + + @param infos notification informations. + */ +- (void)mi_setNotificationInfos:(NSMutableSet *)infos { + + objc_setAssociatedObject(self, kMINotificationInfoKey, infos, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/Minya/Minya.h b/Minya/Minya.h index 5d21d41..61e4610 100644 --- a/Minya/Minya.h +++ b/Minya/Minya.h @@ -23,6 +23,7 @@ #import "NSObject+MIDealloc.h" #import "NSObject+MIKVO.h" +#import "NSObject+MINotification.h" #import "EXTScope.h"