• 内存管理


    为什么要注意内存管理

    无论编写任何程序, 都需要有效和高效地管理资源。内存资源则是重中之重, 都知道如果电脑内存如果不够用, 系统就能会卡, 反应尺寸, 那么手机也是一样的。在OC程序中对象一旦不需要了必须释放资源. 
    在一个复杂的系统中,可能很难精确地确定从何时起您不再需要某个对象。Cocoa定义了一些有助于使这一抉择变得更加容易的规则 和原则

    注意: 这里直说iOS的内存不参与Mac的内存, Mac在10.5以后就有垃圾回收机制。 

    非ARC(iOS5之前)

    内存管理原则 :

      如果使用alloc、new、copy、mutableCopy创建一个对象, 系统会给这个对象的内部引用计数器赋值1, 您也就拥有这个对象的所有权, 不是自己创建的也可以对对象进行retain操作也会拥有这个对象的所有权。注意如果您对对象retain操作, 其内部的引用计数器也会 +1操作
      释放对象的所有权: 可以用release, 或者autorelease自动释放一个对象的所有权. 注意: 不一定release之后对象就一定被释放了, 他只是释放对象的所有权, 换句话来说只是将其内部的引用计数器 -1操作, 对象是否被释放要看内部的【引用计数器】为0
      autorelease不是马上释放对象, 而是在一个运行循环(runloop)结束之后向所有等待释放的对象发送一条release操作, 详细介绍在后面

    对象的所有权(引用计数器):

    说简单点就是如果你引用给一个对象就拥有这个对象的所有权, 一个对象可以被N个所有者拥有, 如果有人拥有这个对象, 那么这个对象就一直存在, 如果没有人拥有就会在运行时被释放. Cocoa 制定了一个非常完美的策略来实现对象是否被释放

    • 自己使用alloc, new, copy, mutableCopy创建的对象就有对象的所有权, 并且其内部的引用计数器retainCount=1。
    • 如果不是自己创建的您可以用retain来获取对象的拥有权, retain之后, 对象其内部的retainCount+1操作
    • 如果不使用对象则需要对对象进行release操作, 其内部retainCount-1, 您也可以使用autorelease自动释放对象
    • 如果类被销毁之前需要在dealloc方法里对其内部的所有属性进行release操作, 对象销毁同时对其内部对象所有权也就释放了
      • 需要注意非ARC如果调用dealloc 必须重写父类方法, 重写代码一定放在最后调用, 如果先调用该对象就成为僵尸对象, 僵尸对象是不可操控对象, 从而无法释放内部对象

    下面我们看看具体代码

    • 以new为例

    如果您在ARC项目中把一个class修改成非ARC状态之后在执行44行的时候会发现跑通了没报错, 而且他的retainCount值也是1, 首先解决这个现象需要在product-> scheme-> Edit scheme -> Diagnostics -> Enable Zombie Objects勾选上, 在运行就会出错了, 那么这里给你详细的解释是:
    事实上obj的确已经被dealloc了,保留计数器的值也已经变成0了,其原来占用的内存也已经不可用了,但是原来这块内存中的内容还没有变(标记删除),将会在未来某个不确定的时间上被清理(就是runloop做的事),这就是为什么NSLog输出的obj保留计数器的值仍为1,而如果在此时再加上一个NSLog, 用1个僵尸对象调用一块被释放的坏内存, 于是程序crash

    • 不是自己创建的对象

    首先定义一个Student创建一个对象叫car

    重写 car对象setter方法, 里面判断是否是同一个对象传进来, 如果不同对象需要对其旧对象释放, 新对象retain操作, 这样就拥有对象的所有权

    下面是对象的retainCount值, 如果看不太明白自己动手操作一下就明白了.

     引用计数器(保留数)

    每一个对象内部都有一个retainCount属性, 用来判断有多少个对象拥有它, 如果没有对象拥有它就被释放

    • 当对象被创建(retainCount =1) 引用计数器为1
    • 当向对象发送retain消息时, 该对象引用计数器会+1
    • 当向对象发送release消息时, 该对象引用计数器会-1, 如果发送autorelease 不会立即-1 是在运行时某一时间-1
    • 引用计数器为0 就会被释放掉

    自动释放(autorelease), 有人也叫半自动释放

    如果向一个对象发送autoreleace消息之后, 不会立即被释放, 对象会被放入自动释放池中, 等待系统会在某一时间点, 对其释放池内部的所有对象进行一次releace操作

    自动释放池

    自动释放池是一个NSAutoreleasePool实例(允许被嵌套使用), 其中“包含”已经收到autorelease消息的其他对象; 当自动释放池被回收时, 它会向其中的每个对象发送一条release消息。一个对象可以被数次放入一个自动释放池中, 并且在每次被放入池中的时候都会收到一条release消息。因此,向对象发送autorelease消息(而不是release消息)可以至少将该对象的生命周期延长至自动释放池本身 被释放的时候(如果在此期间对象被保留,则它可以存活更久)。 多了不详细介绍, 现在都ARC时代了, 谁还用非ARC东西, 如果不研究底层东西可以忽略

    5.0之前的写法: 

    5.0之后写法

     

    循环引用

    项目中经常出现的就是2个对象互相引用的情况, 如果2端全用retain 进行修饰, 就会出现循环引用状况, 因为2者互相引用导致计数器都不为0, 2者谁也不能放过谁, 长时间就出现app crash

    代码表示如下: 

    Car.h文件

    Car.m文件

    Student.h文件

    Student.m文件

    实例代码: 

    看log最后他们的引用计数都为1, 解决办法: 一端用assign, 一段用retain 即可

    拿Car文件举例, Student文件没动

    Car.h文件修改为

    Car.m文件修改为

    实例代码:

     ARC (5.0之后)

    iOS5出来一个非常好的机制就是ARC, 他不用程序员手动去管理内存, 完全由系统自动管理, 系统也屏蔽了release, retain, retainCount方法, dealloc里也不能调用[super dealloc], 因为ARC的内存管理原则与非ARC完全不同。下面就看一下ARC内存管理原则 

    ARC内存管理原则: 对象是否有强指针指向(引用), 有强指针引用对象一直存在, 没有就会被释放.

    ARC出来之后出来2个关键字:weak, strong(弱, 强), 干什么用的? 修饰对象用的

    weak: 用来修饰弱指针引用。

    strong: 用来修饰强指针引用。(默认: 系统默认创建出来的对象都是强指针引用)

    有人会问assign不也可以修饰对象吗? weak和assign有什么区别?

    什么时候用weak:一般在解决循环引用的时候用weak,比如delegate, block内部需要引用外部对象, 或者该对象已经被强引用着, 而现在需要转成weak.
    什么时候用assign:在MRC中解决循环引用的时候, 修饰基本数据类型, 修饰对象是简单的赋值, 不操作内部的引用计数器, 而ARC中它不能修饰对象, 只能修饰基本数据类型.

    ARC的循环引用

    MRC中有循环引用在ARC中也同样有循环引用,为什么会造成循环引用?

    就是因为2个对象都属于强类型, 我们看看下面的图片你就应该明白了

     

    学生引用车, 车反过来引用学生, 导致2者都不能被释放, 互相咬着, 谁也不放谁

    解决办法: 一端用weak, 一端用strong, 让其一端变成弱引用.看看下面图片

    这样c对象内部是s 被释放了,s里的car就不存在了, c没有强指针引用, 所以c也就被释放了

    ARC与MRC的性能对比: 

    ARC的性能是更好的,这主要得益于一些底层的优化以及autorelease pool的优化,这个从官方文档也能看到。
    但在一些情况下,ARC确实是更慢,ARC会发送一些额外的retain/release消息,如一些涉及到临时变量的地方, 之所以添加这些额外的retain/release操作,是为了保证代码运行的正确性

    最后补充一下ARC 与MRC的转换

    • MRC旧项目改 ARC新项目

    edit —> refactor —> convert to ARC
    证明是不是ARC项目
    build settings 搜索auto —> objective - c ARC
    • ARC ,MRC共存

    build phases 双击文件/回车 输入
    -fno-objc-arc 非ARC
    -f-objc-arc ARC

    如果有什么问题欢迎留言让我们一起进步........欢迎您耐心看完。

  • 相关阅读:
    Nginx报400 Bad Request
    当前系统代理不是安全代理,是否信任
    nginx反向代理解决跨域问题
    SQL Prompt快捷键
    本地SQL Server怎么连接服务器上的数据库
    进制之间的转换
    计算机知识汇总
    C#语言学习记录
    excel常用技巧
    T-SQL学习记录
  • 原文地址:https://www.cnblogs.com/MrTao/p/5232736.html
Copyright © 2020-2023  润新知