Skip to content

Commit

Permalink
Add MINotification category for NSObject to supply an easy way to use…
Browse files Browse the repository at this point in the history
… the notification
  • Loading branch information
Minya committed Nov 8, 2016
1 parent 9f4dc2c commit 12eb134
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 2 deletions.
11 changes: 9 additions & 2 deletions Demo/MinyaDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -183,6 +184,8 @@
E1C3D81A1DAF8B3A00044718 /* PhotoDetailPipeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoDetailPipeline.m; sourceTree = "<group>"; };
E1D07B421DC21D4B00B8E840 /* MIUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIUtilities.h; sourceTree = "<group>"; };
E1D07B431DC21D4B00B8E840 /* MIUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIUtilities.m; sourceTree = "<group>"; };
E1EC48A81DC9B3A200F221CF /* NSObject+MINotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MINotification.h"; sourceTree = "<group>"; };
E1EC48A91DC9B3A200F221CF /* NSObject+MINotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MINotification.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -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 = "<group>";
Expand Down Expand Up @@ -519,6 +524,7 @@
TargetAttributes = {
E13555AD1D9A714C001F8FA8 = {
CreatedOnToolsVersion = 8.0;
DevelopmentTeam = TV929D9GDN;
ProvisioningStyle = Automatic;
};
E1798D751DB2391B00254295 = {
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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";
Expand All @@ -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";
Expand Down
2 changes: 2 additions & 0 deletions Demo/MinyaDemo/PhotoDetailViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ - (void)viewDidLoad {
self.view.backgroundColor = [UIColor whiteColor];

[self.store fetchData];

[[NSNotificationCenter defaultCenter] postNotificationName:@"Test" object:nil userInfo:nil];
}

@end
6 changes: 6 additions & 0 deletions Demo/MinyaDemo/PhotoListViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
43 changes: 43 additions & 0 deletions Minya/Core/NSObject+MINotification.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// NSObject+MINotification.h
// MinyaDemo
//
// Created by 00 on 2016/11/2.
// Copyright © 2016年 Minya. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
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
187 changes: 187 additions & 0 deletions Minya/Core/NSObject+MINotification.m
Original file line number Diff line number Diff line change
@@ -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 <objc/runtime.h>

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
1 change: 1 addition & 0 deletions Minya/Minya.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#import "NSObject+MIDealloc.h"
#import "NSObject+MIKVO.h"
#import "NSObject+MINotification.h"

#import "EXTScope.h"

Expand Down

0 comments on commit 12eb134

Please sign in to comment.