NSObject+RACDeallocating.m 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. //
  2. // NSObject+RACDeallocating.m
  3. // ReactiveObjC
  4. //
  5. // Created by Kazuo Koga on 2013/03/15.
  6. // Copyright (c) 2013 GitHub, Inc. All rights reserved.
  7. //
  8. #import "NSObject+RACDeallocating.h"
  9. #import "RACCompoundDisposable.h"
  10. #import "RACDisposable.h"
  11. #import "RACReplaySubject.h"
  12. #import <objc/message.h>
  13. #import <objc/runtime.h>
  14. static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable;
  15. static NSMutableSet *swizzledClasses() {
  16. static dispatch_once_t onceToken;
  17. static NSMutableSet *swizzledClasses = nil;
  18. dispatch_once(&onceToken, ^{
  19. swizzledClasses = [[NSMutableSet alloc] init];
  20. });
  21. return swizzledClasses;
  22. }
  23. static void swizzleDeallocIfNeeded(Class classToSwizzle) {
  24. @synchronized (swizzledClasses()) {
  25. NSString *className = NSStringFromClass(classToSwizzle);
  26. if ([swizzledClasses() containsObject:className]) return;
  27. SEL deallocSelector = sel_registerName("dealloc");
  28. __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
  29. id newDealloc = ^(__unsafe_unretained id self) {
  30. RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
  31. [compoundDisposable dispose];
  32. if (originalDealloc == NULL) {
  33. struct objc_super superInfo = {
  34. .receiver = self,
  35. .super_class = class_getSuperclass(classToSwizzle)
  36. };
  37. void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
  38. msgSend(&superInfo, deallocSelector);
  39. } else {
  40. originalDealloc(self, deallocSelector);
  41. }
  42. };
  43. IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
  44. if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) {
  45. // The class already contains a method implementation.
  46. Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
  47. // We need to store original implementation before setting new implementation
  48. // in case method is called at the time of setting.
  49. originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
  50. // We need to store original implementation again, in case it just changed.
  51. originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
  52. }
  53. [swizzledClasses() addObject:className];
  54. }
  55. }
  56. @implementation NSObject (RACDeallocating)
  57. - (RACSignal *)rac_willDeallocSignal {
  58. RACSignal *signal = objc_getAssociatedObject(self, _cmd);
  59. if (signal != nil) return signal;
  60. RACReplaySubject *subject = [RACReplaySubject subject];
  61. [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  62. [subject sendCompleted];
  63. }]];
  64. objc_setAssociatedObject(self, _cmd, subject, OBJC_ASSOCIATION_RETAIN);
  65. return subject;
  66. }
  67. - (RACCompoundDisposable *)rac_deallocDisposable {
  68. @synchronized (self) {
  69. RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
  70. if (compoundDisposable != nil) return compoundDisposable;
  71. swizzleDeallocIfNeeded(self.class);
  72. compoundDisposable = [RACCompoundDisposable compoundDisposable];
  73. objc_setAssociatedObject(self, RACObjectCompoundDisposable, compoundDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  74. return compoundDisposable;
  75. }
  76. }
  77. @end