-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
XuNing
committed
Jan 23, 2019
1 parent
2abab23
commit b590bee
Showing
51 changed files
with
2,617 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,83 @@ | ||
# XNGNotificationProxy | ||
wip | ||
|
||
English | [中文](https://www.jianshu.com/p/3fe728c4d7a3) | ||
|
||
XNGNotificationProxy is a replacement of custom NSNotification. | ||
|
||
## A common scenario of NSNotification in use | ||
|
||
You followed a user in VC_U, so the follow buttons in VC_A and VC_B should update state. | ||
|
||
``` | ||
// VC_U.m | ||
[[NSNotificationCenter defaultCenter] postNotificationName:@"UserDidFollowUser" | ||
object:nil | ||
userInfo:@{@"isFollowed": @(YES), @"userID": @(123456)}]; | ||
``` | ||
``` | ||
// VC_A.m or VC_B.m | ||
[[NSNotificationCenter defaultCenter] addObserverForName:@"UserDidFollowUser" | ||
object:nil | ||
queue:nil | ||
usingBlock:^(NSNotification * _Nonnull note) { | ||
BOOL isFollowed = [note.userInfo[@"isFollowed"] boolValue]; | ||
NSNumber *userID = note.userInfo[@"userID"]; | ||
}]; | ||
``` | ||
There are a lot of magic strings. We may define some global constant strings to solve this, but we can never know what parameters are in the userInfo intuitively. | ||
|
||
## Use XNGNotificationProxy | ||
|
||
You need create a category for XNGNotificationProxy that conforms a custom protocol first. | ||
``` | ||
// XNGNotificationProxy+Protocol.h | ||
@protocol UserActionProtocol <NSObject> | ||
@optional | ||
- (void)userDidFollow:(BOOL)isFollowed userID:(NSNumber *)userID; | ||
@end | ||
@interface XNGNotificationProxy (Protocol) <UserActionProtocol> | ||
@end | ||
``` | ||
then | ||
``` | ||
// VC_U.m | ||
[[XNGNotificationProxy sharedProxy] userDidFollow:YES userID:@(123456)]; | ||
``` | ||
``` | ||
// VC_A.m | ||
@interface VC_A () <UserActionProtocol> | ||
@end | ||
// -viewDidLoad or somewhere else | ||
[[XNGNotificationProxy sharedProxy] registerProtocol:@protocol(UserActionProtocol) forObject:self]; | ||
// | ||
- (void)userDidFollow:(BOOL)isFollowed userID:(NSNumber *)userID { | ||
// do something | ||
} | ||
``` | ||
|
||
## Requirements | ||
|
||
iOS 7.0 or later. | ||
|
||
## Installation | ||
|
||
There are two ways to use XNGNotificationProxy in your project: | ||
- using CocoaPods | ||
- drag `XNGNotificationProxy` folder into your project | ||
|
||
## Licenses | ||
|
||
XNGNotificationProxy is released under the MIT license. See [LICENSE](https://github.com/xuning0/XNGNotificationProxy/blob/master/LICENSE) for details. | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Pod::Spec.new do |s| | ||
s.name = "XNGNotificationProxy" | ||
s.version = "1.0" | ||
s.summary = "A replacement of custom NSNotification using NSProxy." | ||
s.homepage = "https://github.com/xuning0/XNGNotificationProxy" | ||
s.license = "MIT" | ||
s.author = { "XuNing" => "xuning0@outlook.com" } | ||
s.platform = :ios, "7.0" | ||
s.source = { :git => "https://github.com/xuning0/XNGNotificationProxy.git", :tag => "#{s.version}" } | ||
s.requires_arc = true | ||
|
||
s.source_files = "XNGNotificationProxy/*.{h,m}" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// XNGNotificationProxy.h | ||
// XNGNotificationProxy | ||
// | ||
// Created by XuNing on 2019/1/22. | ||
// Copyright © 2019 xuning. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface XNGNotificationProxy : NSProxy | ||
|
||
+ (instancetype)sharedProxy; | ||
|
||
- (void)registerProtocol:(Protocol *)protocol forObject:(id)obj; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// | ||
// XNGNotificationProxy.m | ||
// XNGNotificationProxy | ||
// | ||
// Created by XuNing on 2019/1/22. | ||
// Copyright © 2019 xuning. All rights reserved. | ||
// | ||
|
||
#import "XNGNotificationProxy.h" | ||
#import <objc/runtime.h> | ||
|
||
@interface XNGNotificationProxy () | ||
// { method1: [obj1, obj2], method2: [obj3, obj4], ... } | ||
@property(nonatomic, strong) NSMutableDictionary<NSString *, NSHashTable *> *methodDictionary; | ||
// for self.methodDictionary thread safty | ||
@property(nonatomic, strong) dispatch_queue_t modificationQueue; | ||
@end | ||
|
||
@implementation XNGNotificationProxy | ||
|
||
+ (instancetype)sharedProxy { | ||
static XNGNotificationProxy *proxy; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
proxy = [XNGNotificationProxy alloc]; | ||
proxy.methodDictionary = [NSMutableDictionary dictionary]; | ||
proxy.modificationQueue = dispatch_queue_create("com.notificationProxy.modification", DISPATCH_QUEUE_SERIAL); | ||
}); | ||
return proxy; | ||
} | ||
|
||
- (void)registerProtocol:(Protocol *)protocol forObject:(id)obj { | ||
NSParameterAssert(protocol); | ||
NSParameterAssert(obj); | ||
NSAssert([obj conformsToProtocol:protocol], @"object %@ does not conform to protocol: %@", obj, protocol); | ||
NSArray *methodNames = [XNGNotificationProxy getAllMethodNamesInProtocol:protocol]; | ||
for (NSString *name in methodNames) { | ||
dispatch_sync(self.modificationQueue, ^{ | ||
NSHashTable *hashTable = self.methodDictionary[name]; | ||
if (!hashTable.anyObject) { | ||
hashTable = [NSHashTable weakObjectsHashTable]; | ||
} | ||
[hashTable addObject:obj]; | ||
self.methodDictionary[name] = hashTable; | ||
}); | ||
} | ||
} | ||
|
||
#pragma mark - Override Method | ||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { | ||
for (id obj in [self objectsInHashTableForSelector:sel]) { | ||
if ([obj respondsToSelector:sel]) { | ||
return [obj methodSignatureForSelector:sel]; | ||
} | ||
} | ||
// if this method returns nil, a selector not found exception is raised. | ||
// https://github.com/facebookarchive/AsyncDisplayKit/pull/1562 | ||
return [NSMethodSignature signatureWithObjCTypes:"@^v^c"]; | ||
} | ||
|
||
- (void)forwardInvocation:(NSInvocation *)invocation { | ||
for (id obj in [self objectsInHashTableForSelector:invocation.selector]) { | ||
if ([obj respondsToSelector:invocation.selector]) { | ||
[invocation invokeWithTarget:obj]; | ||
} | ||
} | ||
} | ||
|
||
#pragma mark - Private Method | ||
- (NSArray *)objectsInHashTableForSelector:(SEL)sel { | ||
__block NSArray *objects; | ||
dispatch_sync(self.modificationQueue, ^{ | ||
objects = self.methodDictionary[NSStringFromSelector(sel)].allObjects; | ||
}); | ||
return objects; | ||
} | ||
|
||
+ (void)enumerateMethodsInProtocol:(Protocol *)protocol | ||
isRequired:(BOOL)isRequired | ||
usingBlock:(void (^)(struct objc_method_description method))block { | ||
unsigned int methodsCountInProtocol = 0; | ||
struct objc_method_description *methods = protocol_copyMethodDescriptionList(protocol, isRequired, YES, &methodsCountInProtocol); | ||
for (unsigned int i = 0; i < methodsCountInProtocol; i++) { | ||
struct objc_method_description method = methods[i]; | ||
!block ?: block(method); | ||
} | ||
free(methods); | ||
} | ||
|
||
+ (NSArray *)getAllMethodNamesInProtocol:(Protocol *)protocol { | ||
NSMutableArray *methodNames = [NSMutableArray array]; | ||
[self enumerateMethodsInProtocol:protocol | ||
isRequired:YES | ||
usingBlock:^(struct objc_method_description method) { | ||
[methodNames addObject:NSStringFromSelector(method.name)]; | ||
}]; | ||
[self enumerateMethodsInProtocol:protocol | ||
isRequired:NO | ||
usingBlock:^(struct objc_method_description method) { | ||
[methodNames addObject:NSStringFromSelector(method.name)]; | ||
}]; | ||
return [methodNames copy]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
platform :ios, '9.0' | ||
|
||
target 'XNGNotificationProxyDemo' do | ||
|
||
use_frameworks! | ||
|
||
pod 'XNGNotificationProxy', :path => '..' | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
PODS: | ||
- XNGNotificationProxy (1.0) | ||
|
||
DEPENDENCIES: | ||
- XNGNotificationProxy (from `..`) | ||
|
||
EXTERNAL SOURCES: | ||
XNGNotificationProxy: | ||
:path: ".." | ||
|
||
SPEC CHECKSUMS: | ||
XNGNotificationProxy: 746998e2e0d0b55f6ad825197b6ec12aebe11ceb | ||
|
||
PODFILE CHECKSUM: 2a37536700d33c5101136eb7474edbd371fa61c1 | ||
|
||
COCOAPODS: 1.5.3 |
19 changes: 19 additions & 0 deletions
19
XNGNotificationProxyDemo/Pods/Local Podspecs/XNGNotificationProxy.podspec.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.