zoukankan      html  css  js  c++  java
  • OC-引用计数器,内存管理,野指针

    总结

    • 全局断点
      • →-->+-->Add Exception Breakpoint
    • 开启僵尸监听

      • 打开Edit scheme -->Diagnostics-->Enable Zombie Object
    • retain 不仅仅会对计数器 + 1,而且还会返回当前对象

    标号标题内容
    内存管理 内存管理的重要性/内存管理概念/堆和栈/内存管理原则/多对象内存管理/set方法内存管理/dealloc方法的内存管理
    引用计数器 引用计数概念器/作用/操作
    dealloc dealloc方法基本概念
    野指针/空指针 僵尸对象概念/野指针概念/空指针概念
    Xcode设置 如何关闭ARC功能/如何开启僵尸对象监控
    Property修饰符 控制set方法的内存管理/控制需不需要生成set方法/多线程管理/控制set方法和get方法的名称
    @class @class基本概念/应用场景/@class和#import区别
    循环retain 循环retian基本概念

    一.内存管理

    • ARC: Automatic Reference Counting

    • 什么是自动引用计数

      • 不需要程序员管理内容,编译器会在适当的地方自动给我们添加release/retain等代码
      • 注意点:
        • OC中的ARC和Java中的垃圾回收机制不太一样,Java中的垃圾回收是系统干的,而OC中的ARC是编译器干的
        • 只要创建一个对象默认引用计数器的值就是1
    • MRC: Manual Reference Counting
    • 什么是手动引用计数
      • 所有对象的内容都需要我们手动管理,需要程序员自己编写release/retain等代码
    • 内存管理的原则
      • 有加有减
    1.内存管理的重要性
    • 移动设备的内存极其有限,每个app所能占用的内存是有限制的

    • 下列行为都会增加一个app的内存占用

      • 创建一个OC对象
      • 定义一个变量
      • 调用一个函数或者方法
    • 如果app占用内存过大, 系统可能会强制关闭app, 造成闪退现象, 影响用户体验

    2.什么是内存管理
    • 如何回收那些不需要再使用的对象?

      • 学会OC的内存管理
    • 所谓内存管理, 就是对内存进行管理, 涉及的操作有:

      • 分配内存 : 比如创建一个对象, 会增加内存占用
      • 清除内存 : 比如销毁一个对象, 能减小内存占用
    • 内存管理的管理范围

      • 任何继承了NSObject的对象
      • 对其他非对象类型无效(int、char、float、double、struct、enum等 )
    • 只有OC对象才需要进行内存管理的本质原因

      • OC对象存放于堆里面
      • 非OC对象一般放在栈里面(栈内存会被系统自动回收)
    3.堆和栈
    • 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈(先进后出);

    • 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(Operating System 操作系统)回收,分配方式类似于链表。

    • 示例:

    
    int main(int argc, const char * argv[])
    {
        @autoreleasepool {
            int a = 10; // 栈
            int b = 20; // 栈
            // p : 栈
            // Person对象(计数器==1) : 堆
            Person *p = [[Person alloc] init];
        }
        // 经过上一行代码后, 栈里面的变量ap都会被回收
        // 但是堆里面的Person对象还会留在内存中,因为它是计数器依然是1
        return 0;
    }
    
    4.内存管理原则
    • 苹果官方规定的内存管理原则

      • 谁创建谁release :

        • 如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
      • 谁retain谁release:

        • 只要你调用了retain,就必须调用一次release
    • 总结一下就是

      • 有加就有减
      • 曾经让对象的计数器+1,就必须在最后让对象计数器-1
    5.多对象内存管理
    • 只要还有人在用某个对象,那么这个对象就不会被回收
    • 只要你想用这个对象,就让对象的计数器+1
    • 当你不再使用这个对象时,就让对象的计数器-1
    6.set方法内存管理
    • (1)retain需要使用的对象
    • (2)release之前的对象
    • (3)只有传入的对象和之前的不同才需要release和retain
    - (void)setRoom:(Room *)room
    {
        // 避免过度释放
        if (room != _room)
        {
            // 对当前正在使用的车(旧车)做一次release
            [_room release];
    
            // 对新车做一次retain操作
             _room = [room retain];
        }
    }
    
    7.dealloc方法的内存管理
    - (void)dealloc
    {
        // 当人不在了,代表不用房间了
        // 对房间做一次release操作
        [_room release];
        [super dealloc];
    }
    

    二.引用计数器
    • 占4个字节
    1.什么是引用计数器
    • 系统是如何判断什么时候需要回收一个对象所占用的内存?

      • 根据对象的引用计数器
    • 什么是引用计数器

      • 每个OC对象都有自己的引用计数器
      • 它是一个整数
      • 从字面上, 可以理解为”对象被引用的次数”
    2.引用计数器的作用
    • 简单来说, 可以理解为:

      • 引用计数器表示有多少人正在使用这个对象
    • 当没有任何人使用这个对象时,系统才会回收这个对象,也就是说

      • 当对象的引用计数器为0时,对象占用的内存就会被系统回收
      • 如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出)
    • 任何一个刚创建的对象, 引用计数器都为1

      • 当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1
    3.引用计数器的操作
    • 引用计数器的常见操作

      • 给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
      • 给对象发送一条release消息, 可以使引用计数器值-1
      • 给对象发送retainCount消息,可以获得当前的引用计数器值
    • 需要注意的是:release并不代表销毁回收对象,仅仅是计数器-1


    三.dealloc

    1.dealloc方法基本概念
    • 当一个对象的引用计数器值为0时,这个对象即将被销毁,其占用的内存被系统回收
    • 对象即将被销毁时系统会自动给对象发送一条dealloc消息 (因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)

    • 作用

      • 用来判断对象是否被销毁
    • 规律

      • 当一个对象引用计数器 = 0时,该对象即将被销毁,就会调用dealloc
    • dealloc方法的重写

      • 一般会重写dealloc方法,在这里释放相关资源
      • 一旦重写了dealloc方法, 就必须调用[super dealloc](在MRC环境下),并且放在最后面调用
    • 使用注意

      • 不能直接调用dealloc方法
      • 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

    四.野指针/空指针

    1.僵尸对象
    • 已经被销毁的对象(不能再使用的对象)
    2.野指针
    • 指向僵尸对象(不可用内存)的指针
    • 给野指针发消息会报EXC_BAD_ACCESS错误
    3.空指针
    • 没有指向存储空间的指针(里面存的是nil, 也就是0)
    • 给空指针发消息是没有任何反应的

    • 为了避免野指针错误的常见办法

      • 在对象被销毁之后, 将指向对象的指针变为空指针
      • 因为OC中给空指针发送消息不会报错

    五.Xcode设置

    1.如何关闭ARC功能
    • 要想手动调用retain、release等方法 , 就必须关闭ARC功能
      • 项目--> Bulid Setting / All --> 搜automatic-->将Objective-C Automatic Reference Counting 改成 No
    2.如何开启僵尸对象监控
    • 默认情况下,Xcode是不会管僵尸对象的,使用一块被释放的内存也不会报错。为了方便调试,应该开启僵尸对象监控

      • Edit Scheme-->Diagnostics-->Objective-C后的Enable Zombile Objects 打√号

    * 六.Property修饰符

    1.控制set方法的内存管理
    • retain : release旧值,retain新值(用于OC对象)
      • retain会自动生成getter和setter方法内存管理的代码
        • 相同的property修饰符不能同时使用
    • assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
      • assign不会自动生成getter和setter方法内存管理的代码,仅仅只会生成普通的getter和setter方法(默认)
    • copy : release旧值,copy新值(一般用于NSString *)
    2.控制需不需要生成set方法
    • readwrite :同时生成set方法和get方法(默认)
    • readonly :只会生成get方法
    3.多线程管理
    • atomic:性能低(默认),一次只能通过一个人
    • nonatomic:性能高,一次可以通过多个人(ios中多用此修饰符)
    4.控制set方法和get方法的名称
    • setter : 设置set方法的名称,一定有个冒号:
    • getter : 设置get方法的名称
    • 注意: 不同类型的参数可以组合在一起使用

    七. @class

    1.@class基本概念
    • 作用

      • 可以简单地引用一个类
    • 简单使用

      • @class Dog;
      • 仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容
    • 具体使用

      • 在.h文件中使用@class引用一个类
      • 在.m文件中使用#import包含这个类的.h文件
    2.@class其它应用场景
    • 对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类
    • 这种嵌套包含的代码编译会报错
    #import "B.h"
    @interface A : NSObject
    {
        B *_b;
    }
    @end
    
    #import “A.h"
    @interface B : NSObject
    {
        A *_a;
    }
    @end
    
    • 当使用@class在两个类相互声明,就不会出现编译报错
    @class B;
    @interface A : NSObject
    {
        B *_b;
    }
    @end
    
    @class A;
    @interface B : NSObject
    {
        A *_a;
    }
    @end
    
    3.@class和#import区别
    • 作用上的区别

      • #import会包含引用类的所有信息(内容),包括引用类的变量和方法
      • @class仅仅是告诉编译器有这么一个类,具体这个类里有什么信息,完全不知
    • 效率上的区别

      • 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,编译效率非常低
      • 相对来讲,使用@class方式就不会出现这种问题了

    八. 循环retain

    1.循环retian基本概念
    • 循环retain的场景

      • 比如A对象retain了B对象,B对象retain了A对象
    • 循环retain的弊端

      • 这样会导致A对象和B对象永远无法释放
    • 循环retain的解决方案

      • 当两端互相引用时,应该一端用retain、一端用assign

  • 相关阅读:
    移动硬盘文件被恶意隐藏
    asp.net identity UserSecurityStamp 的作用
    Head First Python学习笔记1
    WPF 确认动态加载数据完成
    rust by example 2
    Rust by Example1
    奇葩!把类型转成object
    Lambda高手之路第一部分
    理解Lambda表达式
    贪心算法-找零钱(C#实现)
  • 原文地址:https://www.cnblogs.com/HMJ-29/p/4700222.html
Copyright © 2011-2022 走看看