手动内存管理MRC
首先介绍一下引用计数器:用来保存当前对象有几个东西在使用它(数字)
引用计数器的作用:用来判断对象是否应该回收内存空间(如果对象不等于nil,当引用计数器为0,此时要回收对象的内存空间)
引用计数器的操作:
-
retain 使得引用计数器+1
-
release 使的引用计数器-1
-
retainCount 得到引用计数器的值
如果一个对象被释放的时候,会调用该对象的dealloc方法
注意:
- dealloc方法是NSObject 的,一般我们要重写dealloc方法
- 在dealloc 方法的内部,要调用 [super dealloc];
内存管理的范围:
-
所有的集成了NSObject的对象的内存管理
-
基本数据类型(int double float char struct enum )的数据内存不需要我们进行管理
内存管理的原则:
-
如果对象有人使用,就不应该回收
如果你想使用这个对象,应该让这个对象 retain一次
如果你不想使用这个对象了,应该让这个对象 relase一次
-
谁创建 谁release
-
谁 retain 谁 release
内存管理研究的内容:
- 野指针:
1)定义的指针变量没有初始化 2)指向的空间已经被释放
-
内存泄露:
{ Person *p = [Person new]; } /* p 栈区 [Person new]; 堆区 如果栈区的p已经释放了,而堆区的空间还没有释放,堆区的空间就被泄露了 */
set方法内存管理
// Dog* _dog; // 对于对象作为另外一个类的实例变量 - (void)setDog:(Dog*)dog { // 判断对象是否是原对象 if(_dog != dog) { //2) release旧值 [_dog release]; // retain 新的值,并且赋值给实例变量 _dog = [dog retain]; } }
循环retain问题
循环的retain 会导致两个对象都会内存泄露
防止方法:
-
让某个对象多释放一次 (注意顺序)
-
一端使用 assign 一端使用retain(推荐使用)
NSString类的内存管理问题
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { //定义字符串 //字符串的常量池, //如果你需要的字符串在常量池中已经存在了,不会分配内存空间 //使用字符串的时候, // @"abc" stringWithString alloc initWithString 都在常量区/ //0x100001030 小 NSString *str1 = @"abc"; //@"abc" 字符串的常量 NSString *str3 = [NSString stringWithString:@"abc"]; //常量区 NSString *str5 = [[NSString alloc] initWithString:@"abc"]; //也在常量区 NSString *str6 = [[NSString alloc] init];//常量区 str6 = @"abc"; //0x100202030 大 //如果在常量区 str2 str4 地址应该是一样的 //实际上不一样的,所以 str2 str4都在堆区 NSString *str2 = [NSString stringWithFormat:@"abc"]; //不是在栈区,在堆区 NSString *str4 = [[NSString alloc] initWithFormat:@"abc"];//不是在栈区,在堆区 //0x7fff5fbff764 int a = 10; //栈区 NSLog(@"str1 = %@,%p,%lu",str1,str1,str1.retainCount); NSLog(@"str2 = %@,%p,%lu",str2,str2,str2.retainCount); NSLog(@"str3 = %@,%p,%lu",str3,str3,str3.retainCount); NSLog(@"str4 = %@,%p,%lu",str4,str4,str4.retainCount); NSLog(@"str5 = %@,%p,%lu",str5,str5,str5.retainCount); NSLog(@"str6 = %@,%p,%lu",str6,str6,str6.retainCount); NSLog(@"a = %p",&a); } return 0; }
自动释放池 :特殊的栈结构
特点:
- 对象可以加入到自动释放池中
- 自动释放池结束的时候,会给池中的对象发送一条 release消息
自动释放池的使用:
- 创建自动释放池
@autoreleasepool {
}
2. 加入自动释放池
/* 在自动释放池中 [对象 autorelease]; */
模拟一个Person类 类中有个一个对象方法- (void)run;
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { //1 创建自动释放池 Person *p = [Person new]; // p 1 @autoreleasepool {//自动释放池开始 [p run]; NSLog(@"%lu",p.retainCount); // 1 // [p autorelease] 把对象p加入到自动释放池中 // 注意:加入到自动释放池中以后, 引用计数不会变化 [p autorelease]; //加入自动释放池, NSLog(@"%lu",p.retainCount); // 1 [p run]; }//自动释放池结束 [p release]; [p run]; return 0; }
我们可以给Person添加一个类方法,让其创建完对象就加入到自动释放池中
+(instancetype)person{ //Person person ---> Person //Stduent person ----> Student //创建对象 return [[[self alloc] init] autorelease]; // 返回的时对象的空间 // 能够帮我们把对象给加入到自动释放池 }