OC加强DAY01 - 内存管理MRC

OC加强DAY01 - 内存管理

内存管理

内存管理的概述
内存是用来存储数据的
  1. 如何将数据存储到内存中.
  1. 声明变量就可以将数据存储到内存中
  2. 存储到内存中的数据应该如何被释放呢?
  1. 内存中的五大区域
  1. 局部变量的作用域执行完就回收
  2. 手动申请的字节空间需要调用free函数来释放
  3. 一旦初始化,未初始化的全局变量和静态变量就会从BSS段中回收掉.转存到数据段中
  4. 数据段的数据直到程序结束才回收
  5. 代码段也是程序结束才回收
  1. 堆区中的OC对象一定是要回收的
  1. 如果不回收马上就会占的慢慢的
  2. 40M 为程序发送一条警告
  3. 45M 再发送一条警告
  4. 120M直接杀死.
  1. 堆区中的OC对象不会自动回收

除了堆区其他区域中的数据,都是系统自动释放的.堆区中的OC对象是不会自动释放的,直到程序结束

  1. ==内存管理的范围==

只需要管理内存中的OC对象的释放,也就是任何NSObject的对象

  1. 对象什么时候需要释放呢?
  1. 如果OC对象有人在使用,千万不能释放
  2. 只有当这个OC对象无人使用的时候,才可以释放
7. 引用计数器
  1. 每一个OC对象都有一个属性.叫做retainCount
  2. 翻译中文:引用计数器
  3. 类型是: unsigned long 占据8个字节.
  4. 作用:这个对象有多少个人在使用.
  5. 当我们新创建一个独享出来的时候,引用计数器的默认值是1
  • 当这个对象多一个人使用的时候,就应该先让这个对象的计数器+1
  • 当这个对象少一个人使用的时候,就应该让这个对象的引用计数器-1
  • 当这个对象的引用计数器值为0的时候,代表这个对象无人使用
  1. 这个时候,西永就会李奇回收这个对象.
  2. 回收这个对象的同时,会自动调用这个对象的dealloc
    方法
  1. 如何操作引用计数器
  1. 为对象发送一条retain消息.那么这个对象的计数器就会+1
  2. 发送一条releas消息.就会-1
  3. 发送retainCount消息.就可以得到计数器的值
  4. 加加减减,当值为0系统就会立即回收,会自动调用这个对象的dealloc方法
  1. 内存管理的分类
    • MRC
  1. MRC : Manual Reference Counting 手动引用计数
  2. 加加减减都是程序员手动执行
  • ARC
  1. Automatic Reference Counting 自动引用计数
  2. 不需要手动计数,程序员不用手动写代码,系统自动的来改变计数器的值
  • 今天重点学MRC.
  1. ==面试100%必考==
  2. 早期的APP是基于MRC开发的.
  3. 现在的iOS大牛都是从MRC成长过来的.
  4. ARC是基于MRC的.

我们以后,写项目:绝大多数都是ARC.

第一个MRC程序
  1. 从2011年开始,iOS5 苹果退出了ARC Xcode6开始 . 默认就是ARC
  2. 在对象回收的时候,会自动调用dealloc方法.

为了监视对象的回收,我们重写dealloc方法,重写dealloc方法必须调用父类的dealloc方法.并且放在最后调用

  1. 测试
- (void)dealloc
{
NSLog(@"名字叫做%@的人挂了",_name);
 [super dealloc];
}

[p1 retain]; //1
NSLog(@"%lu",p1.retainCount);
[p1 release]; //2
NSLog(@"%lu",p1.retainCount);
[p1 release]; //1
NSLog(@"%lu",p1.retainCount);

#### 内存管理的规则
1. 内存管理的重点

> 分清楚什么时候位对象发送retain让计数器+1,什么时候发release让计数器-1;

2. ==内存管理的原则==

> 1. 新创建一个对象,计数器的初始值是1.有对象的创建,就必须匹配一个releas 

