Objective-C 学习第六天

简介: 一、内存管理内存的作用: 存储数据.1). 如何将数据存储到内存之中声明1个变量,将这个数据存储进去2). 当数据不再被使用的时候,占用的内存空间如何被释放内存中的五大区域栈:局部变量,当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收.

一、

  1. 内存管理
    内存的作用: 存储数据.
    1). 如何将数据存储到内存之中
    声明1个变量,将这个数据存储进去
    2). 当数据不再被使用的时候,占用的内存空间如何被释放

  2. 内存中的五大区域
    栈:局部变量,当局部变量的作用域被执行完毕之后,这个局部变量就会被系统立即回收.
    堆:OC对象,使用C函数申请的空间
    BSS段:未初始化的全局变量、静态变量,一旦初始化就回收,并转存到数据段中.
    数据段:已经初始化的全局变量、静态变量,知道程序结束的时候才会被回收.
    代码段:代码,程序结束的时候,系统会自动回收存储在代码段中的数据.

    栈、BSS段、数据段、代码段存储在它们中的数据的回收,是系统自动完成的.

  3. 分配在堆区的OC对象,是肯定需要被回收的.
    iPhone 内存机制,如果内存占用内存达到40M会受到系统警告,120M会闪退.

    存储在堆中的OC对象,系统是不会自动回收的,知道程序结束的时候才会被回收。

  4. 内存管理的范围:
    只需要管理堆中的OC对象的回收,其他区域中的数据是系统自动管理的.

  5. 对象应该什么时候被回收?
    当有人使用这个对象的时候,这个对象就不会被回收,只有在没有人使用的时候才可以被回收.

  6. 引用计数器
    1). 每一次对象都有一个属性,叫做retainCount(引用计数器),类型是unsigned long占据八个字节。
    引用计数器的作用: 用来记录这个对象有多少人在使用它,默认情况下,创建一个对象出来,这个引用计数器的默认值是1.
    2). 当多一个人使用的时候,这个引用计数器的值+1
    3). 当少一个人使用的时候,这个引用计数器的值-1
    4). 当这个对象的引用计数器变为0的时候,代表这个无人使用,系统就会自动回收它.

  7. 如何操作引用计数器
    1). 为对象发送1条retain消息,对象的引用计数器就会加1,当多一个人使用的对象的时候才发
    2). 为对象发送1条release消息,对象的引用计数器就会减1,当少一个人使用的对象的时候才发
    3). 为对象发送一条retainCount消息,就可以了取到对象的引用计数器的值
    4). 当对象的引用计数器变为0的时候,对象就会被系统立即回收, 在对象被回收的时候,会自动调用对象的dealloc方法.

  8. 内存管理的分类
    1).MRC(Manual Reference Counting)手动引用计数,手动内存管理.
    当多一个人使用对象的时候,要求程序员手动的发送retain消息;反之.
    2). ARC(Automatic Reference Counting)自动引用计数,自动内存管理.
    系统自动的在合适的地方发送retain realse消息.

    1. 学习MRC理由:面试、早期的App开发使用的是MRC技术、ARC是基于MRC的.

