基础概念
内存管理的重要性
- 移动设备的内存有限
- 以下行为会增加app的内存
+ 创建一个OC对象
+ 定义一个变量
+ 调用函数和方法 - 如果app的内存占用过大会导致关闭app影响用户体验
什么是内存管理
- 内存管理的范围
+ 任何即成NSObject的对象
+ 对非对象类型无效 - 只有OC对象需要进行内存管理的本质原因
- OC对象存放于堆里面
- 非OC对象会存放于栈里(会被系统自动释放)
堆和栈
栈(操作系统):由于操作系统自动分配释放,存放函数的参数值,
局部变量的值等,其操作方式类似于数据结构栈
堆 (操作系统):一般由程序员分配释放,若程序员不释放,程序结束时才会释放
引用计数
概念
每个OC对象都拥有自己的引用计数器
是一个整数
可以理解为对象此时被引用的次数也可以理解为有多少人正在使用此对象
作用
当引用计数器为0时,对象所占用的内存会被系统吸收
如果引用计数不为0的时候,那么在整个程序运行的过程中,占用的内存不会被吸收除非整个系统已经退出
任何一个对象被创建引用计数就为 1
操作
引用计数器的常见操作有
retain 计数器+1
release 计数器-1
retainCount 返回计数器的值(但是不一定准确)
内存管理的经验总结
当调用alloc、new、copy、mutableCopy方法返回了一个对象
在不需要这个对象时,要调用release或者autorelease来释放它
想拥有某个对象,就让它的引用计数+1;
不想再拥有某个对象,就让它的引用计数-1
几个熟悉关键字
assign: 简单赋值,不更改索引计数(Reference Counting)。
copy: 建立一个索引计数为1的对象,然后释放旧对象
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
使用assign: 对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等)
使用copy: 对NSString
使用retain: 对其他NSObject和其子类
nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问
MRC
setter 方法
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
@implementation Person
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
//或者
- (void)setName:(NSString *)name {
_name = [name retain];
[_name release];
}
}
- (void)setAge:(int)age {
_age = age;
}
- (void)dealloc {
self.name = nil;
[super dealloc];
}
@end
autorelease
基本概念
1 autorelease是一种支持引用计数的内存管理方式
只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中
2 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0
则该对象依然不会被释放。
autorelease方法会返回对象本身
调用完autorelease方法后,对象的计数器不变
Person *p = [[Person alloc]init];
p = [p autorelease]; //[p retainCount] == 1
autorelease的好处
不用再关心对象释放的时间
不用再关心什么时候调用release
autorelease的原理
autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
自动释放池
iOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 创建自动释放池
[pool release]; // [pool drain]; 销毁自动释放池
iOS 5.0 之后
@autoreleasepool
{ //开始代表创建自动释放池
} //结束代表销毁自动释放池
基本使用
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease];
[autoreleasePool drain];
@autoreleasepool
{ // 创建一个自动释放池
Person *p = [[Person new] autorelease];
} // 销毁自动释放池(会给池子中所有对象发送一条release消息)
注意事项
并不是放到自动释放池代码中,都会自动加入到自动释放池
在自动释放池的外部发送autorelease 不会被加入到自动释放池中
autorelease是一个方法,只有在自动释 放池中调用才有效。
自动释放池中不适宜放占用内存比较大的对象
尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
不要把大量循环操作放到同一个 @autoreleasepool 之间,这样会造成内存峰值的上升
// 内存暴涨
@autoreleasepool {
for (int i = 0; i < 99999; ++i) {
Person *p = [[[Person alloc] init] autorelease];
}
}
// 内存不会暴涨
for (int i = 0; i < 99999; ++i) {
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}
}
循环引用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class Dog;
@interface Person : NSObject
@property (nonatomic,retain) Dog* dog;
@end
#import "Person.h"
#import "Dog.h"
@implementation Person
//- (void)setDog:(Dog *)dog {
// if (_dog != dog) {
//
// [_dog release];
//
// // MRC中需要手动对房间的引用计数器+1
// _dog = [_dog retain];
//
// }
//}
- (void)dealloc
{
NSLog(@"%s",__func__);
[_dog release];
[super dealloc];
}
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class Person;
@interface Dog : NSObject
@property (nonatomic,retain) Person* owner;
@end
#import “Dog.h”
@implementation Dog
- (void)dealloc
{
NSLog(@"%s",func);
[_owner release];
[super dealloc];
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..
Person * p = [[Person alloc] init];//
Dog * d = [[Dog alloc] init];
p.dog = d;
d.owner = p;
[p release];
[d release];
}
return 0;
}
但是程序结束发生了内存泄漏
若想解决循环引用则需要使用week关键字
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class Person;
@interface Dog : NSObject
@property (nonatomic,weak) Person* owner;
@end
`
ARC
基本概念
ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 可以使用该机能。简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(Static Analyzer)工具。
基本使用
基本的ARC使用规则
代码中不能使用retain, release, retain, autorelease
不重载dealloc(如果是释放对象内存以外的处理,是可以重载该函数的,但是不能调用[super dealloc])
不能在C结构体中使用对象指针
id与void *间的如果cast时需要用特定的方法(__bridge关键字)
不能使用NSAutoReleasePool、而需要@autoreleasepool块
ARC的判断准则
默认情况下所有指针为强指针,而只要没有强指针指向对象对象就会释放