内存管理

内存

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

 

下列行为都会增加一个app的内存占用

创建一个OC对象

定义一个变量

调用一个函数或者方法

 

当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等

 

如果app占用内存过大

系统可能会强制关闭app, 造成闪退现象, 影响用户体验

内存管理

如何回收那些不需要再使用的对象?

那就得学会OC的内存管理

 

所谓内存管理, 就是对内存进行管理, 涉及的操作有:

分配内存 : 比如创建一个对象, 会增加内存占用

清除内存 : 比如销毁一个对象, 能减小内存占用

 

内存管理的管理范围

任何继承了NSObject的对象

对其他非对象类型无效(int、char、float、double、struct、enum等 )

 

只有OC对象才需要进行内存管理的本质原因

OC对象存放于堆里面

非OC对象一般放在栈里面(栈内存会被系统自动回收)

堆和栈

循环引用

多个对象之间不要封闭的环
如出现这个那么环中所有对象都不会释放
解决方法:其中一端必须是assign

什么是引用计数器

系统是如何判断 什么时候需要回收一个对象所占用的内存?

根据对象的引用计数器

 

什么是引用计数器

每个OC对象都有自己的引用计数器

它是一个整数

从字面上, 可以理解为”对象被引用的次数”

也可以理解为: 它表示有多少人正在用这个对象

每个OC对象内部都有4个字节的存储空间来存放引用计数器

引用计数器的作用

简单来说, 可以理解为: 引用计数器表示有多少人正在使用这个对象

 

当没有任何人使用这个对象时, 系统才会回收这个对象, 也就是说

当对象的引用计数器为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)

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

 

为了避免野指针错误的常见办法

在对象被销毁之后, 将指向对象的指针变为空指针

关闭ARC功能

要想手动调用retain、release等方法 , 就必须关闭ARC功能

开启僵尸对象监控

默认情况下,Xcode是不会管僵尸对象的,使用一块被释放的内存也不会报错。为了方便调试,应该开启僵尸对象监控

 

多对象内存管理

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

 

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

 

其实, 多个对象的管理思路 跟 很多游戏的房间管理差不多

比如斗地主 \ QQ堂

 

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

只要还有人在用某个对象,那么这个对象就不会被回收

只要你想用这个对象,就让对象的计数器+1

当你不再使用这个对象时,就让对象的计数器-1

内存管理原则

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

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

 

谁retain谁release :只要你调用了retain,就必须调用一次release

 

总结一下就是

有加就有减 

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

set方法的内存管理

set方法

- (void)setCar:(Car *)car

{

    if (car != _car)

    {

        // 对当前正在使用的车(旧车)做一次release

        [_car release];

        

        // 对新车做一次retain操作

              _car = [car retain];

    }

}

dealloc方法的内存管理

dealloc方法

- (void)dealloc

{

    // 当人不在了,代表不用车了

        // 对车做一次release操作

    [_car release]; 

    [super dealloc];

}

 

错误写法

下面代码都会引发内存泄露

p.dog = [[Dog alloc] init];

 

[[Dog alloc] init].weight = 20.8;

@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方法的名称

@class

作用

可以简单地引用一个类

 

简单使用

@class Dog;

仅仅是告诉编译器: Dog是一个类; 并不会包含Dog这个类的所有内容

 

具体使用

在.h文件中使用@class引用一个类

在.m文件中使用#import包含这个类的.h文件

 

@class和#import

作用上的区别

#import会包含引用类的所有信息(内容), 包括引用类的变量和方法

@class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知

 

效率上的区别

如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍 , 编译效率非常低

相对来讲,使用@class方式就不会出现这种问题了

循环retian

循环retain的场景

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

 

循环retain的弊端

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

 

循环retain的解决方案

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

autorelease简介

autorelease方法的基本作用

给对象发送一条autorelease消息, 会将对象放到一个自动释放池中

当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

会返回对象本身

调用完autorelease方法后,对象的计数器不变

 

autorelease的好处

不用再关心对象释放的时间

不用再关心什么时候调用release

 

autorelease的使用注意

占用内存较大的对象不要随便使用autorelease

占用内存较小的对象使用autorelease,没有太大影响

自动释放池

在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

 

当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

 

自动释放池的创建方式

 iOS 5.0前

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [pool release]; // [pool drain]; 

 

iOS 5.0 开始

@autoreleasepool {  

 }

autorelease的常见错误

alloc之后调用了autorelease,又调用release

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

[p release];

 

连续调用多次autorelease

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

autorelease和release使用对比

使用release

Book *book = [[Book alloc] init];

[book release];

 

使用autorelease

Book *book = [[[Book alloc] init] autorelease];

// 不要再调用[book release];

 

autorelease的应用场合

一般可以为类添加一个快速创建对象的类方法 

+ (id)book {

    return [[[self alloc] init] autorelease];

}

外界调用[Book book]就可以获得和使用新建的Book对象,根本不用考虑在什么时候释放Book对象

 

一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease

 

比如下面的对象都已经是autorelease的,不需要再release

NSNumber *n = [NSNumber numberWithInt:100];

NSString *s = [NSString stringWithFormat:@"jack"];

NSString *s2 = @"rose";

 

 

转载于:https://my.oschina.net/itcastBlogs/blog/649738

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值