二、MRC

  1. Xcode默认的开发方式是ARC模式.
    关闭ARC:Project->选中工程->Build Settings-> All->Apple LLVM7.0 - Language - Objective C ->Objective-C Automatic Reference Counting,设置为NO.

  2. 当对象的引用计数器变为0的时候,系统会自动回收对象. 在系统回收对象的时候,会自动的调用对象的dealloc方法.
    1). 重写类的dealloc方法, 必须要调用父类的dealloc方法,要放在最后一句代码的位置。

        - (void)dealloc{
            NSLog(@"名字叫XXX消失了");
            [super dealloc];
        }
    
  3. 测试引用计数器
    1). 新创建一个对象,这个对象的引用计数器的值默认是1.

        Person *p1 = [[Person alloc] init];
        [p1 retainCount];// 获取引用计数器的值
        [p1 retain];//为引用计数器加1
        [p1 release];//为引用计数器减1
    

    2). 当对象的引用计数器变为0的时候,对象就会被系统立即回收,并自动调用dealloc方法.
    3). 为对象发送这个return消息,对象的引用计数器就会加1

  4. 为对象发送release消息并不是回收对象,而是让对象的引用计数器减1,当对象的引用计数器的值变为0的时候,对象才会被系统立即回收.

  5. 内存管理的重点
    1). 什么时候为对象发送retain消息
    当多一个人使用这个对象的时候,应该先为这个对象发送retain消息
    2). 什么时候为对象发送release消息
    当少一个人使用这个对象的时候,应该为这个对象发送一条release消息
    在ARC机制下,retain、release、dealloc这些方法无法调用

  6. 内存管理的原则
    1). 有对象的创建,就要匹配一个release
    2). retain和release的调用次数要匹配
    3). 谁用谁retain,谁不用谁release
    4). 只有在多一个人用的时候才retain,少一个人使用的时候才release,有始有终,有加就有减。

  7. 野指针
    C语言中的野指针: 定义一个指针变量,没有初始化,这个指针变量的值时一个垃圾值,指向一块随机的空间
    OC中的野指针:指针指向的对象已经被回收了.

  8. 内存回收的本质:
    申请一个变量,实际上就是向系统申请指定字节数的空间,这段空间就不会分配给别人了,当变量被回收的时候,代表变量占用的字节空间可以分配给别人了,但是这个字节空间中存储的数据还在。
    对象回收的本质:指的是对象占用的空间可以分配给别人了,当这个对象占用的空间没有分配给别人之前,其实对象数据还在.

  9. 僵尸对象
    1个已经被释放的对象,但是这个对象所占空间还没有分配给别人。通过野指针访问僵尸对象的时候,有可能没问题,也有可能有问题。当僵尸对象占用的空间还没有分配给别人使用的时候,还是可以的;反之。

  10. 我们认为只要对象成为了僵尸对象,无论如何都应该不允许访问了。如果访问的是僵尸对象,无论如何都报错。
    僵尸对象的实时检查机制,可以将这个机制打开,打开之后,只要访问的是僵尸对象,无论空间是否分配,就会报错。

    停止运行按钮右侧的Target切换点击-> Edit Scheme...->Run->Diagnostics->勾选Enable Zomble Objects.

  11. 为什么不默认打开僵尸对象检测
    一旦打开僵尸对象检测,那么在每访问一个僵尸对象的时候,都会先检测这个对象是否是一个僵尸对象,这样是极其消耗性能的.

  12. 如何避免僵尸对象错误?
    当一个指针成为野指针以后,将这个指针的值设置为nil.
    当一个指针为nil时,通过这个指针调用对象的方法的时候,不会报错,只是没有任何反应,但是如果通过指针直接访问属性就会报错.

  13. 无法复活一个僵尸对象

