OC基础(六)——内存管理

内存管理概述

  1. 内存管理
    内存的作用:存储数据.
  • 如何将数据存储到内存之中.
    声明1个变量.然后将数据存储进去.

  • 当数据不再被使用的时候,占用的内存空间如何被释放.

  1. 内存中的五大区域

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

    栈、BSS段、数据段、代码段存储在它们中的数据的回收,是由系统自动完成的.不需要我们干预.

  2. 分配在堆区中的OC对象,是肯定需要被回收的.

    iPhone 内存机制.

    40M 警告
    45M 警告
    120M 闪退.

    存储在堆中的OC对象,系统不会自动回收. 直到程序结束的时候才会被回收.

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

  4. 对象应该什么时候被回收?

    当有人使用这个对象的时候,这个对象就千万不能回收.
    只有在没有任何人使用这个对象的时候,才可以回收.

  5. 引用计数器

    1). 每1个对象都有1个属性.叫做retainCount.叫做引用计数器. 类型是unsigned long 占据8个字节.
    引用计数器的作用: 用来记录当前这个对象有多少个人在使用它.
    默认情况下,创建1个对象出来 这个对象的引用计数器的默认值是1.

    2). 当多1个人使用这个对象的时候.应该先让这个对象的引用计数器的值+1 代表这个对象多1个人使用.

    3). 当这个对象少1个人使用的时候.应该先让这个对象的引用计数器的值-1 代表这个对象少1个人使用.

    4). 当这个对象的引用计数器变为0的时候.代表这个对象无人使用. 这个时候系统就会自动回收这个对象.

  6. 如何操作引用计数器.

    1). 为对象发送1条retain消息. 对象的引用计数器就会加1. 当多1个人使用对象的时候才发.

    2). 为对象发送1条release消息.对象的引用计数器就会减1. 当少1个人使用对象的时候才发.

    3). 为对象发送1条retainCount消息. 就可以去到对象的引用计数器的值.

    就这样++ -- 当对象的引用计数器变为0的时候,对象就会被系统立即回收.

    在对象被回收的时候.会自动调用对象的dealloc方法.

  7. 内存管理的分类

    MRC: Manual Reference Counting 手动引用计数.手动内存管理.

    当多1个人使用对象的时候,要求程序员手动的发送retain消息.少1个人使用的时候程序员手动的发送relase消息.

    2011年之前 iOS5之前
    ARC: Automatic Reference Counting 自动引用计数.自动内存管理.
    系统自动的在合适的地方发送retain relase消息.

第一个MRC程序(这个简单知道就行了)

  1. iOS5开始. Xcode4.2开始就支持ARC
    Xcode7 默认支持ARC开发.
    默认使用的开发方式就是ARC的模式.

    关闭ARC开启MRC.

  2. 当对象的引用计数器变为0的时候,系统会自动回收对象.
    在系统回收对象的时候.会自动的调用对象的dealloc方法.

    重写dealloc方法的规范:
    必须要调用父类的dealloc方法. 并且要放在最后一句代码.

  3. 测试引用计数器.

    1). 新创建1个对象,这个对象的引用计数器的值默认是1.
    2). 当对象的引用计数器变为0的时候.对象就会被系统立即回收 并自动调用dealloc方法.
    3). 为对象发送retain消息 对象的引用计数器就会+1

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

内存管理原则

  1. 内存管理的重点

    1). 什么时候为对象发送retain消息.

    当多1个人使用这个对象的时候,应该先为这个对象发送retain消息.

    2). 什么时候为对象发送release消息.

    当少1个人使用这个对象的时候.应该为这个对象发送1条release消息.

  2. 在ARC机制下,retain release dealloc这些方法无法调用.

  3. 内存管理的原则

    1). 有对象的创建,就要匹配1个release

    2). retain的次数和release的次数要匹配.

    3). 谁用谁retain. 谁不用谁release.
    谁负责retain 谁就负责relase

    4). 只有在多1个人用的时候才retain 少1个人使用的时候才release

    有始有终,有加就有减. 有retain就应该匹配1个release 一定要平衡.

