黑马程序员—OC语言基础—内存管理

本文深入探讨Objective-C中的内存管理机制,包括引用计数器的工作原理、野指针的概念、 autorelease的使用方法以及ARC机制的介绍。文章还提供了实用的代码规范建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

————————Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———————

之前我们已经提到过C语言中的内存存储空间。有五大区域:堆、栈、自由存储区、全局\静态存储区和常量存储区。

OC是以C语言为基础的,所以也分这五大区域。

下面我们讨论一下OC中内存管理的规律。

一、引用计数器

1.每个OC对象都有自己的引用计数器,是一个整数,即对象被引用的次数。

该计数器一旦诞生,次数就为1,当计数器为0时,该存储空间就消失了。

所以我们要操纵该计数器来控制内存。

2.引用计数器的作用

1)当使用alloc、new或copy创建一个新对象时,新对象的引用计数器默认就是1;

2)当一个对象的引用计数器为0时,对象占用的内存就会被系统回收,即若对象的计数器不为0,那么在整个程序运行过程中,它占用的内存就不可能被回收,除非整个程序已经推出。

3.引用计数器的操作

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

[p retain];

2)给对象发送一条release消息可使引用计数器-1

[p release];

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

[p retainCount];

4.对象的销毁

1)当一个对象的引用计数器值为0时,那么它将销毁,其占用的内存被系统回收

2)当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

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

4)不要直接调用dealloc方法

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


总结:为了更好地控制内存管理,有1个alloc就有1个release,有1个retain,就有1个release。


下面我们了解一下什么是野指针。

二、野指针和空指针

当对象的引用计数器值为0,该对象销毁,称为僵尸对象。这时,p为野指针:指向僵尸对象(不可用的内存)的指针。

在引用计数器为0时,还调用[p release];,则运行报错。


总结:

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

2.当不再用这个对象,计数器-1

3.谁创建,谁release

1)如果通过alloc、new或[mutable]copy来创建一个对象,那你必须调用release或autorelease;----->所以不是你创建的,就不用你去[auto]release

4.谁retain,谁release

只要你调用了retain,无论这个对象如何生成的,你都要调用release

总之,就是要:有始有终,有加就有减。


三、内存管理代码规范

1.只要调用了alloc,必须有release(autorelease)

2.set方法的代码规范

(1)基本数据类型:直接赋值

- (void)setAge:(int)age
{
    _age = age;
}

(2)OC对象类型

- (void)setCar:(Car *)car
{
    //1.判断是不是新传进来对象
    if(car != _car)
    {
        //2.对旧对象做一次release
        [_car release];
        //3.对新对象做一次retain
        _car = [car retain];
    }
}

3.dealloc方法的代码规范

1)一定要[super dealloc],且放到最后面

2)对当前对象所拥有的其他对象做一次release

- (void)dealloc
{
    [_car release];
    [super dealloc];
}

四、@property内存管理

1.@property的内存管理

@property (retain)Book *book;

就相当于

- (void)setBook:(Book *)book
{
    if(_book !=book)
    {
        [_book release];
        _book = [book retain];
     }
}
retain的作用是生成的set方法里面,release旧值,retain新值

2.@property的参数

@property(retain)int age;这样写是不对的,因为基本数据类型不需要内存控制,因为会跟着程序块自动清除。而OC类型不会。

1)set方法内存管理相关的参数

retain : release旧值,retain新值(适用于OC对象类型)

assign : 直接赋值(默认,适用于非OC对象类型)

copy   : release旧值,copy新值

2)是否要生成set方法

readwrite : 同时生成settergetter的声明、实现(默认)

readonly  : 只会生成getter的声明、实现

3.多线程管理

nonatomic : 性能高 (一般就用这个)

atomic    : 性能低(默认)

4.settergetter方法的名称

setter : 决定了set方法的名称,一定要有个冒号 :

getter : 决定了get方法的名称(一般用在BOOL类型)


五、@class和循环retain

1.@class的用法

@class Card;

该语句仅仅告诉编译器,Card是一个类,但不知道Card类中又什么变量。

一般情况下,如果两个类,你有我,我有你,一般两边都用@class xxx;,所以涉及到循环引用就用@class。

但是@class比import少一个功能,即没有将所有的功能拷贝过来。一般在日常开发中,如果要到.h文件,则将#import "Person.h"放到.m文件中声明。


思考题:

为什么开发中用@class而不用#import(除了NSObject用#import)?(@class与#import区别)

如果有上百个文件#import同一个文件或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样效率很低,而用@class就不会出现这种问题。


2.循环retain

比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放。

遇到这种问题的解决办法就是:当两端互相利用时,应该一端用retain,一端用assign


六、autorelease

autorelease是半自动释放内存,实际上只是延迟了释放的时间。

基本格式:

@autoreleasepool
{   //开始代表创建了释放池
    ......
}   //结束代表销毁释放池

缺点:不能精确控制释放时间,所以不能乱用,所以尽量用release来精确控制。

因此,autorelease一般适用于占用内存比较小的对象。


总结:

1.autorelease的基本用法

1)会将对象放到一个自动释放池中

2)当自动释放池被销毁时,会对池子里面的所有对象做一次release操作(不是销毁对象,只是做-1操作)

3)会返回对象本身

4)调用完autorelease后,对象的计数器不变

2.autorelease好处

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

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

3.autorelease的使用注意

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

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

4.错误写法

1)alloc之后调用autorelease,又调用release

2)连续调用多次autorelease

5.自动释放池

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

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

6.autorelease使用细节

一个方法可以创建出不同类型的对象(谁调用我,我就创建谁);创建对象时,尽量考虑是否可以使用self,尽量用self。

注:没有autoreleasepool,autorelease是没有作用的。


七、ARC机制(Automatic Reference Counting 自动引用计数)

ARC机制是一个编译器特性,这与Java中的垃圾回收是不一样的,Java中的垃圾回收是运行时特性。

1.ARC原理

int main()
{
    Person *p = [[Person alloc]init];
    p = [[Person alloc]init];  //这一行就释放Person对象的存储空间
    NSLog(@"————");
    return 0;
}
释放内存的位置是当强指针p指针不再指向对象的时候,这时对象就会销毁。


总结:

ARC的判断准则:只要没有强指针指向对象,就会释放对象。

指针分两种:(1)强指针:默认情况下,所有的指针都是强指针(__strong);(2)弱指针:__weak

ARC中,只需将之前的retain换成strong,例如:

@property (nonatomic, strong)Dog *dog;

若写为retain,但dealloc中不能用release了。

因此,在ARC中不允许调用release、retain、retainCount,允许重写dealloc,但不允许调用[super dealloc];

@property的参数

*strong:成员变量是强指针(适用于OC对象类型)

*weak:成员变量是弱对象(适用于OC对象类型)

*assign:适用于非OC对象类型


注意:ARC的循环应用问题

这个问题和循环retain问题是一样的。解决办法也是类似的。

解决方案:

当两端循环引用的时候,(1)ARC,一端用strong,一端用weak;(2)非ARC,一端用retain,一端用assign。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值