三、

  1. 内存泄漏
    指的是一个对象没有被及时的回收,在该回收的时候没有及时回收,一直驻留在内存中,知道程序结束的时候才结束.

  2. 单个对象的内存泄漏情况
    1). 有对象的创建,而没有对应的release
    2). retain的次数和release的次数不匹配
    3). 在不适当的时候为指针赋值为nil
    4). 在方法中为传入的参数不适当的retain

  3. 如何保证单个对象可以被回收
    1). 有对象的创建,就必须要匹配一个release
    2). retain和release次数一定要匹配
    3). 只有在指针成为野指针时才赋值为nil
    4). 在方法中不要随意的为传入的对象retain

  4. 多个对象的内存管理
    1). 当属性是一个OC对象的时候,setter方法的写法.
    将传进来的对象赋值给当前对象的属性,代表传入的对象多一个人使用,所以我们应该先为这个传入的对象发送一条retain消息再赋值.当当前对象销毁的时候,代表属性指向的对象少一个人使用,就应该在dealloc中release.
    代码写法:

        - (void)setCar:(Car *)car{
            _car = [car retain];
        }
    
        - (void)dealloc{
            [_car release];
            [super dealloc];
        }
    

    2). 当属性是一个OC对象的时候,setter方法按照上面写还是有bug.
    当为对象的这个属性多次赋值的时候,就会发生内存泄漏,发生泄漏的原因: 当为属性赋值的时候,代表就对象少一个人用,新对象多一个人用,应该release,旧对象retain新的.
    代码写法:

        - (void)setCar:(Car *)car{
            [_car release];
            _car = [car retain];
        }
    
        - (void)dealloc{
            [_car release];
            [super dealloc];
        }
    

    3). 出现僵尸对象的原因,新旧对象是同一个,当发现新旧对象是同一个的时候,什么都不用做.只有当新旧对象不是同一对象的时候,才release旧的.
    最终代码写法:

        - (void)setCar:(Car *)car{
            if(_car != car){
                [_car release];
                _car = [car retain];
            }
        }
    
        - (void)dealloc{
            [_car release];
            [super dealloc];
        }
    

    4). 特别注意: 内存管理的范围是OC对象,这个属性的setter方法才要像上面那样写.

  5. @property参数
    1). 作用
    a. 自动生成私有属性
    b. 自动生成这个属性的getter/setter的声明
    c. 自动生成这个属性的getter/setter的实现
    特别播报: 生成的setter方法实现中,无论什么类型,都是直接赋值.
    2). @property参数
    a. @property可以带参数
    @property(参数1, 参数2, 参数3...)数据类型 名称;
    b. @property的四组参数
    a). 与多线程相关的两个参数
    atomic/nonatomic
    b). 与生成的setter方法的实现相关的参数
    assign/retain
    c). 与生成只读、读写相关的参数
    readonly/readwrite
    d). 与生成getter/setter方法名相关的参数
    getter/setter

    3). 与多线程相关的参数(atomic/nonatomic)
    atomic: 默认值,这个时候生成的setter方法的代码就会被加上一把线程安全锁.特点: 安全、效率低
    nonatomic: 生成的setter方法的代码不会加线程安全锁,但是效率高.
    建议要效率,选择使用nonatomic
    4). 与生成的setter方法的实现相关的参数(assign/retain)
    assign: 默认值,生成的setter方法实现就是直接赋值
    retain: 生成的setter方法的方法实现就是标准的MRC内存管理代码,先判断新旧对象是否为同一对象,如果不是就release旧的,retain新的.
    当属性的类型是OC对象类型的时候就使用retain,当属性的类型实时非OC对象的时候就使用assign.
    注意: retain只是生成标准的setter方法为标准的MRC内存管理代码,不会自动的在dealloc中生成release的代码,需要手动添加.
    5). 与生成只读、读写相关的参数readonly/readwrite)
    readwrite: 默认值,代表同时生成getter/setter
    readonly: 只会生成getter,不会生成setter

    6). 与生成getter/setter方法名相关的参数(getter/setter)
    默认情况下: @property生成的getter/setter是标准类型的,其实我们可以通过参数来指定@property生成的方法的名字
    setter: setter=setter方法名字 用来指定@property生成的setter的方法的名字,注意,setter方法是带参数的,要加一个冒号
    getter: getter=getter方法名字 用来指定@property生成的getter方法的名字
    注意: 如果使用setter/getter修改了生成方法的名字,在使用点语法的时候,编译器在转换的时候转换成修改后的名字的代码.
    使用建议:
    a. 无论什么情况都不要改setter方法的名字,默认情况下生成的名字已经是最标准的了
    b. 什么修改getter方法的名字,当属性的类型是一个BOOL类型的时候,就修改这个getter的名字以is开头,提高代码的阅读性.

  6. @class
    1). 当两个类相互引用的时候,当Person.h中包含Book.h,而Book.h中又包含Person.h,这个时候就会造成无限递归,而导致无法编译通过.
    2). 解决方案:
    其中一个类中不要使用#import引入对方的头文件,而是使用@class 类名,来标注这是一个类,这样就可以在不引入对方头文件的情况下,告诉编译器这是一个类。
    在.m文件当中再#import对方的头文件
    3). @class与#import的区别
    a. #import是将指定的文件的内容拷贝到写指令的地方
    b. @class并不会拷贝任何内容,只是告诉编译器这是一个类,这样编译器在编译的时候才可以指定这是一个类。

  7. 循环retain
    1). 当两个对象相互引用的时候
    A对象的属性是B对象,B对象的属性是A对象.如果两边都使用retain,就会发生内存泄漏
    2). 解决方案
    一段使用retain,一段使用assign,使用assign的那一段在dealloc中不再需要release.