野指针与僵尸对象

  1. 野指针

    C语言中的野指针: 定义1个指针变量.没有初始化.这个指针变量的值是1个垃圾值,指向1块随机的空间.这个指针就叫做野指针.

    OC中的野指针: 指针指向的对象已经被回收了.这样的指针就叫做野指针.

  2. 对象回收的本质.

    内存回收的本质:
    申请1个变量,实际上就是向系统申请指定字节数的空间.这些空间系统就不会再分配给别人了.
    当变量被回收的时候,代表变量占用的字节空间从此以后系统可以分配给别人使用了.
    但是字节空间中存储的数据还在.

    回收对象:
    所谓的对象的回收,指的是对象占用的空间可以分配给别人.
    当这个对象占用的空间没有分配给别人之前 其实对象数据还在.

  3. 僵尸对象
    1个已经被释放的对象,但是这个对象所占的空间还没有分配给别人.这样的对象叫做僵尸对象.

    我们通过野指针去访问僵尸对象的时候.有可能没问题 也有可能有问题.

    当僵尸对象占用的空间还没有分配给别人的时候.这是可以的.
    当僵尸对象占用的空间分配给了别人使用的时候 就不可以.

  4. 我们认为只要对象称为了僵尸对象,无论如何 都不允许访问了.
    就希望如果访问的是僵尸对象,无论如何报错.

    僵尸对象的实时检查机制.可以将这个机制打开. 打开之后. 只要访问的是僵尸对象,无论空间是否分配 就会报错.

  5. 为什么不默认打开僵尸对象检测.

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

  6. 使用野指针访问僵尸对象会报错. 如何避免僵尸对象错误..

    当1个指针称为野指针以后.将这个指针的值设置nil

    当1个指针的值为nil 通过这个指针去调用对象的方法(包括使用点语法)的时候.不会报错. 只是没有任何反应.
    但是如果通过直接访问属性 -> 就会报错.

  7. 无法复活1个僵尸对象.

单个对象的内存管理

  1. 内存泄露.

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

  2. 单个对象的内存泄露的情况.

    1). 有对象的创建,而没有对应的relase

    2). retain的次数和relase的次数不匹配.

    3). 在不适当的时候,为指针赋值为nil

    4). 在方法中为传入的对象进行不适当的retain

  3. 如何保证单个对象可以被回收

    1). 有对象的创建 就必须要匹配1个relase

    2). retain次数和release次数一定要匹配.

    3). 只有在指针称为野指针的时候才赋值为nil

    4). 在方法中不要随意的为传入的对象retain.

多个对象的内存管理

  1. 当属性是1个OC对象的时候. setter方法的写法.

    将传进来的对象赋值给当前对象的属性,代表传入的对象多了1个人使用,所以我们应该先为这个传入的对象发送1条retain消息 再赋值.

    当当前对象销毁的时候.代表属性指向的对象少1个人使用. 就应该在dealloc中relase

    代码写法:

    - (void)setCar:(Car *)car
      {
      _car = [car retain];
      }
    
    - (void)dealloc
      {
      [_car release];
      [super dealloc];
      }
    
  2. 当属性是1个OC对象的时候,setter方法照着上面那样写,其实还是有Bug的.

  3. 当为对象的这个属性多次赋值的时候.就会发生内存泄露.
    发生泄露的原因: 当为属性赋值的时候, 代表旧对象少1个人用.新对象多1个人使用.
    应该relase旧的 retain新的.

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

