内存管理-MRC

0、概念:

持有与引用:  
  对象的持有和引用的区别,持有必定引用,引用不一定持有,引用只是保存了对象所在的内存空间地址,系统可以随时释放该内存空间,给其他它程序用;持有则是,拥有这个内存空间的所有权,系统无权释放。例如由房产证的的人和没有房产证的人,它们都知道房子的地址(引用了该地址),但是有房产证的人,政府(操作系统)无权力收回该房子,无房产证的人,政府有权力收回该房子。

释放与废弃:
  释放对象表示指针放弃对对象的持有权,即MRC下调用release,ARC下指针置为nil;废弃对象表示系统收回对象所占用的内存空间。

1、OC下的内存管理原则:

  • 存放在栈区和常量区的数据所占用的内存,由系统负责回收。
  • 存放在堆区的数据所占用的内存,由用户自己负责回收。如果不会回收就会造成内存的泄漏。
  • 一般内存的泄漏,指的是堆区中的内存没有被释放。
  • 内存的管理通过引用计数器(记录还有多少引用持有该对象)来管理的,对象的释放并不是自动的,如果是自动的话那叫垃圾回收,使用过release方法来回收。

2、release、alloc、retain、autorelease方法介绍

release方法中的逻辑
-(void)release{
  if(对象的引用计数器 == 0){
    释放内存空间;
  }else{
    对象引用计数器--;
  }
}

3、MRC规则

  • 自己创建的对象自己释放,即使用alloc、new、copy、mutableCopy方法创建的对象,对应的引用持有该对象,需要自己调用release释放。
  • 以alloc、new、copy、mutableCopy开头的方法(allocPerson、newPerson)创建的对象,对应的引用也持有该对象,需要自己调用release释放。
  • 不是以上述两种方式创建的对象,对应的引用不持有该对象,不需要自己调用release释放,想要使用,需要先调用retain。(例如NSArray的array方法)。
  • 对于使用第三条创建的对象,需要使用autoReleasePool(自动释放池)来释放该对象。
  • 如果想要使用该对象,首先要持有它,即调用retain方法。
  • 如果已经持有该对象的引用,不想要再使用了,需要调用release方法取消对对象的持有权。
--------------------Person类--------------------------------
@interface Person : NSObject
+(instancetype)person;
@end

@implementation Person

+(instancetype)person
{
    // 将创建的对象加入到栈顶的自动释放池中。若没有自动释放池,则该对象无法释放,导致内存泄漏。
    return [[[Person alloc] init] autorelease];
}
@end

--------------------Man类--------------------------------
@interface Man : NSObject
+(instancetype)allocMan;
@end

@implementation Man

+(instancetype) allocMan
{
    return [[Man alloc] init];
}
@end

--------------------main类--------------------------------
#import "Person.h"
#import "Man.h"
int main(int argc, const char * argv[]) {
    /*
      *没有使用alloc、new、copy、mutableCopy开头的方法创建的对象。
      *指针str1、str2、str3不会持有这三个对象,即不需要p调用release方法释放该对象,即使调用了release也释放不了。
      *下面的代码中,虽然每个对象调用了release方法,但是内存还是暴涨,对象没有释放掉。
    */
    for (int i = 0; i<999999; i++) {
        Person *str1 = [Person person];
        Person *str2 = [Person person];
        Person *str3 = [Person person];
        NSLog(@"%@===%@===%@", str1, str2, str3);
        [str1 release];
        [str2 release];
        [str3 release];
    }

    // 使用自动释放池,每次循环开始创建一个自动释放池,每次循环结束,自动释放池销毁,并向str1、str2、str3三个对象发送release方法。内存不暴涨了。
    for (int i = 0; i<999999; i++) {
        @autoreleasepool{
            Person *str1 = [Person person];
            Person *str2 = [Person person];
            Person *str3 = [Person person];
        // str1、str2、str3三个引用没有持有这三个对象,想要使用该对象,需要先持有。
        [str1 retain];
        [str2 retain];
        [str3 retain];
            NSLog(@"%@===%@===%@", str1, str2, str3);
        }
    }    

    // 使用alloc、new、copy、mutableCopy方法创建的对象,str1、str2、str3持有各自的对象,调用release,会释放创建的三个对象对应的内存空间,内存不暴涨。
    for (int i = 0; i<999999; i++) {
        Person *str1 = [[Person alloc] init];
        Person *str2 = [[Person alloc] init];
        Person *str3 = [[Person alloc] init];
        NSLog(@"%@===%@===%@", str1, str2, str3);
        [str1 release];
        [str2 release];
        [str3 release];
    }

    // 使用以alloc、new、copy、mutableCopy单词开头的方法创建的对象,str1、str2、str3持有各自的对象,调用release,会释放创建的三个对象对应的内存空间,内存不暴涨。
    for (int i = 0; i<999999; i++) {
        Man *str1 = [Man allocMan];
        Man *str2 = [Man allocMan];
        Man *str3 = [Man allocMan];
        NSLog(@"%@===%@===%@", str1, str2, str3);
        [str1 release];
        [str2 release];
        [str3 release];
    }
}           

总结:

  • 由此可见,在对象没有其它指针引用的情况下,内存的释不释放,关键看创建对象时,是否调用了autorelease方法,如果调用了该方法,则需要使用自动释放池调用release方法才能释放内存空间。
  • Person *p = [[[Person alloc] init] autorelease]; p指针没有持有该对象,栈顶的自动释放池持有该对象,当自动释放池销毁时,会向所有池中的对象发送release方法,如果没有使用自动释放池包裹,会发生内存泄漏。

4、MRC机制下的代码写法

int main(int argc, const char * argv[]) {
     // 使用alloc、new、copy、mutableCopy方法创建的对象,引用p已经持有该对象,无需调用retain。
     Person *p = [[Person alloc] init];
     NSLog("%@", p);

     // 不是用alloc、new、copy、mutableCopy方法创建的对象,引用p没有持有该对象,需要调用retain,才能使用。
     Person *p1 = [Person person];
     [p1 retain];
     NSLog("%@", p1);
}

-------------------------Person类------------------

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
+(instancetype)person;
+(instancetype)allocPerson;
@end

@implementation Person

// 方法名没有使用alloc、new、copy、mutableCopy开头的方法,创建对象时需要调用autorelease。
+(instancetype)person
{
    return [[[Person alloc] init] autorelease];
}

// 方法名使用alloc、new、copy、mutableCopy开头的方法,创建对象时不需要调用autorelease。
+(instancetype)allocPerson
{
    return [[Person alloc] init];
}
// 两种方式创建对象的区别在于有无调用autorelease,即是否需要使用自动释放池来调用一次release方法。

// name的set方法,取消属性当前引用对象的持有权,添加新对象的持有权。
- (void)setName:(NSString *)name
{
    if (_name) {
        [_name release];
    }
    _name = name;
    [_name retain];
}
    
原文地址:https://www.cnblogs.com/Zp3sss/p/8879924.html