IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    OC基础 05-手动内存管理(MRC) - S了个J

    S了个J发表于 2015-11-02 15:48:00
    love 0

    ---恢复内容开始---

    MRC: Manul(手动) Reference(引用) Counting(计数)

     什么是手动引用计数?

     所有对象的内容都需要我们手动管理, 需要程序员自己编写release/retain等代码

     内存管理的原则就是有加就有减

     也就是说, 一次alloc/new/copy对应一次release, 一次retain对应一次relese

     内存管理的重要性:

      移动设备的内存极其有限,每个app的占用内存也是有限制的.

    当我们创建一个OC对象,定义一个变量,调用一个函数或者方法,都会增加app的内存占用.

    当app内存占用极多的时候,系统会发出内存警告,而这时候就需要回收一些不需要在使用的内存空间,比如"对象""变量";

    如果app占用内存极大的时候,会造成程序崩溃,闪退等现象,极大的影响了用户体验


     

    什么是内存管理?

      >对内存进行管理

    内存管理涉及的操作?

      >分配内存:创建对象.增加内存占用

      >清楚内存:释放对象,减少内存占用

    OC中内存管理的范围?

      >任何继承NSObject的对象

      >对非对象类型无效

    为什么只有OC对象才需要内存管理,本质是什么?

      >OC对象存放于堆中

      >非OC对象一般存放于栈中(存放于栈中的由系统自动回收)

     

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

     


    引用计数器

      >每个OC对象都有一个计数器

      >它是一个整数

      >可以理解为"对象被引用的次数"或者"有多少人再用它"

    引用计数器的作用?

      >系统根据对象的引用计数器判断对象是否需要被回收

      >当对象的引用计数器为0时,对象所占用的内存空间就会被系统回收

      >如果对象的引用计数器不为0时,那么在整个程序运行期间,对象所占用的内存空间就不会被释放,除非整个程序已经退出.

    任何一个对象刚创建出来,期引用计数器为1

      >当使用alloc new copy创建对象时,对象的引用计数器默认就为1

    引用计数器的常见操作

      >给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)

      >给对象发送一条release消息, 可以使引用计数器值-1

      >给对象发送retainCount消息, 可以获得当前的引用计数器值

    注意: release并不代表销毁\回收对象, 仅仅是计数器-1


     

    dealloc方法

      >当一个对象的引用计数器值为0时,这个对象即将被销毁,其占用的内存被系统回收

      >对象即将被销毁时系统会自动给对象发送一条dealloc消息 (因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)

    dealloc方法的重写

      >一般会重写dealloc方法,在这里释放相关资源,dealloc就是对象的遗言

      >一旦重写了dealloc方法, 就必须调用[super dealloc],并且放在最后面调用

    注意

      >不能直接调用dealloc方法

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


     

    野指针/空指针/僵尸对象

    野指针

      >指向不可用内存(僵尸对象)的的指针,我们称之为野指针

      >给野指针发消息会报EXC_BAD_ACCESS错误

     

    空指针

      >没有指向存储空间的指针(里面存的是nil, 也就是0)

      >给空指针发消息是没有任何反应的

      >避免野指针错误的常见办法(在对象被销毁之后, 将指向对象的指针变为空指针)

    僵尸对象

      >已经被释放的对象,不能在被使用的对象

    Xcode打开僵尸对象检控

     


    内存管理原则
    • 苹果官方规定的内存管理原则

      • 谁创建谁release :

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

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

      • 有加就有减
      • 曾经让对象的计数器+1,就必须在最后让对象计数器-1

    多对象内存管理
    • 单个对象的内存管理, 看起来非常简单

    • 如果对多个对象进行内存管理, 并且对象之间是有联系的, 那么管理就会变得比较复杂

    • 总的来说, 有这么几点管理规律

      • 只要还有人在用某个对象,那么这个对象就不会被回收
      • 只要你想用这个对象,就让对象的计数器+1
      • 当你不再使用这个对象时,就让对象的计数器-1

    set方法内存管理
    • (1)retain需要使用的对象
    • (2)release之前的对象
    • (3)只有传入的对象和之前的不同才需要release和retain
    - (void)setRoom:(Room *)room
    {
    // 避免过度释放
    if (room != _room)
    {
    // 对当前正在使用的房间(旧房间)做一次release
    [_room release];

    // 对新房间做一次retain操作
    _room = [room retain];
    }
    }

     

    dealloc方法的内存管理

    - (void)dealloc
    {
    // 当人不在了,代表不用房间了
    // 对房间做一次release操作
    [_room release];
    [super dealloc];
    }

    多个对象示例:

    要求:

    /*

    微博类(Status)

    文字内容(text)
    配图(picture)
    发表时间(createTime)
    作者(author)
    转发的说说(repostStatus)
    评论数(commentCount)
    转发数(retweetCount)
    赞数(likeCount)
    作者类(Author)

    昵称(name)
    头像(icon)
    生日(birthday)
    账号(account)
    账号(Account)

    账号名称(name)
    账号密码(pwd)
    账号注册时间(registerTime)
    模拟场景:
    老王在2010-1-1 17:56:34注册了一个账号
    (名称:xiaomage@520it.com, 密码:haomage)
    老王的生日是1986-3-8 18:18:18
    老王发布一条说说
    文字内容 @“爆米花手机比逼格更有逼格”
    图片 @“phone.png”
    发表时间: 2015-6-20 10:23:23
    作者: 老王
    被转发的说说: 没有
    评论数: 100
    转发数: 90
    点赞数: 200

    王大锤在2012-8-8 19:26:54注册了一个账号
    (名称:dachuimeimei@520it.com, 密码:654321)
    王大锤的生日是1989-9-6 14:16:28
    王大锤在2015-6-21 20:47:09时,转发了张三之前发布的说说, 并且还附带了一句话:@“真的很有逼格”

    */

    Account.h

    #import <Foundation/Foundation.h>

    typedef
    struct {

    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    } MyDate;
    @interface Account : NSObject
    // 账号名称
    @property(nonatomic, retain)NSString *eaml;
    // 账号密码
    @property(nonatomic, retain)NSString *pwd;
    // 账号注册时间
    @property(nonatomic, assign)MyDate registerTime;
    @end

    Account.m

    #import "Account.h"

    @implementation Account

    - (void)dealloc {

    NSLog(
    @"%s", __func__);
    self.eaml
    = nil;
    self.pwd
    = nil;
    [super dealloc];
    }
    @end

    Author.h

    #import <Foundation/Foundation.h>
    #import "Account.h"

    @interface Author : NSObject
    // 昵称
    @property(nonatomic, retain)NSString *name;
    // 头像
    @property(nonatomic, retain)NSString *icon;
    // 生日
    @property(nonatomic, assign)MyDate birthday;
    // 账号
    @property(nonatomic, retain)Account *account;
    @end

    Author.m

    #import "Author.h"

    @implementation Author
    - (NSString *)description {

    return [NSString stringWithFormat:@"\n作者:%@\n", _name];
    }
    - (void)dealloc {

    NSLog(
    @"%s", __func__);

    self.name
    = nil;
    self.icon
    = nil;
    self.account
    = nil;
    [super dealloc];
    }
    @end

    Status.h

    #import <Foundation/Foundation.h>
    #import "Author.h"

    @interface Status : NSObject

    // 文字内容
    @property(nonatomic, retain)NSString *text;
    // 配图
    @property(nonatomic, retain)NSString *picture;
    // 发表时间
    @property(nonatomic, assign)MyDate createTime;
    // 作者
    @property(nonatomic, retain)Author *author;
    // 转发的微博
    @property(nonatomic, retain)Status *repostStatus;
    // 评论数
    @property(nonatomic, assign)int commentCount;
    // 转发数
    @property(nonatomic, assign)int retweetCount;
    // 赞数
    @property(nonatomic, assign)int likeCount;
    @end

    Status.m

    #import "Status.h"

    @implementation Status

    - (NSString *)description {

    return [NSString stringWithFormat:@"\n微博内容:%@\n发表时间:%d-%d-%d-%d-%d-%d\n评论:%d 转发:%d 点赞:%d",_text, _createTime.year, _createTime.month, _createTime.day, _createTime.hour, _createTime.minute, _createTime.second, _commentCount, _retweetCount, _likeCount];
    }

    - (void)dealloc {

    NSLog(
    @"%s", __func__);

    self.text
    = nil;
    self.picture
    = nil;
    self.repostStatus
    = nil;
    self.author
    = nil;
    [super dealloc];
    }
    @end

    main.m

    #import <Foundation/Foundation.h>
    #import "Status.h"

    int main(int argc, const char * argv[]) {
    @autoreleasepool {
    // 老王
    // 1.创建账号
    Account *lwAccount = [[Account alloc] init];
    lwAccount.eaml
    = @"xiaomage@520it.com";
    lwAccount.pwd
    = @"haomage";
    lwAccount.registerTime
    = (MyDate){2010, 1, 1, 17, 56, 34};

    // 2.创建作者
    Author *lwAuthor = [[Author alloc] init];
    lwAuthor.name
    = @"老王";
    lwAuthor.icon
    = @"lw.png";
    lwAuthor.birthday
    = (MyDate){1986, 3, 8, 18, 18, 18};
    lwAuthor.account
    = lwAccount;

    // 3.发微博
    Status *lwStatus = [[Status alloc] init];
    lwStatus.text
    = @"爆米花手机比逼格更有逼格";
    lwStatus.picture
    = @"phone.png";
    lwStatus.createTime
    = (MyDate){2015, 6, 20, 10, 23, 23};
    lwStatus.author
    = lwAuthor;
    lwStatus.repostStatus
    = nil;
    lwStatus.commentCount
    = 100;
    lwStatus.retweetCount
    = 90;
    lwStatus.likeCount
    = 200;


    // 王大锤
    Account *wdcAccount = [[Account alloc] init];
    wdcAccount.eaml
    = @"dachuimeimei@520it.com";
    wdcAccount.pwd
    = @"654321";
    wdcAccount.registerTime
    = (MyDate){2012, 8, 8, 19, 26, 54};

    Author
    *wdcAuthor = [[Author alloc] init];
    wdcAuthor.name
    = @"王大锤";
    wdcAuthor.icon
    = @"wdc.png";
    wdcAuthor.birthday
    = (MyDate){1989, 9, 6, 14, 16, 28};
    wdcAuthor.account
    = wdcAccount;

    Status
    *wdcStatus = [[Status alloc] init];
    wdcStatus.text
    = @"真的很有逼格";
    lwStatus.picture
    = nil;
    wdcStatus.createTime
    = (MyDate){2015, 6, 21, 20, 47, 9};
    wdcStatus.author
    = lwAuthor;
    wdcStatus.repostStatus
    = lwStatus;
    wdcStatus.commentCount
    = 0;
    wdcStatus.retweetCount
    = 0;
    wdcStatus.likeCount
    = 0;


    NSLog(
    @"%@%@", lwStatus, lwAuthor);

    [lwAccount release];
    [lwAuthor release];
    [lwStatus release];

    [wdcAccount release];
    [wdcAuthor release];
    [wdcStatus release];
    }
    return 0;
    }

    @property参数

    控制set方法的内存管理

      >retain : release旧值,retain新值(用于OC对象)

      >assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)

      >copy : release旧值,copy新值(一般用于NSString *)

    控制需不需要生成set方法

      >readwrite :同时生成set方法和get方法(默认)

      >readonly :只会生成get方法

    多线程管理

      >atomic :性能低(默认)

      >nonatomic :性能高

    控制set方法和get方法的名称

      >setter : 设置set方法的名称,一定有个冒号:

      >getter : 设置get方法的名称

      >注意: 不同类型的参数可以组合在一起使用


     

    循环retian基本概念

    循环retain的场景

      >比如A对象retain了B对象,B对象retain了A对象

    循环retain的弊端

      >这样会导致A对象和B对象永远无法释放

    循环retain的解决方案

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


    本文链接:OC基础 05-手动内存管理(MRC),转载请注明。



沪ICP备19023445号-2号
友情链接