@property参数

  1. 在MRC的开发模式下.1个类的属性如果是1个OC对象类型的.那么这个属性的setter方法就应该按照下面的格式写.

    - (void)setCar:(Car *)car
      {
      if(_car != car)
      {
      [_car release];
      _car = [car retain];
      }
      }
    
    还要重写dealloc方法.
    
    - (void)dealloc
      {
      [_car release];
      [super delloc];
      }
    
    如果属性的类型不是OC对象类型的.不需要像上面那样写. 还是像之前那样写就OK了.
    
  2. @property

    1). 作用
    a. 自动生成私有属性.
    b. 自动生成这个属性的getter setter方法的声明.
    c. 自动生成这个属性的getter setter方法的实现.

    特别播报:
    生成的setter方法的实现中,无论是什么类型的,都是直接赋值.

  3. @property参数.

    1). @property可以带参数的.

    @property(参数1,参数2,参数3......)数据类型 名称;

    2). 介绍一下@property的四组参数.

    a. 与多线程相关的两个参数.
    atomic、nonatomic.

    b. 与生成的setter方法的实现相关的参数.
    assign、retain.

    c. 与生成只读、读写相关的参数
    readonly readwrite

    d. 是与生成的getter setter方法名字相关的参数.
    getter setter

  4. 介绍与多线程相关的参数.

    atomic: 默认值. 如果写atomic,这个时候生成的setter方法的代码就会被加上一把线程安全锁.
    特点: 安全、效率低下.
    nonatomic: 如果写nonatomic 这个时候生成的setter方法的代码就不会加线程安全锁.
    特点: 不安全,但是效率高.

    建议: 要效率. 选择使用nonatomic 在没有讲解多线程的知识以前 统统使用nonatomic

  5. 与生成的setter方法的实现相关的参数.

    assign: 默认值 生成的setter方法的实现就是直接赋值.

    retain: 生成的setter方法的实现就是标准的MRC内存管理代码.
    也就是. 先判断新旧对象是否为同1个对象 如果不是 release旧的 retain新的.

    当属性的类型是OC对象类型的时候,那么就使用retain
    当属性的类型是非OC对象的时候,使用assign.

    千万注意:
    retain参数.只是生成标准的setter方法为标准的MRC内存管理代码 不会自动的再dealloc中生成relase的代码.
    所以, 我们还要自己手动的在dealloc中release

  6. 与生成只读、读写的封装.

    readwrite: 默认值.代表同时生成getter setter
    readonly: 只会生成getter 不会生成setter

  7. 生成getter、setter方法名称相关的参数.

    默认情况下.@property生成的getter setter方法的名字都是最标准的名字.
    其实我们可以通过参数来指定@property生成的方法的名字.

    getter = getter方法名字 用来指定@property生成的getter方法的名字.
    setter = setter方法名字.用来指定@property生成的setter方法的名字. 注意.setter方法是带参数的 所以要加1个冒号.

    记住:如果使用getter setter修改了生成的方法的名字.
    在使用点语法的时候.编译器会转换为调用修改后的名字的代码.

    修改生成的getter setter方法名字. 因为默认情况下生成的方法的名字已经是最标准的名字了.
    所以.一般情况下不要去改.

1). 无论什么情况都不要改setter方法的名字. 因为默认情况下生成的名字就已经是最标准的了.
2). 什么时候修改getter方法的名字.当属性的类型是1个BOOL类型的时候.就修改这个getter的名字以is开头 提高代码的阅读性.

------总结-------

  1. 与多线程相关的参数: 用nonatomic

  2. 与生成的setter方法实现相关的参数
    属性的类型是OC对象的时候 使用retain
    属性的类型是非OC对象的时候 使用assign

  3. 只读 读写.
    如果你希望生成的封装是只读封装 那么就使用readonly
    如果希望读写封装 readwrite

  4. 1). 无论什么情况都不要改setter方法的名字. 因为默认情况下生成的名字就已经是最标准的了.
    2). 什么时候修改getter方法的名字.当属性的类型是1个BOOL类型的时候.就修改这个getter的名字以is开头 提高代码的阅读性.

------使用参数注意-----

  1. 同1组参数只能使用1个.
    getter setter可以同时使用.

  2. 参数的顺序可以随意.

@class

  1. 当两个类相互包含的时候. 当Person.h中包含Book.h 而Book.h中又包含Person.h
    这个时候,就会出现循环引用的问题. 就会造成无限递归的问题,而导致无法编译通过.

  2. 解决方案:
    其中一边不要使用#import引入对方的头文件.
    而是使用@class 类名; 来标注这是1个类.这样子就可以在不引入对方头文件的情况下,告诉编译器这是1个类.

    在.m文件中再#import对方的头文件.就可以使用了.

  3. @class与#import的区别

    1). #import是将指定的文件的内容拷贝到写指令的地方.

    2). @class 并不会拷贝任何内容. 只是告诉编译器,这是1个类,这样编译器在编译的时候才可以知道这是1个类.

循环retain

  1. 当两个对象相互引用的时候.
    A对象的属性是B对象 B对象的属性是A对象.
    这个时候 如果两边都使用retain 那么就会发生内存泄露.

  2. 解决方案: 1端使用retain 另外1端使用assign 使用assign的那1端在dealloc中不再需要release了.

自动释放池(了解)

自动释放池的原理.

存入到自动释放池中的对象,在自动释放池被销毁的时候.会自动调用存储在该自动释放池中的所有对象的release方法.

可以解决的问题:
将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.
因为池子销毁的时候 就会自动的调用池中所有的对象的relase。