目录
相关文章
|
存储 Java 程序员
C++ 开发者快速学习 Objective-C 语言核心语法
本文将讨论 Objective-C 语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。
223 0
|
C++ iOS开发 开发者
C++开发者快速学习Objective-C语言核心语法
  本文将Objective-C讨论了语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。   类并不是特殊的   在Smalltalk中,类是具有一些特性的对象。在Objective-C中也一样。
944 0
|
存储 C语言 iOS开发
Objective-C 学习第一天
一、基础语法 OC相对于C a. 在C的基础上新增了面向对象的语言 b. 将C的复杂、繁琐的语法封装的更为简单 c. OC完全兼容C语言 OC程序的源文件后缀名是.m m代表message 代表OC当中最重要的一个机制 消息机制 C程序的源文件的后缀名.c main函数仍然是OC程序的入口和出口 int类型的返回值代表程序的结束状态 main函数的参数: 仍然可以接收用户在运行程序的时候传递数据给程序, 参数也可以不要 #import指令 1). 以#号开头,是1个预处理指令 2). 作用: 是#include指令的增强版。
813 0
|
存储 iOS开发 C语言
Objective-C 学习第二天
一、 对象在内存中的存储 内存中的五大区域 栈 存储局部变量 堆 程度员手动申请的字节看空间 malloc calloc realloc函数 BSS段 存储未被初始化的全局变量, 静态变量 数据段(常量区) 存储已被初始化的全局、静态变量、常量数据 代码段 存储代码 存储代码程序 类加载 a.
716 0
|
存储 iOS开发
Objective-C 学习第三天
封装 一、 什么是错误 一般情况下,错误是指源代码不符合语法规范,然后编译报错 后果: 程序无法编译 什么是Bug? 程序可以编译、链接、执行,但是程序执行的结果不是我们预想的。
879 0
|
存储 iOS开发
Objective-C 学习第四天
一、 Xcode文档的安装 1). Xcode文档提供了很多框架, 框架当中有很多类和函数, 提供的一些数据类型. 2). Xcode文档需要单独安装.
896 0
|
存储 iOS开发 编译器
Objective-C 学习第五天
一、 创建一个对象,内存是如何分配 1). 子类对象中有自己的属性和所有父类的属性 2). 代码段中每一个类都有一个isa指针,这个指针指向它的父类. 结构体与类 相同点: 都可以将多个数据封装为1个整体 struct Data{ ...
651 0
|
存储 对象存储 iOS开发
Objective-C 学习第七天
一、 自动释放池的原理 存入到自动释放池中的对象,在自动释放池销毁的时候,会自动调用储存在该自动释放池中的所有对象的release方法. 可以解决的问题: 将创建的对象,存入到自动释放池之中,就不再需要手动的release这个对象了,因为池子销毁的时候,就会自动的调用池中所有的对象release.
839 0
|
存储 iOS开发
Objective-C 学习第八天
一、延展 延展: Extension 1). 是一个特殊的分类,所以延展也是类的一部分 2). 特殊之处: a. 延展这个特殊的分类没有名字 b. 只有声明没有实现,和本类共享一个实现 延展的语法 语法: @interface 本类名 () @end 没有实现,和本类共享一个实现.
835 0
|
存储 iOS开发 对象存储
Objective-C 学习第九天
一、 框架: 系统或者第三方事先写好的写很牛X功能的类,把这些类交给我们使用,这些类的集合就叫框架. Foundation框架: 是一个包,有很多类和函数,定义了一些数据类型.
912 0