内存问题:
1、应用的内存使用超过了单个进程的上限,应用就会被操作系统终止
2、90%的应用崩溃与内存管理有关,包括错误的内存访问和循环引用引起的内存泄露
3、Objective-C和swift运行时是用引用计数,Java和Koltin基于垃圾回收。如果开发过程对引用计数不够小心,会导致内存重复释放或者循环引用(没有及时释放)的问题
应用的内存消耗(RAM)包括栈和堆的内存消耗,内存优化的两个方向是对栈大小和堆大小的使用优化
栈大小:
1、应用中每个线程有独立的栈空间
2、线程的栈空间很小,递归调用的方法数、一个方法的全部变量都会压栈占用栈帧,递归过深和方法过多会导致栈溢出;视图渲染的时候对视图层级树递归调用layoutSubviews和drawRect方法,视图层级树过深也会导致栈溢出
堆大小:
1、一个进程所有线程共享一个堆空间,分配给应用的堆大小由操作系统分配
2、使用String、图片、JSON/XML、视图会消耗大量堆内存
3、类创建的对象的实例变量(iVars)都放在堆中
4、对象创建并被赋值时,数据可能会从栈赋值到堆;方法中使用对象的成员变量时,数据可能会从堆赋值到栈,例如下面的例子
@inteface Photo
@property (nonatomic, assign) NSInteger tag;
@property (nonatomic, copy) NSString *url;
@end
- (Photo)createPhotoWithTag:(NSInteger)tag url:(NSString *)url {
Photo *p = [[Photo alloc] init]; //p在堆上创建
p.tag = tag; //从tag复制到堆
p.url = url; //url标记成copy, 可能从栈复制到堆,这取决于copyWithZone:的实现
}
- (void)calTagTotal:(NSArray *)items {
NSInteger total = 0;
NSMutableString *combineURL = [NSMutableString string];
for (Photo *obj in items) {
total += obj.tag; //从堆复制到栈
[combineURL appendString:obj.url];
}
}
内存管理模型:
1、应用在运行时内存是如何管理的
2、内存管理模型基于引用计数,就是这个对象被持有的次数
3、引用计数不为0占用的内存不会被回收,为0则会被回收
4、一个对象在方法内部创建,这个方法就持有该对象;如果对象从方法返回,则调用者持有这个对象;这个值可以赋值给其他变量,那么其他变量也持有了这个对象
5、于某个对象相关的任务全部完成,就是放弃了持有关系
6、持有对象retain,释放对象release
自动释放对象和自动释放池:
1、autorelease可以放弃对象持有关系,同时延迟对象的销毁
2、autoreleasepool代码块中的所有对象会在代码块执行完时收到autorelease消息,每个autorelease消息会调用一个release消息
3、main函数、runloop自动创建了autoreleasepool,通常不需要手动创建autoreleasepool
4、需要手动autoreleasepool的可能场景:存在生成多个临时变量的循环;在自定义的子线程中;可以有效防止内存均值过高
ARC:
1、2011年WWDC发布的一种编译器特性
2、Objc支持MRC和ARC,Xcode默认开启ARC;Swift不支持MRC
3、项目设定:Building Settings->Apple Clang-Language- Objective-C->Objective-C Automatic Reference Counting
4、文件设定:Building Phase->Compiler Sources->Compiler Flags->-fno-obj-arc/-f
ARC规则:
1、不能显示调用retain\release\autorelease\retainCount,包括[obj retain]和@selector(retain)都会带来编译错误
2、只能实现dealloc方法,但不能显示调用它
3、不能使用NSAuatoreleasePool, 需要使用@autorelease {..}
4、不能使用NSZone
5、id类型和void *只能显示转行而不能自动转行
6、支持使用MRC和ARC混合使用
ARC变量限定符:
指明了变量和被引用对象的持有关系和生命周期
1、__strong默认限定符,变量将长时间驻留内存,等同retain
2、__weak,不保存被引用对象的存活,当没有强引用指向对象时,弱引用会被置为nil
3、__unsafe_unretained,与weak相同不保存被引用对象的存活,但不会被自动设置成nil
4、__autorelease延长被引用对象的生命到本方法调用结束
Person * __strong p1 = [[Person] alloc] init]; //对象引用计数1 在p1引用期间不会被释放
Person * __weak p2 = [[Person] alloc] init]; //对象引用计数0 会被立即释放 p2=nil
Person * __unsafe_unretained p3 = [[Person] alloc] init]; //对象引用计数0 会被立即释放 p3不会置为nil
Person * __autorelease p4 = [[Person] alloc] init]; //对象引用计数1 当前方法返回时对象被立即释放
循环引用场景:
1、委托。被委托对象强引用,委托对象弱引用
2、块。块被外部对象持有,块内部又捕获了外部对象。
3、线程与计时器
//委托导致循环引用的例子:
@interface ViewController: UIViewController
@property (strong) UpdateOperation *updateOp;
@end
@interface UpdateOperation: NSObject
- (void)setDelegate:(id)del withSelector:(SEL)sel;
@end
@implementation ViewController
- (IBAction)onRefreshClick:(id)sender {
[self.updateOp setDelegate:self withSelector:@selector(refreshData:)];
}
- (void)refreshData {
///
}
@end
@implementation UpdateOperation
- (void)setDelegate:(id)del withSelector:(SEL)sel {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if ([del isResponseToSelector:sel]) {
[del perfomanceSelector:sel withObject:nil];
}
});
});
}
@end
//解决循环引用
@interface ViewController: UIViewController
@property (strong) UpdateOperation *updateOp;
@end
@interface UpdateOperation: NSObject
@property (weak) id<UpdateOperationDelegate> delegate;
- (void)setDelegate:(id)del withSelector:(SEL)sel;
@end
@implementation ViewController
- (IBAction)onRefreshClick:(id)sender {
[self.updateOp setDelegate:self withSelector:@selector(refreshData:)];
}
- (void)refreshData {
///
}
@end
@implementation UpdateOperation
- (void)setDelegate:(id)del withSelector:(SEL)sel {
self.delegate = del;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
__strong id<UpdateOperationDelegate> del = self.delegate;//块中防止对象被释放,需要等块执行完才释放使用强引用这个对象
if ([del isResponseToSelector:sel]) {
[del perfomanceSelector:sel withObject:nil];
}
});
});
}
@end
//block捕获外部变量导致循环引用
- (void)doSomething {
ViewController *vc = [[ViewController alloc] init];
[self presentViewController:vc animated:YES completion:^{
self.data = vc.data;//blk捕获了父视图控制器 而父视图控制器持有了blk
[self dismissViewControllerAnimated:YES completion:nil];
}];
}
//解决循环引用
- (void)doSomething {
ViewController *vc = [[ViewController alloc] init];
__weak typeof(self) weakSelf = self;//弱引用防止blk持有父视图控制器的强引用
[self presentViewController:vc animated:YES completion:^{
__strong typeof(self) sself = weakSelf; //blk中防止父视图控制器被释放需要强引用weakSelf
sself.data = vc.data;
[sself dismissViewControllerAnimated:YES completion:nil];
}];