自动释放池的好处: 将创建的对象存储到自动释放池中,不需要再写release

如何创建自动释放池.
@autoreleasepool

{

}
这对大括弧代表这个自动释放池的范围.
如何将对象存储到自动释放池之中

在自动释放池之中调用对象的autorelease方法.就会将这个对象存入到当前自动释放池之中.

这个autorealse方法返回的是对象本身. 所以,我们可以这么写

@autoreleasepool

{

Person *p1 = [[[Person alloc] init] autorelease];

}

这个时候,当这个自动释放池执行完毕之后,就会立即为这个自动释放池中的对象发送1条release消息.

目前为止,我们感受到得autorelase的好处:
创建对象,调用对象的autorelase方法 将这个对象存入到当前的自动释放池之中.

我们就不需要再去relase 因为自动释放池销毁的时候 就会自动的调用池中所有对象的relase

使用注意

1). 只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池之中.

如果只是将对象的创建代码写在自动释放之中,而没有调用对象的autorelease方法.是不会将这个对象存储到这个自动释放池之中的.

2). 对象的创建可以在自动释放池的外面,在自动释放池之中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池之中.

3). 当自动释放池结束的时候.仅仅是对存储在自动释放池中的对象发送1条release消息 而不是销毁对象.

4). 如果在自动释放池中,调用同1个对象的autorelease方法多次.就会将对象存储多次到自动释放池之中.

在自动释放池结束的时候.会为对象发送多条release消息.那么这个是就会出现僵尸对象错误.
所以,1个自动释放池之中,只autorelease1次,只将这个对象放1次, 否则就会出现野指针错误.

5). 如果在自动释放池中,调用了存储到自动释放中的对象的release方法.

在自动释放池结束的时候,还会再调用对象的release方法.
这个时候就有有可能会造成野指针操作.

也可以调用存储在自动释放池中的对象的retain方法.

6). 将对象存储到自动释放池,并不会使对象的引用计数器+1

所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了.

7). 自动释放池可以嵌套.

调用对象的autorelease方法,会讲对象加入到当前自动释放池之中
只有在当前自动释放池结束的时候才会像对象发送release消息.

autorelease的规范.

0). 创建对象,将对象存储到自动释放池之中. 就不需要再去手动的realse。

1). 类方法的第1个规范:

一般情况下,要求提供与自定义构造方法相同功能的类方法.这样可以快速的创建1个对象.

2). 我们一般情况下,写1个类. 会为我们的类写1个同名的类方法,用来让外界调用类方法来快速的得到1个对象.

规范:使用类方法创建的对象,要求这个对象在方法中就已经被autorelease过了.

这样,我们只要在自动释放池中, 调用类方法来创建对象, 那么创建的对象就会被自动的加入到自动释放中.

提供1个类方法来快速的得到1个对象.
规范
a. 这个类方法以类名开头. 如果没有参数就直接是类名 如果有参数就是 类名WithXX:
b. 使用类方法得到的对象,要求这个对象就已经被autorelease过了.

-(instancetype)person{ return [[[self alloc] init] autorelease]; }

这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.

@autoreleasepool
{

Person *p1 = [Person person];
//这个p1对象已经被autorelase过了.不需要再调用autorelase
//这个p1对象就被存储到当前自动释放池之中.
}//当自动释放池结束.就会为存储在其中的p1对象发送release消息.

实际上Apple的框架中的类也是遵守这个规范的.

通过类方法创建的对象都是已经被autorelease过的了.

所以,我们也要遵守这个规范. 类方法返回的对象也要被autorealse过.

以后,我们凡事创建对象是调用类方法创建的对象 这个对象已经是被autorelease过的了.

arc机制概述

什么是ARC

Automatic Reference Counting,自动引用计数. 即ARC.
顾名思义:系统自动的帮助我们去计算对象的引用计数器的值,

可以说是WWDC2011和iOS5引入的最大的变革和最激动人心的变化.
ARC是新的LLVM3.0编译器的一项特性,使用ARC,可以说一举解决了广大iOS开着所憎恨的手动管理内存的麻烦.

在程序中使用ARC非常简单,只需要像往常那样编写代码.
只不过永远不要写retain、release、autorelease 永远要手动的调用dealloc 这三个关键字就好,这是ARC的最基本的原则.
当ARC开启时, 编译器会自动的在合适的地方插入retain、release、autorelase代码.
编译器自动为对象做引用计数. 而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用了ARC).

