OC基础 05-手动内存管理(MRC)

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

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注册了一个账号
(名称:[email protected], 密码:haomage)
老王的生日是1986-3-8 18:18:18
老王发布一条说说
文字内容 @“爆米花手机比逼格更有逼格”
图片 @“phone.png”
发表时间: 2015-6-20 10:23:23
作者: 老王
被转发的说说: 没有
评论数: 100
转发数: 90
点赞数: 200

王大锤在2012-8-8 19:26:54注册了一个账号
(名称:[email protected], 密码: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 = @"[email protected]";
        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 = @"[email protected]";
        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

时间: 2024-10-14 07:01:34

OC基础 05-手动内存管理(MRC)的相关文章

iOS开发之oc(十一)--内存管理MRC

掌握内容 >理解内存管理的原理 >掌握手动内存管理MRC >掌握内存管理在实际工程中的使用 (一) 1.理解部分 1.1内存管理 (传统内存管理demo) 1.1.1内存管理做了一件什么事? 内存管理就是确保开辟的堆空间得到正确的释放 如果堆空间没有释放,称为内存泄露 使用已释放的堆空间,称为提前释放 重复释放同一个空间,称为重复释放 1.1.2传统内存管理的困境 (1)当我们要释放一个堆,首先要确定使用这个堆的指针,都访问完毕,避免提前释放. (2)释放指针指向的堆空间,首先要确定那些

黑马程序员---OC基础6【内存管理】【手动内存管理】【单、多个对象的内存管理】【*@property参数】【@class的使用】【NSString类的内存管理】【autorelease使用】

------- iOS培训.Android培训.Java培训.期待与您交流! ---------- [内存管理] 1.内存管理概念 由于移动设备内存及其有限,所以每个app所占的内存也是有限的 需要回收一些不使用的空间 2.OC内存管理的范围 管理任何继承NSOject的对象,对其他的基本数据类型无效 主要管理堆区中的对象的内存管理   3.内存管理的原理 1)对象所有权概念 任何对象都可以能拥有一个或多个所有者,只要一个对象至少还拥有一个所有者,他就会继续存在 cocoasu所有权策略 任何自

iOS-旧项目中手动内存管理(MRC)转ARC

在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题,每周都安排有人值班用 Instruments挨个跑功能,关键是每次都总能检查出来不少.其实不管是菜鸟级还是资深级开发者都避免不了写出内存泄露的代码,规则大家都懂,可是 天知道什么时候手一抖就少写了个release? 好在项目决定转成ARC了,下面将自己转换的过程和中间遇到的问题写出来和大家共享,希望

object-C 手动内存管理(MRC)

object-C的内存管理和javascript的垃圾回收不一样,今天总结下手动内存管理,ARC的后边补上. 1:基本铺垫 oc采用引用计数来表示对象的状态,比如通过init创建出来的一个对象引用计数为1,如果想让它释放则对这个对象发送一条release消息,则引用计数-1,那怎么+1呢,给这个对象发送retain消息. 对象操作 Object-C方法 生成并持有对象 alloc/new/copy/mutableCopy方法 持有对象 retain方法 释放对象 release方法 废弃对象 d

IOS基础 Day-1手动内存管理

辞职回家打算自学IOS开发,就在借个地方记录一下 Day-1      手动内存管理                   主要内容:release  retain必须配对好,不然会占用内存 慢慢积累导致错误 一旦内存占用超过40m 45m时分别发生警告,一旦超过120m 系统将kill你的app 发生闪退 主要要防止发生的问题: 1.野指针操作 2.内存泄漏 理解retain和assign的区别和 retain的原理 Main 1 // 2 // main.m 3 // 1-1内存管理 4 //

OC 内存管理之手动内存管理MRC(MannulReference Counting)

一.    基本原理 1.        什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需要使用的对象.变量等 管理范围:任何继承了NSObject的对象,对其他基本数据类型(int.char.float.double.struct.enum等)无效 2.        对象的基本结构 每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人

七.OC基础加强--1.内存管理 2.野指针,内存泄露 3.set方法的内存管理 [email&#160;protected]参数 [email&#160;protected]和循环retain的使用 6.NSString的内存管理

1,内存管理简单介绍 1,为什么要有内存管理? malloc selloc dealloc```需要回头复习 一般的内存 4s 是512m内存:6 是1024m内存: 当内存过大时,会耗尽内存.出现程序闪退. 2.OC内存管理的范围 : 管理任何继承NSObject的对象,对其他的基本数据类型无效. 3.对象类型是程序运行过程中动态分配的,存储在堆区:内存管理主要是对 堆区中的对象的内存管理. 4.OC内存管理的原理 为了防止内存泄露 对象的引用计数器 : 每个OC对象都有自己的引用计数器,是一

OC基础15:内存管理和自动引用计数

1.什么是ARC? (1).ARC全名为Automatic Reference Counting,即是自动引用计数,会自动统计内存中对象的引用数,并在适当时候自动释放对象: (2).在工程中使用ARC非常简单:只需要像往常那样编写代码,只不过永远不用写retain. release和autorelease三个关键字: (3).在使用ARC之前,需要手动管理内存计数,这种机制称为MRC,即是手动引用计数 (Manual Referecen Counting): (4).ARC是Objective-

【OC基础】03-OC内存管理

概述 跟Java和C#类似,OC创建的对象在堆上.与Java和C#不同的是Java和C#有垃圾回收机制,所以不需要程序员手动释放堆上的内存.而OC没有垃圾回收机制,必须手动管理内存的创建和释放.下面介绍一下OC内存管理内存管理的方式. 引用计数器 OC管理内存的方式类似C++中的智能指针,创建一个对象时,在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后初始化它的引用计数器为1,当调用这个对象的alloc.retain.new.cop