ios内存管理

    xiaoxiao2021-03-25  111

    内存管理

    1 内存管理的基本概念

    1.1 Objective-C中的内存管理

    手动内存管理和自动释放池---MRC>(Mannul Reference Counting)自动内存管理---ARC>(Automatic Reference Count)自动垃圾回收---GC>(Garbage Collection) 由于iOS系统不支持垃圾回收,所以我们在iOS开发中只能使用MRC和ARC来进行内存管理,本文不再介绍Objective-C中的垃圾回收机制,但是此处注意Objective-C中是存在垃圾回收机制的

    1.2 内存管理中存在的问题

    内存泄露:不再需要的对象没有释放

    引起的问题:程序的内存占有量不断增加,最终会被耗尽导致程序崩溃

    野指针:没有进行初始化得指针

    引起的问题:浪费内存资源,如果调用程序会出现未知的结果,甚至导致程序崩溃

    空指针:一个指针指向一个被销毁的对象

    引起的问题:调用悬空指针指向的属性或者方法时,程序会出现未知的结果,甚至导致程序崩溃

    僵尸对象:过度释放的对象

    引起的问题:指向僵尸对象的指针成为野指针,给野指针发送消息会报EXC_BAD_ACCESS 错误

    2.手动内存管理和自动释放池---MRC>(Mannul Reference Counting)

    2.1 什么是引用计数(Reference Counting)

    引用计数:Objective-C中引入了引用计数这一机制来跟踪并处理对象的生命周期,

    管理方式:每个对象都有一个与之关联的整数,这个整数被称为引用计数,在Objective-C中,通过不同的方法可以对引用计数进行操作,具体的处理如下表:

    对象操作Objective-C方法对应的操作结果生成并持有对象alloc, new, copy,mutableCopy等方法生成对象并设置引用计数 =1持有对象reatain方法使引用计数 +1释放对象release方法使引用计数 -1废弃对象dealloc方法---系统自动调用引用计数 =0 时调用

    关于delloc方法:dealloc方法继承自NSObject,因此所有的对象都具有此方法,当一个对象的引用计数为0时,也就意味着没有任何程序需要此对象,系统会回收该对象所占用的内存,在系统销毁对象之前,会自动调用该对象的dealloc方法来执行一些回收操作,如果该对象还持有其他对象的引用,我们必须重写dealloc方法来释放该对象引用的其他对象(通常就是使用该对象的release方法)

    引用计数机制回收对象的说明:如果一个对象的引用计数为0,则表明程序已经不再需要它,这时系统会自动回收该对象所占内存,相反,如果一个对象的引用计数不为0,系统就不应该回收,也不会回收它所占的内存

    关于retainCount方法:Objective-C提供了retainCount方法来返回一个对象当前的引用计数

    如何重写dealloc方法:

    - (void)dealloc { // 处理该对象的其他引用(通过release方法) /** 回调父类的dealloc方法 */ [super dealloc]; }

    2.2 苹果如何管理引用计数

    2.2.1 因为NSObject类的源代码没有公开,我们利用Xcode的调试器(lldb)和iOS大概追溯出其实现过程

    alloc

    +alloc +allocWithZone: class_createInstance //此方法可以通过objc4中的runtime/objc-runtime-new.mm确认 calloc // 分配内存块

    retainCount

    -retainCount __CFDoExternRefOperation // 此函数根据retain,retainCount,release操作进行分发,调用__CFBasicHashXXX方法 CFBasicHashGetCountOfKey

    retain

    -retain __CFDoExternRefOperation CFBasicHashAddValue

    release

    -release __CFDoExternRefOperation CFBasicHashRemoveValue // 当此函数返回0时, -release调用dealloc方法 2.2.2 由__CFDoExternRefOperation函数以及此函数的调用关系,我们大概推算苹果大概是使用散列表(引用计数表)来管理引用计数 通过引用计数表来管理引用计数的好处: 对象用内存块的分配无须考虑内存块头部引用计数表各记录中存有内存块的地址,可从各个记录追溯到各对象的内存块(在进行内存泄露的检查时,此条特性具有举足轻重的作用,即使出现故障导致对象占用的内存块损坏,但是只要引用计数表没有被破坏,我们就可以确定各内存块的位置,这就是设置全局断点可以查出哪里出现内存泄露的原因)

    2.3 内存管理的思考方式

    自己生成的对象,自己持有

    1.1 使用alloc new copy mutableCopy创建的对象只能自己持有

    id obj1 = [[NSObject alloc] init]; id obj2 = [NSObject new]; id obj3 = [NSObject copy]; id obj4 = [NSObject mutableCopy];

    1.2 使用以上名称的开头的方法也意味着自己生成并持有对象

    allocNewObject

    newNewObject

    copyNewObject

    mutableCopyNewObject

    非自己生成的对象,自己也能持有

    2.1 非alloc new copy mutableCopy生成的对象,变量obj本身不持有该对象

    id obj1 = [NSMutableArray array]; id obj2 = [NSDictionary dictionary];

    2.2 通过retain方法,非通过alloc new copy mutableCopy生成的对象,可以成为自己持有的对象

    id obj = [NSMutableArray array]; [obj retain];

    不再需要自己持有的对象时释放

    3.1 释放通过alloc new copy mutableCopy生成的对象,一旦不在需要,务必要使用release方法释放

    ``` id obj = [[NSObject alloc] init];

    [obj release]; ```

    3.2 用retain方法持有的非自己生成的对象,一旦不再需要,也一定要使用release释放

    id obj = [NSMutableArray array]; [obj retain]; // 通过retain方法持有对象 [obj release]; // 在不需要时也要通过release方法释放对象

    3.3 用某个方法生成对象,并将其作为方法的返回值,这时我们该如何处理

    3.3.1 通过alloc new copy mutableCopy 或其他符合命名规则的方法生成的对象,只需要原封不动的返回就能让调用方也持有该对象

    ```

    (id)allocObject { id obj = [[NSObject alloc] init]; return obj; }

    (id)allocObjectWithObject:(id)obj { id object = [obj allocObject]; return object; } ```

    3.3.2 如果持有非自己生成的对象,例如[NSMutableArray array]生成的对象,我们要使用autorelease方法释放

    注:命名规则:用来取得谁都不持有的对象的方法名不能以alloc new copy mutableCopy开头

    - (id)object { id obj = [NSMutableArray array]; [obj autorelease]; return obj; }

    3.3.3 autorelease方法:提供了这样的功能,使对象在超出指定的生存范围时自动并正确释放(调用release方法)

    非自己持有的对象无法释放---注意以下两点,如果发生这样的情况会导致程序崩溃

    4.1 通过alloc new copy mutableCopy方法或者通过retain方法持有的对象,一旦不再需要时,必须进行释放,除此之外其他方法获得的对象绝对不能释放,一旦释放会造成程序崩溃

    4.2 自己持有的对象释放后再次释放,造成僵死对象,引起程序崩溃或在访问废弃的对象时崩溃

    id obj = [[NSObject alloc] init]; [obj release]; [obj release]; // 再次释放

    空指针 野指针 僵尸对象 被释放的对象为僵尸对象 指向僵尸对象的指针是野指针 空指针 是把指针位 nil 不会报错 //问题1:什么是僵尸对象?什么是野指针?什么是空指针?如何避免野指针错误? 答: 被释放的对象为僵尸对象 指向僵尸对象的指针为野指针 空指针是把指针为nil 1. 僵尸对象 已经被销毁的对象(不能再使用的对象) 2.野指针 指向僵尸对象(不可用内存)的指针 给野指针发消息会报EXC_BAD_ACCESS错误 3.空指针 没有指向存储空间的指针(里面存的是nil, 也就是0) 给空指针发消息是没有任何反应的 4. 为了避免野指针错误的常见办法: 在对象被销毁之后, 将指向对象的指针变为空指针 Person *p = [[Person alloc]init]; //只要一个对象被释放了,我们就称这个对象位“僵尸对象” //当一个指针指向一个僵尸对象,我们就称这个指针为野指针 //只要给一个野指针发送消息就会报错 [p release]; //开启僵尸对象报错 /* *** -[Person release]: message sent to deallocated instance 0x1002066b0 */ //空指针 nil 0 //为了避免给野指针发送消息会报错,一般情况下,荡一个对象被释放后我们将会将这个对象的指针设置为空指针 //因为在OC中给空指针发送消息是不会报错的 p = nil; [p release];
    转载请注明原文地址: https://ju.6miu.com/read-14103.html

    最新回复(0)