需要特别注意的是: ARC是编译器机制. 在编译器编译代码的时候,会在适时的位置加入retain、release和autorealse代码.

ARC机制下,对象何时被释放

本质: 对象的引用计数器为0的时候,自动释放.

表象: 只要没有强指针指向这个对象,这个对象就会立即回收.

强指针与弱指针.

强指针: 默认情况下,我们声明1个指针 这个指针就是1个强指针.
我们也可以使用__strong来显示的声明这是1个强指针.

Person *p1; 这是1个强指针. 指针默认情况下都是1个强指针.
__strong Person *p2; 这也是1个强指针.使用__strong来显示的声明强指针.

弱指针: 使用__weak标识的指针就叫做弱指针.

无论是强指针还是弱指针,都是指针,都可以用来存储地址,这1点没有任何区别 。
都可以通过这个指针访问对象的成员.
唯一的区别就是在ARC模式下.他们用来作为回收对象的基准.

如果1个对象没有任何强类型的指针指向这个对象的时候,对象就会被立即自动释放

确认程序是否开启ARC机制.

1).默认情况下,Xcode开启ARC机制.
2).ARC机制下,不允许调用retain、relase、retainCount、autorelease方法.
3).在dealloc中 不允许[super dealloc];

演示第1个ARC案例
int main(int argc, const char * argv[])

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1个强指针.

//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.

NSLog(@"------");

}//当执行到这里的时候.p1指针被回收,那么Person对象就没有任何

//强指针指向它了. 对象就在这被回收.

return 0;

}

第一个arc程序

ARC下的单个对象的内存管理.

在ARC的机制下: 当1个对象没有任何的强指针指向它的时候 这个对象就会被立即回收.

1). 当指向对象的所有的强指针被回收的时候,对象就会被立即回收.

int main(int argc, const char * argv[])

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1个强指针.

Person *p2 = p1;//p2也是个强指针.p1和p2都指向Person对象.

//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.

NSLog(@"------");

}//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何

//强指针指向它了. 对象就在这被回收.

return 0;

}

2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.

int main(int argc, const char * argv[])

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1个强指针.

//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.

p1 = nil;//当执行到这句话的时候.p1赋值为nil.

//p1指针不再执行Person对象.

//Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.

NSLog(@"------");

}

return 0;

}

这两种情况就叫做没有任何强指针指向对象.
1). 指向对象的所有强指针被回收掉
2). 指向对象的所有的强指针赋值为nil

强指针与弱指针.

1). 强指针与弱指针的声明.

默认情况下,所有的指针都是强类型的,也就是说我们之前声明的指针变量都是强类类型的

p1指针是强类型的,因为默认情况下指针都是强类型的.
Person *p1 = [[Person alloc] init];

不过我们可以使用__strong来显示的标识指针是强类型指针.
__strong Person *p2 = [Person new];
这个时候p2指针类型是强指针类型的.其实写不写__strong都是强类型的指针.

指针类型也可以是弱指针类型.
使用__weak标识指针的类型是弱类型指针.
__weak Person *p3 = p2;
这个时候,p3指针就是1个弱类型的指针. p3弱指针也指向p2指针指向的对象.

在操作对象的时候,通过强指针或者弱指针都可以操作,没有任何区别.

2). ARC模式下的对象回收标准

ARC机制下释放1个对象的标准是: 没有任何强指针指向对象的时候,对象就会被释放.
如果这个时候有弱指针指向,也会被释放.

int main(int argc, const char * argv[])

{

@autoreleasepool

{

//使用strong来标识p1指针是强类型的,其实不写strong也是强类型的.

__strong Person *p1 = [[Person alloc] init];

//使用__weak标识指针p2的类型是弱类型指针.

__weak Person *p2 = p1;

//这个时候,p2指针和p1指针都指向Person对象.

//这个时候如果设置p1的值为nil

p1 = nil;

//这个时候Person对象只有被1个弱指针p2指向,没有任何强指针指向

//所以Person对象在这里被回收.

}

return 0;

}

3).最重要的1点:不能创建对象用1个弱指针存储这个对象的指针.
这样的话,刚创建出来的对象,就没有任何强指针指向,创建出来就会被回收.