```objc
HMPerson *p1 = [[HMPerson alloc] init];

[p1 release]; //在MRC下,有创建就要有回收




<div class="se-preview-section-delimiter"></div>

在ARC模式下严禁调用dealloc.否则就报错

  1. 谁负责[p1 retain] 谁就要负责[p1 release]
  2. retain 的次数要和 release 相匹配.
  3. 只有在多一个人使用这个对象的时候才[p1 retain],不要乱用.有始有终,有加就有减.
    • 一句话,谁创建,谁release,谁retain,谁release.
野指针和僵尸对象.
  1. ==野指针==
  1. C语言中的野指针指:声明一个指针变量,没有为这个指针变量初始化.这个指针变量的值就是垃圾值指向内存当中一块随机的空间.
  2. OC中的野指针:一个指针指向的对象已经被释放了.那么这个指针就叫做野指针.
HMPerson* p1 = [[HMPerson alloc] init];
[p1 release];//这句话执行完以后p1就是野指针了




<div class="se-preview-section-delimiter"></div>
  1. 对象的回收

    1. 声明一个变量,就是指在内存中申请指定字节数的连休空间来使用.
    2. 变量的回收就是,变量所占用的空间可以分配给别人,字节中的数据仍然还在
    3. 对象的回收也是如此,并且系统未将空间分配给别人时,数据也扔在内存中
  2. ==僵尸对象==:

指的是已经被回收但是这个对象的数据还在内存中.

HMPerson* p1 = [[HMPerson alloc] init];
[p1 release];//这句话执行完以后p1就是野指针了
[p1 sayHi];
[p1 sayHi];
[p1 sayHi];
[p1 sayHi];//有可能说到这里就挂了,前面还能sayHi
[p1 sayHi];//那么这块空间应该就已经分给别人了.

//僵尸对象有可能可以访问,也有可能不能访问.




<div class="se-preview-section-delimiter"></div>
  1. 一个对象一旦成为了僵尸对象后,无论如后都不应该使用了.都不能用了!
  1. 可以开启僵尸对象的实时检测:
  2. 当我们开启检测,通过一个指针去访问,只要是僵尸对象,就不能访问,无论还在不在.
  3. 只要访问僵尸对象控制台就输出错误.
  4. 为什么不默认开启检测呢?

一旦开启检测,每次通过指针访问都会检测,无疑会浪费性能

  1. 僵尸对象是无法复活的.
  1. 一个已经被release干掉的对象 是不能通过 retain 复活的
  2. 僵尸对象的引用计数器确实为0,只不过再访问这个对象就算是1也不代表什么
单个对象的内存管理
  1. 当我们通过野指针区访问僵尸对象的时候,如果开启了检测一定会报错
  1. 为了防止报错,在对象release成为野指针以后就为指针赋值一个nil.
  2. ==指向nil的对象调用方法不会报错,什么都不会法神==
  1. ==内存泄露==

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

  1. 单个对象发生内存泄露的情况.
  1. 有对象创建,没有匹配相应的p1.release//点语法就是调用方法
  2. retain的次数和release的次数不匹配.
  3. 在不适当的时候,为指针赋值为nil
  4. 在方法当中不适当的为传入对象[per retain]
  1. 如何避免单个对象发生内存泄露
  1. 有对象的创建,就必须匹配一个release
  2. retain的次数要和release的次数相同
  3. 不要随便为一个指针赋值为nil,除非是一个野指针
  4. 在方法中,不要随意的堆闯入的对象进行retain.
多个对象的内存管理
在多个对象中管理 凤姐开车去拉萨
  1. 当车作为凤姐的属性时,车不能回收.
  1. 将bmw对象赋值给fengJie对象的_car属性,代表对象多一个人使用
  2. set方法做的事情
- (void)setCar:(NSCar *)car
{
    //多一个对象调用car就应该为他发送一条retain
    _car = [car retain];
    //为car传送retain信息,返回值是car本身.
}




<div class="se-preview-section-delimiter"></div>
  1. 调用者凤姐delloc的时候,就应该为调用的对象release一次.
- (void)delloc
{
    NSSlog(@"名字叫做%@的人死了",_name);
    [_car release];//调用者死之前就发送一条releas信息,表示不再用了.
}   [super delloc];//调用父类的delloc,自行了断.




<div class="se-preview-section-delimiter"></div>
多个对象管理 冰冰开车 换车
//1. 创建奔驰,冰冰
NSPersin* p1 = [NSPerson personWithName:@"冰冰"];
NSCar* benz = [NSCar carWithSpeed:120];

//3. 这个时候有钱了换车保时捷
NSCar* prosche = [NSCar carWithSpeed:300];
p1.car = porsche;

//2. 创建完立即跟一个release
[benz release];
[p1 release];
//4. 冰冰释放了,同时现在开的保时捷释放了,奔驰泄露了
[porsche release];




<div class="se-preview-section-delimiter"></div>
  1. 当换车的时候就会发生内存泄露
  2. 所以让set方法换车的时候多做一件事
  1. 应该先[_car release];代表旧车不再使用
  2. 新车_car = [car retain];,代表新车多一个人用
  1. dealloc方法不能忘,还是要重写.

  2. 多个对象调用一个对象

验证多个对象调用一个对象,刚才的写法是没有问题的

上面的写法还存在一些问题
NSCar* bmw = [NSCar carWithSpeed:200];
NSPerson* xiaoMing = [NSPerson personWithName:@"小明"];
xiaoMing.car = bmw;
[xiaoMing drive];
//在这里release后计数为1
[bmw release];
//2. 改装时速到400
bmw.speed = 400;
//3. 重新赋给小明,这个时候要先release,计数为0,立即成为僵尸对象.
xiaoMing.car = bmw//把僵尸对象给小明赋值就会出错.

[xiaoMing release];//1. 先创建的对象后release




<div class="se-preview-section-delimiter"></div>
  1. 如果赋值的对象是同一个对象,不需要release.
  2. 所以set方法要先if(_car != car)再进行release.
  3. 如果是同一个对象就什么都不用做了.

    • 只有属性的类型是一个OC对象的时候才需要这么写

普通C语言类型就不需要了

- (void)setCar:(HMCar *)car
{
    if(_car != car)
    {
        [_car release];
        _car = [car retain];
    }
}
@property
  1. 在MRC里面自动生成的setter getter 方法都是直接赋值代码.
  2. @preperty可以带参数,带不同的参数可以实现一些特别的功能
  1. 语法@preperty(参数1,参数2,参数3...)数据类型 名称;
  2. 与多线程相关的参数可以带atomic,nonatomic.
  3. 与生成setter方法实现相关的参数assign,retain.
  4. 是否生成只读,读写的封装readwrite
  5. 指定生成的getter setter方法的名称
  1. 和多线程相关的参数,加锁不加锁.
  1. atomic是默认值,不写默认就是他.生成的set get方法是很安全的,但是性能低下
  2. nonatomic生成的方法不安全,但是效率高.
    推荐使用nonatomic 首先是要快!!(iOS)一般都是这个
    @property(nonatomic) NSString *name;
  1. 与生成setter方法相关的参数
  1. assign:默认值,生成的setter方法的实现中就是直接赋值.仅此`
  2. retain:生成的setter方法实现中是标准的MRC内存管理代码
  3. @property(nonatomic,retain) NSCarg *car;
  4. 当属性的类型是oc对象的时候就是用retain,非oc对象就使用release.
  1. 是否生成只读或者读写的封装

1.readwrite: 默认值, 同时生成getter setter
2.readonly: 只生成get,不生成getter
3.@property(nonatomic,retain,readonly) NSString *name;

  1. 指定生成的setter getter方法的名称
  1. 指定生成的getter setter方法的名称

    @property(nonatomic,retain,getter = sbsb ,setter = hbh:) NSString *name;
    //set带参数,所以set名字后面跟冒号:
    <ul><li>(void)hbh:(NSString *)name;</li>
    <li>(NSString *)sbsb;
    //无论什么情况下都不要去改set方法名字.
    //get情况下一般也不该,只有一种
    @property(nonatomic,assign,get=isGoodMan) BOOL goodMan
    //只有属性的类型是BOOL类型的时候才去改名字,is开头提高代码阅读性.
补充两个重点
  1. 与多线程相关的两个参数.

  2. 生成的setter方法实现相关的参数:

    1. assign : 当属性的类型是非oc对象时
    2. retain : 属性是oc对象是
    3. 只会自动生成set get 
    4. dealloc还是自己写
    
    
    @property(nonatomic,retain)NSString *name;
    @property(nonatomic,retain)HMCar *car;
  3. 同一组参数只能用一个

  4. 顺序可以换.一般先写nonatomic,再写retai
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值