内存管理初级

来源:互联网 发布:hmcl启动器 linux 编辑:程序博客网 时间:2024/06/05 16:29
复习C语言中的内存:

int *p = malloc(8); —>堆内存
free(p);
free(p); —>过度释放

  • 内存溢出: 内存不释放
  • 野指针: free之后还可以用p找到那块内存, 因为是标记删除; 但内存中内容不能保证还是原来的, 因为所有权已还给系统

野指针异常是程序crash主要原因

将ARC改为MRC:


  • ARC Automatic Reference Counting 自动引用计数
  • MRC Manual Reference Counting 手动引用计数
  • 关闭ARC就是MRC
  • ARC是基于MRC的

内存管理:

1. - retainCount: 打印当前对象的引用计数, 结果只能作为一个参考

2. + alloc: 开辟内存空间,让被开辟的内存空间引用计数变为1, 这是由0到1的过程
Person*person1 = [[Personalloc]init];
此时 person1 引用计数由0变为1 
NSLog(@"%lu", [person1retainCount]);
打印结果: 1

3. - retain: 引用计数+1
[person1retain];
[person1retain];
person1 引用计数两次 +1
NSLog(@"%lu", [person1retainCount]);
打印结果:  3

4. - release: 引用计数-1, 而不是释放
[person1release];
person1引用计数 -1

NSLog(@"%lu", [person1retainCount]);
打印结果:  2

[person1release];
NSLog(@"%lu", [person1retainCount]);
打印结果:  1

[person1 release];
NSLog(@"%lu", [person1retainCount]);
打印结果:  1(*为何是1而不是0?)

[person1release];
过多的释放,会导致crash

内存的所有权:
所有权是管理内存的根本性的问题, 一个指针能不能对内存进行release, 就看有没有所有权.
只有开辟空间的对象才拥有内存的所有权.

5. - autorelease:自动释放池 (@autoreleasepool {}) 时-1
Person*person2 = [[Personalloc]init];
@autoreleasepool{
           
     [person2 retain];
     [person2retain];
     [person2release];    
     [person2 autorelease];
     [person2autorelease];
//     [person2 autorelease]; —>同样会造成过度释放
     NSLog(@"%lu", [person2retainCount]); 
     打印结果: 2
} —> 在出释放池,运行释放池后第一个语句之前会执行dealloc
NSLog(@"%lu", [person2retainCount]);
打印结果:  1

6. - dealloc: 释放内存, 当对象的引用计数变为0的时候, 系统会自动调用dealloc方法, 可以重写父类的dealloc方法, 重写时不用声明,不能手动调用.
在类中重写dealloc方法:
- (void)dealloc{
    [_namerelease]; —> 将由成员变量指向的内存空间的引用计数-1. 基本数据类型的成员变量不需要release, 因为在栈区, 由系统自动管理
    [_sexrelease];
    [superdealloc];
}
调用父类的dealloc方法,释放公共部分的属性和成员变量,[superdealloc]; 作为方法的最后一句.

7. - copy: 会开辟一段内存空间, 和原来大小一致, 存储内容一致, 会保持原来的引用计数不变,新的内存引用计数变为1. 遵循NSCopying协议才能使用copy.
想要使自己创建的Person类使用copy方法:
1. 签订NSCopying协议:  @interface Person : NSObject<NSCopying>
2. 在.h文件中实现NSCopying协议中声明的方法:  - (id)copyWithZone:(NSZone*)zone;
- (id)copyWithZone:(NSZone*)zone{
    NSZone, 内存池, 里面的内存都是没用过的
        
    Person *p = [[PersonallocWithZone:zone]init];
        使用拷贝区zone, 创建一个新的对象, 相当于开辟空间, 并初始化
   
    p.name= self.name;
    p.sex =self.sex;
        把对象自己的属性内容给新的对象拷贝一份
    return p;
}
3. 使用copy方法:  Person *person8 = [person7 copy];

*关于release方法的实现:
系统不让重写release方法, 此处仅为演示:

//- (void)release{
//   
//    if (self.retainCount == 1) { 
////        self.retainCount—; —>当引用计数为1的时候调用release,目的为将内存还给系统,所以没有必要再-1了,直接释放内存即可
//        [self dealloc];
//    }else{
//        self.retainCount--;
//    }
//}

内存管理的原则:
  • 当看到alloc, retain, copy时, 要写release, 要保证引用计数的加减平衡
  • 如果但不到, 则不管

说明: 
引用计数的变化针对的是内存空间,而不是指针变量.
Person *person001 = [[Personalloc] init];
NSLog(@"%lu", [person001retainCount]); —> 打印结果:1
[person001retain];
[person001retain];
NSLog(@"%lu", [person001retainCount]); —> 打印结果:3

Person*person002 = person001; 
NSLog(@"%lu", [person002retainCount]);—> 打印结果: 3
此时person002 与 person001 指的是同一块内存空间

[person002 release];
NSLog(@"%lu", [person001retainCount]); —> 打印结果: 2
当对person2进行release操作时, 使用person001调用retainCount打印出的结果同样会-1


数组的内存管理:
Person*person3 = [[Personalloc]init];
NSMutableArray*arr = [NSMutableArrayarray];

[arr addObject:person3];
NSLog(@"%lu, %lu", [arrretainCount], [person3retainCount]);
打印结果: 1, 2
      
[arr removeObject:person3];
NSLog(@"%lu", [person3retainCount]);
打印结果: 1

被加入到数组中的元素会被retain一次.
当元素被移除的时候,元素会被release一次.
0 0