int main(int argc, const char * argv[])
{
@autoreleasepool
{
//创建1个对象,将这个对象的地址赋值给1个弱指针
//后果就是创建出来的这个对象没有被任何强指针指向.
//刚创建出来就会被释放.
__weak Person *p1 = [[Person alloc] init];

}
return 0;
}

4). 在ARC机制下. 当对象被回收的时候. 原来指向这个对象的弱指针会被自动设置为nil

arc机制下的多个对象的内存管理

  • ARC机制下的对象的回收的标准: 当没有任何强类型的指针指向对象的时候,这个对象就会被立即回收.

  • 强类型指针 弱类型指针.

  • 什么情况下叫做对象没有强指针向指向.

    1). 指向对象的强指针被回收.

    2). 指向对象的强指针被赋值为nil

  • 在ARC的机制下,@property参数不能使用retain

    因为retain代表生成的setter方法是MRC的标准的内存管理代码.
    而我们在ARC的机制下 不需要这些代码.

    所以,在ARC机制下的setter方法 什么都不需要做.直接赋值就可以了.

  • ARC机制下,我们关注的重点.

    当1个类的属性是1个OC对象的时候.这个属性应该声明为强类型的还是弱类型的.
    很明显,应该声明为1个强类型的.

    问题来了?

    如何控制@property生成的私有属性,是1个强类型的还是1个弱类型的呢?

    使用参数, strong和weak

    @property(nonatomic,strong)Car *car;
    代表生成的私有属性_car 是1个强类型的.

    @property(nonatomic,weak)Car *car;
    代表生成的私有属性_car 是1个弱类型的.

    如果不写,默认是strong.

  • 使用建议.

    1). 在ARC机制下.如果属性的类型是OC对象类型的.绝大多数场景下使用strong
    2). 在ARC机制下.如果属性的类型不是OC对象类型的.使用assign

    3). strong和weak都是应用在属性的类型是OC对象的时候. 属性的类型不是OC对象的时候就使用assign.

    在ARC机制下,将MRC下的retain换位strong

    @property(nonatomic,strong)Car *car;
    做的事情:
    1). 生成私有属性.并且这个私有属性是strong
    2). 生成getter setter方法的声明
    3). 生成getter setter方法的声明

    setter的实现:直接赋值.

ARC 循环引用

在ARC机制下.当两个对象相互引用的时候.如果两边都使用strong 那么就会先内存泄露.

解决方案: 1端使用strong 1端使用weak

@property 参数总结

  1. 开发程序分为ARC和MRC

  2. 与多线程相关的参数.
    atomic : 默认值 安全,但是效率低下.
    nonatomic: 不安全,但是效率高.

    无论在ARC还是在MRC都可以使用.
    使用建议: 无论是ARC还是MRC 都使用nonatomic

  3. retain:

    只能用在MRC的模式下.代表生成的setter方法是标准的内存管理代码.
    当属性的类型是OC对象的时候.绝大多数情况下使用retain. 只有在出现了循环引用的时候1边retain 1边assign

  4. assign:
    在ARC和MRC的模式下都可以使用assign.
    当属性的类型是非OC对象的时候 使用assign.

  5. strong:
    只能使用在ARC机制下. 当属性的类型是OC对象类型的时候,绝大多数情况下使用strong]
    只有出现了循环引用的时候, 1端strong 1端weak

  6. weak:

    只能使用在ARC机制下. 当属性的类型是OC对象的时候. 只有出现了循环引用的时候, 1端strong 1端weak

  7. readonly readwrite

    无论是ARC还是MRC 都可以使用.

  8. setter getter 无论在ARC下还是在MRC下都可以改.

在ARC机制下.原来使用retain的用strong
出现循环引用的时候. MRC: 1边retain 1边assign ARC: 1边strong 1边weak

MRC与ARC的兼容

  1. 有可能会遇到的问题.

    ​程序使用的是ARC机制开发的,但是其中的某些类使用的是MRC.

  2. 使用命令. ` -fno-objc-arc

补充

  1. ARC机制和垃圾回收机制的区别.

GC: 程序在运行的期间,有1个东西叫做垃圾回收器.不断的扫描堆中的对象是否无人使用.

Person *p1 = [Person new];
p1 = nil;

ARC: 不是运行时. 在编译的时候就在合适的地方插入retain......
插入的代码足以让对象无人使用的时候 引用计数器为0

你可能感兴趣的:(OC基础(六)——内存管理)