1. 内存分配区
-
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
-
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
-
堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
2.循环引用
2.1 block
@interface CeshiViewController ()
//声明一个block
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end
@implementation CeshiViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 这里存在一个循环引用
self.str = @"dealloc";
//解决方法 __weak typeof(self) weakSelf = self; 将下列代码中weakSelf 替换成 self
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"=======%@",self.str);
});
};
self.block();
}
- (void)dealloc {
NSLog(@"CeshiViewController====dealloc");
}
输出结果:
解决了循环引用,但是我们在block中做的 事情没有实现,解决这个问题只需要在block的内部使用__strong来修饰weakSelf 增加医用计数器1 即可
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"=======%@",strongSelf.str);
});
};
输出结果变成:
- block实质上是一个匿名函数,我们在函数内部声明一个属性 == __strong typeof(self) strongSelf = weakSelf;==
当block(函数)执行完毕的时候后,函数内声明的变量就会被回收。 即强引用会消失。然后通知deallo()方法。
2.2 NSTimer
- (void)viewDidLoad {
[super viewDidLoad];
[self TimerTest];
}
- (void)TimerTest {
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(fire) userInfo:nil repeats:YES];
}
- (void)fire {
NSLog(@"fire=============");
}
- 当控制器pop回去的时候,timer还在执行,当前控制器并没有销毁,不是我们希望的
//上面上传timer的方法--》相当于下面这两句
self.timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(fire) userInfo:nil repeats:YES]
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
- RunLoop是一个死循环,只要循环不结束,timer就一直在,而timer又强引用了,self(VC),所有timer不消失,那self就一直没有办法被回收
- (void)dealloc {
NSLog(@"CeshiViewController====dealloc");
//这两句代码并没有任何作用,因为根本不会走这个dealloc()这个析构函数方法,所有没有任何作用
//但是当我们在- (void)viewWillDisappear:(BOOL)animated方法内执行这两句代码,是可以起到作用的,可以关闭定时器,但是我们跳转下级页面时,定时器也停止,这符合我们的需求。
[self.timer invalidate];
self.timer = nil;
}
- 解决办法:
– 第一种解决方法
//在这个方法中 来执行这两句代码
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (parent == nil) {
//parent 就是当前控制器
[self.timer invalidate];
self.timer = nil;
}
}
- 第二种解决方法
不让定时器强引用我们当前的VC, 我们可不可使用消息转发机制来实现
@interface CeshiViewController ()
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) id target;
@end
@implementation CeshiViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self TimerTest];
}
- (void)TimerTest {
//创建target对象
_target = [NSObject new];
class_addMethod([_target class], @selector(fire), (IMP)fireImp, "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:_target selector:@selector(fire) userInfo:nil repeats:YES];
}
//MARK: -- 第二种方法
void fireImp(id self, SEL _cmd) {
NSLog(@"fireImp====fire=============");
}
- (void)fire {
NSLog(@"fire=============");
}
实现原理(不让定时器强引用self): 当前的self是持有 target 和 self.Timer对象的, 而Timer本身出于一个大的循环中,Timer是持有targe 并不持有self,所有当我们pop VC的时候 当前控制器可以正常的执行 析构函数dealloc(), 正常释放,而我们在析构函数中,销毁Timer
-- 第三种解决方法:
是利用NSProxy来做中间桥梁和利用消息转发的机制 来实现
#import "CeshiViewController.h"
#import "objc/runtime.h"
#import "GYProxy.h"
@interface CeshiViewController ()
@property (nonatomic, strong) GYProxy * gyProxy;
@end
@implementation CeshiViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self thridTest];
}
#pragma mark -- 第三种方法
- (void)thridTest {
//只有alloc方法
self.gyProxy = [GYProxy alloc];
self.gyProxy.target = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self.gyProxy selector:@selector(fire) userInfo:nil repeats:YES];
}
// 中间桥梁的实现代码
@interface GYProxy : NSProxy
/// 声明一个target的对象
@property (weak, nonatomic) id target;
@end
#import "GYProxy.h"
#import <objc/runtime.h>
@implementation GYProxy
/// 消息转发机制-》 方法签名
/// @param sel <#sel description#>
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
/// 返回处理方法的对象
/// @param invocation <#invocation description#>
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
输出结果:
3.内存检测
- 静态检测
- 动态检测(Instrument/MLeakFinder)
- 析构方法打印
4.实现例子
- 需求分析: UIViewController 是否释放, pop/push
- 思路分析:
- ViewWillAppear / didDisAppear
- 确定对象是否存活
NSObject+LGSwizzing
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (LGSwizzing)
- (void)willDealloc;
+ (void)swizzleSEL:(SEL)oringSEL withSEL:(SEL)swizzlingSEL;
@end
NS_ASSUME_NONNULL_END
#import "NSObject+LGSwizzing.h"
#import <objc/runtime.h>
@implementation NSObject (LGSwizzing)
+ (void)swizzleSEL:(SEL)oringSEL withSEL:(SEL)swizzlingSEL {
//1.获取到当前的Class
Class class = [self class];
//2.得到原始方法 和需要交换的方法
Method originMethod = class_getInstanceMethod(class, oringSEL);
Method currentMethod = class_getInstanceMethod(class, swizzlingSEL);
//3.判断当前类中需要替换的的方法有没有在当前类中实现
/**
class_addMethod是runtime中的动态添加方法,如果能够添加成功说明本类并没有实现这个要添加的方法,如果不能添加成功说明本类实现这个方法。实现方法包括新增的方法和重写父类方法。
*/
BOOL didAddMethod = class_addMethod(class, oringSEL, method_getImplementation(currentMethod), method_getTypeEncoding(currentMethod));
if (didAddMethod) {
//代表当前类中被替换的方法已经实现, 那就开始替换
//调用了class_replaceMethod会把原来方法的IMP指向另一个方法的实现
class_replaceMethod(class, swizzlingSEL, method_getImplementation(currentMethod), method_getTypeEncoding(currentMethod));
} else {
//如果没有实现 则替换方法
method_exchangeImplementations(originMethod, currentMethod);
}
}
- (void)willDealloc {
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//在block中引用
__strong typeof(self) strongSelf = weakSelf;
NSLog(@"lg_WillDealloc===============%@",strongSelf);
});
}
@end
UINavigationController+LGLeaksTest
#import "UINavigationController+LGLeaksTest.h"
#import "NSObject+LGSwizzing.h"
#import <objc/runtime.h>
@implementation UINavigationController (LGLeaksTest)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//交换方法--》 pop方法
[self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(lg_popViewControllerAnimated:)];
});
}
- (UIViewController *)lg_popViewControllerAnimated:(BOOL)animated {
UIViewController *popVC = [self lg_popViewControllerAnimated:animated];//pop的出去的VC
//设置popvc出去的属性状态
extern const char * LGVCFLAT;//声明
objc_setAssociatedObject(popVC, LGVCFLAT, @(YES), OBJC_ASSOCIATION_ASSIGN);
return popVC;
}
UIViewController+LGLeakTest
#import "UIViewController+LGLeakTest.h"
#import <objc/runtime.h>
#import "NSObject+LGSwizzing.h"
const char * LGVCFLAT = "LGVCFLAT";
@implementation UIViewController (LGLeakTest)
/**
1.交换方法
*/
+ (void)load {
//为了防止手动调用,出现又把方法交换回来的结果
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//交换两个方法
[self swizzleSEL:@selector(viewWillAppear:) withSEL:@selector(lg_viewWillAppear:)];
[self swizzleSEL:@selector(viewDidDisappear:) withSEL:@selector(lg_viewWillDisAppear:)];
});
}
- (void)lg_viewWillAppear:(BOOL)animate {
[self lg_viewWillAppear:animate];
//怎么知道VC是pop状态还是push状态, 可以使用runtime添加一个属性来记录
objc_setAssociatedObject(self, LGVCFLAT, @(NO), OBJC_ASSOCIATION_ASSIGN);
}
- (void)lg_viewWillDisAppear:(BOOL)animate {
[self lg_viewWillDisAppear:animate];
if ([objc_getAssociatedObject(self, LGVCFLAT) boolValue]) {
// 如果为YES则表示 pop的状态--》 怎么知道vc的pop的状态
[self willDealloc];
}
}