123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- //
- // NSObject+RACDeallocating.m
- // ReactiveObjC
- //
- // Created by Kazuo Koga on 2013/03/15.
- // Copyright (c) 2013 GitHub, Inc. All rights reserved.
- //
- #import "NSObject+RACDeallocating.h"
- #import "RACCompoundDisposable.h"
- #import "RACDisposable.h"
- #import "RACReplaySubject.h"
- #import <objc/message.h>
- #import <objc/runtime.h>
- static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable;
- static NSMutableSet *swizzledClasses() {
- static dispatch_once_t onceToken;
- static NSMutableSet *swizzledClasses = nil;
- dispatch_once(&onceToken, ^{
- swizzledClasses = [[NSMutableSet alloc] init];
- });
-
- return swizzledClasses;
- }
- static void swizzleDeallocIfNeeded(Class classToSwizzle) {
- @synchronized (swizzledClasses()) {
- NSString *className = NSStringFromClass(classToSwizzle);
- if ([swizzledClasses() containsObject:className]) return;
- SEL deallocSelector = sel_registerName("dealloc");
- __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
- id newDealloc = ^(__unsafe_unretained id self) {
- RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
- [compoundDisposable dispose];
- if (originalDealloc == NULL) {
- struct objc_super superInfo = {
- .receiver = self,
- .super_class = class_getSuperclass(classToSwizzle)
- };
- void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
- msgSend(&superInfo, deallocSelector);
- } else {
- originalDealloc(self, deallocSelector);
- }
- };
-
- IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
-
- if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) {
- // The class already contains a method implementation.
- Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
-
- // We need to store original implementation before setting new implementation
- // in case method is called at the time of setting.
- originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
-
- // We need to store original implementation again, in case it just changed.
- originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
- }
- [swizzledClasses() addObject:className];
- }
- }
- @implementation NSObject (RACDeallocating)
- - (RACSignal *)rac_willDeallocSignal {
- RACSignal *signal = objc_getAssociatedObject(self, _cmd);
- if (signal != nil) return signal;
- RACReplaySubject *subject = [RACReplaySubject subject];
- [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
- [subject sendCompleted];
- }]];
- objc_setAssociatedObject(self, _cmd, subject, OBJC_ASSOCIATION_RETAIN);
- return subject;
- }
- - (RACCompoundDisposable *)rac_deallocDisposable {
- @synchronized (self) {
- RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
- if (compoundDisposable != nil) return compoundDisposable;
- swizzleDeallocIfNeeded(self.class);
- compoundDisposable = [RACCompoundDisposable compoundDisposable];
- objc_setAssociatedObject(self, RACObjectCompoundDisposable, compoundDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- return compoundDisposable;
- }
- }
- @end
|