NSObject+XWAdd.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. //
  2. // NSObject+XWAdd.m
  3. // BuguLive
  4. //
  5. // Created by 岳克奎 on 16/9/1.
  6. // Copyright © 2016年 xfg. All rights reserved.
  7. //
  8. #import "NSObject+XWAdd.h"
  9. #import <objc/runtime.h>
  10. #import <objc/message.h>
  11. #pragma mark - 私有实现KVO的真实target类,每一个target对应了一个keyPath和监听该keyPath的所有block,当其KVO方法调用时,需要回调所有的block
  12. @interface _XWBlockTarget : NSObject
  13. /**添加一个KVOBlock*/
  14. - (void)xw_addBlock:(void(^)(__weak id obj, id oldValue, id newValue))block;
  15. - (void)xw_addNotificationBlock:(void(^)(NSNotification *notification))block;
  16. - (void)xw_doNotification:(NSNotification*)notification;
  17. @end
  18. @implementation _XWBlockTarget{
  19. //保存所有的block
  20. NSMutableSet *_kvoBlockSet;
  21. NSMutableSet *_notificationBlockSet;
  22. }
  23. - (instancetype)init
  24. {
  25. self = [super init];
  26. if (self) {
  27. _kvoBlockSet = [NSMutableSet new];
  28. _notificationBlockSet = [NSMutableSet new];
  29. }
  30. return self;
  31. }
  32. - (void)xw_addBlock:(void(^)(__weak id obj, id oldValue, id newValue))block{
  33. [_kvoBlockSet addObject:[block copy]];
  34. }
  35. - (void)xw_addNotificationBlock:(void(^)(NSNotification *notification))block{
  36. [_notificationBlockSet addObject:[block copy]];
  37. }
  38. - (void)xw_doNotification:(NSNotification*)notification{
  39. if (!_notificationBlockSet.count) return;
  40. [_notificationBlockSet enumerateObjectsUsingBlock:^(void (^block)(NSNotification *notification), BOOL * _Nonnull stop) {
  41. block(notification);
  42. }];
  43. }
  44. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
  45. if (!_kvoBlockSet.count) return;
  46. BOOL prior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
  47. //只接受值改变时的消息
  48. if (prior) return;
  49. NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
  50. if (changeKind != NSKeyValueChangeSetting) return;
  51. id oldVal = [change objectForKey:NSKeyValueChangeOldKey];
  52. if (oldVal == [NSNull null]) oldVal = nil;
  53. id newVal = [change objectForKey:NSKeyValueChangeNewKey];
  54. if (newVal == [NSNull null]) newVal = nil;
  55. //执行该target下的所有block
  56. [_kvoBlockSet enumerateObjectsUsingBlock:^(void (^block)(__weak id obj, id oldVal, id newVal), BOOL * _Nonnull stop) {
  57. block(object, oldVal, newVal);
  58. }];
  59. }
  60. @end
  61. @implementation NSObject (XWAdd)
  62. static void *const XWKVOBlockKey = "XWKVOBlockKey";
  63. static void *const XWKVOSemaphoreKey = "XWKVOSemaphoreKey";
  64. - (void)xw_addObserverBlockForKeyPath:(NSString*)keyPath block:(void (^)(id obj, id oldVal, id newVal))block {
  65. if (!keyPath || !block) return;
  66. dispatch_semaphore_t kvoSemaphore = [self _xw_getSemaphoreWithKey:XWKVOSemaphoreKey];
  67. dispatch_semaphore_wait(kvoSemaphore, DISPATCH_TIME_FOREVER);
  68. //取出存有所有KVOTarget的字典
  69. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWKVOBlockKey);
  70. if (!allTargets) {
  71. //没有则创建
  72. allTargets = [NSMutableDictionary new];
  73. //绑定在该对象中
  74. objc_setAssociatedObject(self, XWKVOBlockKey, allTargets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  75. }
  76. //获取对应keyPath中的所有target
  77. _XWBlockTarget *targetForKeyPath = allTargets[keyPath];
  78. if (!targetForKeyPath) {
  79. //没有则创建
  80. targetForKeyPath = [_XWBlockTarget new];
  81. //保存
  82. allTargets[keyPath] = targetForKeyPath;
  83. //如果第一次,则注册对keyPath的KVO监听
  84. [self addObserver:targetForKeyPath forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
  85. }
  86. [targetForKeyPath xw_addBlock:block];
  87. //对第一次注册KVO的类进行dealloc方法调剂
  88. [self _xw_swizzleDealloc];
  89. dispatch_semaphore_signal(kvoSemaphore);
  90. }
  91. - (void)xw_removeObserverBlockForKeyPath:(NSString *)keyPath{
  92. if (!keyPath.length) return;
  93. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWKVOBlockKey);
  94. if (!allTargets) return;
  95. _XWBlockTarget *target = allTargets[keyPath];
  96. if (!target) return;
  97. dispatch_semaphore_t kvoSemaphore = [self _xw_getSemaphoreWithKey:XWKVOSemaphoreKey];
  98. dispatch_semaphore_wait(kvoSemaphore, DISPATCH_TIME_FOREVER);
  99. [self removeObserver:target forKeyPath:keyPath];
  100. [allTargets removeObjectForKey:keyPath];
  101. dispatch_semaphore_signal(kvoSemaphore);
  102. }
  103. - (void)xw_removeAllObserverBlocks {
  104. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWKVOBlockKey);
  105. if (!allTargets) return;
  106. dispatch_semaphore_t kvoSemaphore = [self _xw_getSemaphoreWithKey:XWKVOSemaphoreKey];
  107. dispatch_semaphore_wait(kvoSemaphore, DISPATCH_TIME_FOREVER);
  108. [allTargets enumerateKeysAndObjectsUsingBlock:^(id key, _XWBlockTarget *target, BOOL *stop) {
  109. [self removeObserver:target forKeyPath:key];
  110. }];
  111. [allTargets removeAllObjects];
  112. dispatch_semaphore_signal(kvoSemaphore);
  113. }
  114. static void *const XWNotificationBlockKey = "XWNotificationBlockKey";
  115. static void *const XWNotificationSemaphoreKey = "XWNotificationSemaphoreKey";
  116. - (void)xw_addNotificationForName:(NSString *)name block:(void (^)(NSNotification *notification))block {
  117. if (!name || !block) return;
  118. dispatch_semaphore_t notificationSemaphore = [self _xw_getSemaphoreWithKey:XWNotificationSemaphoreKey];
  119. dispatch_semaphore_wait(notificationSemaphore, DISPATCH_TIME_FOREVER);
  120. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWNotificationBlockKey);
  121. if (!allTargets) {
  122. allTargets = @{}.mutableCopy;
  123. objc_setAssociatedObject(self, XWNotificationBlockKey, allTargets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  124. }
  125. _XWBlockTarget *target = allTargets[name];
  126. if (!target) {
  127. target = [_XWBlockTarget new];
  128. allTargets[name] = target;
  129. [[NSNotificationCenter defaultCenter] addObserver:target selector:@selector(xw_doNotification:) name:name object:nil];
  130. }
  131. [target xw_addNotificationBlock:block];
  132. [self _xw_swizzleDealloc];
  133. dispatch_semaphore_signal(notificationSemaphore);
  134. }
  135. - (void)xw_removeNotificationForName:(NSString *)name{
  136. if (!name) return;
  137. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWNotificationBlockKey);
  138. if (!allTargets.count) return;
  139. _XWBlockTarget *target = allTargets[name];
  140. if (!target) return;
  141. dispatch_semaphore_t notificationSemaphore = [self _xw_getSemaphoreWithKey:XWNotificationSemaphoreKey];
  142. dispatch_semaphore_wait(notificationSemaphore, DISPATCH_TIME_FOREVER);
  143. [[NSNotificationCenter defaultCenter] removeObserver:target];
  144. [allTargets removeObjectForKey:name];
  145. dispatch_semaphore_signal(notificationSemaphore);
  146. }
  147. - (void)xw_removeAllNotification{
  148. NSMutableDictionary *allTargets = objc_getAssociatedObject(self, XWNotificationBlockKey);
  149. if (!allTargets.count) return;
  150. dispatch_semaphore_t notificationSemaphore = [self _xw_getSemaphoreWithKey:XWNotificationSemaphoreKey];
  151. dispatch_semaphore_wait(notificationSemaphore, DISPATCH_TIME_FOREVER);
  152. [allTargets enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, _XWBlockTarget *target, BOOL * _Nonnull stop) {
  153. [[NSNotificationCenter defaultCenter] removeObserver:target];
  154. }];
  155. [allTargets removeAllObjects];
  156. dispatch_semaphore_signal(notificationSemaphore);
  157. }
  158. - (void)xw_postNotificationWithName:(NSString *)name userInfo:(NSDictionary *)userInfo{
  159. [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:userInfo];
  160. }
  161. static void * deallocHasSwizzledKey = "deallocHasSwizzledKey";
  162. /**
  163. * 调剂dealloc方法,由于无法直接使用运行时的swizzle方法对dealloc方法进行调剂,所以稍微麻烦一些
  164. */
  165. - (void)_xw_swizzleDealloc{
  166. //我们给每个类绑定上一个值来判断dealloc方法是否被调剂过,如果调剂过了就无需再次调剂了
  167. BOOL swizzled = [objc_getAssociatedObject(self.class, deallocHasSwizzledKey) boolValue];
  168. //如果调剂过则直接返回
  169. if (swizzled) return;
  170. //开始调剂
  171. Class swizzleClass = self.class;
  172. @synchronized(swizzleClass) {
  173. //获取原有的dealloc方法
  174. SEL deallocSelector = sel_registerName("dealloc");
  175. //初始化一个函数指针用于保存原有的dealloc方法
  176. __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
  177. //实现我们自己的dealloc方法,通过block的方式
  178. id newDealloc = ^(__unsafe_unretained id objSelf){
  179. //在这里我们移除所有的KVO
  180. [objSelf xw_removeAllObserverBlocks];
  181. //移除所有通知
  182. [objSelf xw_removeAllNotification];
  183. //根据原有的dealloc方法是否存在进行判断
  184. if (originalDealloc == NULL) {//如果不存在,说明本类没有实现dealloc方法,则需要向父类发送dealloc消息(objc_msgSendSuper)
  185. //构造objc_msgSendSuper所需要的参数,.receiver为方法的实际调用者,即为类本身,.super_class指向其父类
  186. struct objc_super superInfo = {
  187. .receiver = objSelf,
  188. .super_class = class_getSuperclass(swizzleClass)
  189. };
  190. //构建objc_msgSendSuper函数
  191. void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
  192. //向super发送dealloc消息
  193. msgSend(&superInfo, deallocSelector);
  194. }else{//如果存在,表明该类实现了dealloc方法,则直接调用即可
  195. //调用原有的dealloc方法
  196. originalDealloc(objSelf, deallocSelector);
  197. }
  198. };
  199. //根据block构建新的dealloc实现IMP
  200. IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
  201. //尝试添加新的dealloc方法,如果该类已经复写的dealloc方法则不能添加成功,反之则能够添加成功
  202. if (!class_addMethod(swizzleClass, deallocSelector, newDeallocIMP, "v@:")) {
  203. //如果没有添加成功则保存原有的dealloc方法,用于新的dealloc方法中
  204. Method deallocMethod = class_getInstanceMethod(swizzleClass, deallocSelector);
  205. originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_getImplementation(deallocMethod);
  206. originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_setImplementation(deallocMethod, newDeallocIMP);
  207. }
  208. //标记该类已经调剂过了
  209. objc_setAssociatedObject(self.class, deallocHasSwizzledKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  210. }
  211. }
  212. - (dispatch_semaphore_t)_xw_getSemaphoreWithKey:(void *)key{
  213. dispatch_semaphore_t semaphore = objc_getAssociatedObject(self, key);
  214. if (!semaphore) {
  215. semaphore = dispatch_semaphore_create(1);
  216. objc_setAssociatedObject(self, key, semaphore, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  217. }
  218. return semaphore;
  219. }
  220. @end