zoukankan      html  css  js  c++  java
  • 内存管理-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];
    }
        
  • 相关阅读:
    2.1 CDN Bypass
    4.1 SQL Injection
    ztosec/secscan-authcheck 安装部署教程
    浏览器的内部构造
    左右值的概念
    网络安全学习路线
    Python+Typora博客图片上传
    微信欢乐五子棋辅助
    [SUCTF 2019]EasySQL
    [护网杯 2018]easy_tornado (SSTI)
  • 原文地址:https://www.cnblogs.com/Zp3sss/p/8879924.html
Copyright © 2011-2022 走看看