1:创建工程的目录结构
1.1工程的目录结构
1.2 工程思维导图
2:原理解释
MLFinder的原理就是在viewcontroller返回时候(pop)检测当前的viewcontroller是否还有能力调用指定的willDealloc的方法。
总结成一句话:利用runtime发送消息的时候,receiver可以为nil,如[nil msg]。不会奔溃。
3:运行流程
4:源码如下
4.1 NSObject+MLFinder.m
#import "NSObject+MLFinder.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
const void *const kLatestSenderKey = &kLatestSenderKey;
@implementation NSObject (MLFinder)
+ (void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL {
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSEL);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSEL);
BOOL didAddMethod =
class_addMethod(class,
originalSEL,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSEL,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (BOOL)willDealloc {
__weak id weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
__strong id strongSelf = weakSelf;
[strongSelf assertNotDealloc];
});
return YES;
}
- (void)assertNotDealloc {
NSLog(@"\n\n================assertNotDeallocassertNotDealloc================\n\n");
}
@end
4.2 UINavigationController+MLFinder.m
#import "UINavigationController+MLFinder.h"
#import "NSObject+MLFinder.h"
#import <objc/runtime.h>
static const void *const kPoppedDetailVCKey = &kPoppedDetailVCKey;
@implementation UINavigationController (MLFinder)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(swizzled_popViewControllerAnimated:)];
});
}
- (UIViewController *)swizzled_popViewControllerAnimated:(BOOL)animated {
UIViewController *poppedViewController = [self swizzled_popViewControllerAnimated:animated];
if (!poppedViewController) {
return nil;
}
// // Detail VC in UISplitViewController is not dealloced until another detail VC is shown
// if (self.splitViewController &&
// self.splitViewController.viewControllers.firstObject == self &&
// self.splitViewController == poppedViewController.splitViewController) {
// objc_setAssociatedObject(self, kPoppedDetailVCKey, poppedViewController, OBJC_ASSOCIATION_RETAIN);
// return poppedViewController;
// }
// VC is not dealloced until disappear when popped using a left-edge swipe gesture
extern const void *const kHasBeenPoppedKey;
objc_setAssociatedObject(poppedViewController, kHasBeenPoppedKey, @(YES), OBJC_ASSOCIATION_RETAIN);
return poppedViewController;
}
@end
4.3 UIViewController+MLFinder.m
#import "UIViewController+MLFinder.h"
#import "NSObject+MLFinder.h"
#import <objc/runtime.h>
const void *const kHasBeenPoppedKey = &kHasBeenPoppedKey;
@implementation UIViewController (MLFinder)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleSEL:@selector(viewDidDisappear:) withSEL:@selector(swizzled_viewDidDisappear:)];
[self swizzleSEL:@selector(viewWillAppear:) withSEL:@selector(swizzled_viewWillAppear:)];
// [self swizzleSEL:@selector(dismissViewControllerAnimated:completion:) withSEL:@selector(swizzled_dismissViewControllerAnimated:completion:)];
});
}
- (void)swizzled_viewDidDisappear:(BOOL)animated {
[self swizzled_viewDidDisappear:animated];
BOOL hasBeenPop = [objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue];
if (hasBeenPop) {
[self willDealloc];
}
}
- (void)swizzled_viewWillAppear:(BOOL)animated {
[self swizzled_viewWillAppear:animated];
BOOL hasBeenPop = [objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue];
// NSLog(@"----%d",hasBeenPop);
objc_setAssociatedObject(self, kHasBeenPoppedKey, @(NO), OBJC_ASSOCIATION_RETAIN);
}
@end
5:模拟代码
#import "TimerViewController.h"
@interface TimerViewController ()
@property (nonatomic, strong) NSTimer * timer;
@end
@implementation TimerViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"timer";
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [self timerClick];
// });
// self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];
// [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
__weak typeof(self) ws = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
[ws timerClick];
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerClick {
NSLog(@"%s",__func__);
}
//- (void)viewWillDisappear:(BOOL)animated {
// if (self.timer != nil) {
// [_timer invalidate];
// _timer = nil;
// }
//}
-(void)dealloc {
NSLog(@"%s",__func__);
[_timer invalidate];
_timer = nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"---TimerViewController:viewWillAppear:%@",self);
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSLog(@"---TimerViewController:viewDidDisappear:%@",self);
}
@end