《Objective-C 高级编程》干货三部曲(一):引用计数篇

本文深入探讨Objective-C的内存管理,包括手动内存管理的四大思想:对象生成、持有、释放及废弃,以及alloc、retain、release、dealloc的实现。此外,介绍了autorelease的工作原理,并对比了GNUstep与苹果的实现。文章还概述了ARC(Automatic Reference Counting)内存管理,强调了四种所有权修饰符:__strong、__weak、__autoreleasing和__unsafe_unretained的使用及其内部实现。最后,讨论了ARC下的内存管理规则,如禁用某些方法、使用@autorelease块等。
摘要由CSDN通过智能技术生成

总结了Effective Objective-C之后,还想读一本进阶的iOS书,毫不犹豫选中了《Objective-C 高级编程》:

《Objective-C高级编程:iOS与OS X多线程和内存管理》

这本书有三个章节,我针对每一章节进行总结并加上适当的扩展分享给大家。可以从下面这张图来看一下这三篇的整体结构:

《Objective-C高级编程》 干货三部曲

注意,这个结构并不和书中的结构一致,而是以书中的结构为参考,稍作了调整。

本篇是第一篇:引用计数,简单说两句:
Objective-C通过 retainCount 的机制来决定对象是否需要释放。 每次runloop迭代结束后,都会检查对象的 retainCount,如果retainCount等于0,就说明该对象没有地方需要继续使用它,可以被释放掉了。无论是手动管理内存,还是ARC机制,都是通过对retainCount来进行内存管理的。

先看一下手动内存管理:

手动内存管理

我个人觉得,学习一项新的技术之前,需要先了解一下它的核心思想。理解了核心思想之后,对技术点的把握就会更快一些:

内存管理的思想

  • 思想一:自己生成的对象,自己持有。
  • 思想二:非自己生成的对象,自己也能持有。
  • 思想三:不再需要自己持有的对象时释放对象。
  • 思想四:非自己持有的对象无法释放。

从上面的思想来看,我们对对象的操作可以分为三种:生成,持有,释放,再加上废弃,一共有四种。它们所对应的Objective-C的方法和引用计数的变化是:

对象操作 Objecctive-C方法 引用计数的变化
生成并持有对象 alloc/new/copy/mutableCopy等方法 +1
持有对象 retain方法 +1
释放对象 release方法 -1
废弃对象 dealloc方法

用书中的图来直观感受一下这四种操作:

图片来自:《Objective-C高级编程:iOS与OS X多线程和内存管理》

下面开始逐一解释上面的四条思想:

思想一:自己生成的对象,自己持有

在生成对象时,使用以下面名称开头的方法生成对象以后,就会持有该对象:

  • alloc
  • new
  • copy
  • mutableCopy

举个��:

id obj = [[NSObject alloc] init];//持有新生成的对象

这行代码过后,指向生成并持有[[NSObject alloc] init]的指针被赋给了obj,也就是说obj这个指针强引用[[NSObject alloc] init]这个对象。

同样适用于new方法:

id obj = [NSObject new];//持有新生成的对象

注意:
这种将持有对象的指针赋给指针变量的情况不只局限于上面这四种方法名称,还包括以他们开头的所有方法名称:
- allocThisObject
- newThatObject
- copyThisObject
- mutableCopyThatObject

举个��:

id obj1 = [obj0 allocObject];//符合上述命名规则,生成并持有对象

它的内部实现:

- (id)allocObject
{
    id obj = [[NSObject alloc] init];//持有新生成的对象
    return obj;
}

反过来,如果不符合上述的命名规则,那么就不会持有生成的对象,
看一个不符合上述命名规则的返回对象的createObject方法的内部实现��:

- (id)createObject
{
    id obj = [[NSObject alloc] init];//持有新生成的对象
    [obj autorelease];//取得对象,但自己不持有
    return obj;
}

经由这个方法返回以后,无法持有这个返回的对象。因为这里使用了autorelease。autorelease提供了这样一个功能:在对象超出其指定的生存范围时能够自动并正确地释放(详细会在后面介绍)。

图片来自:《Objective-C高级编程:iOS与OS X多线程和内存管理》

也就是说,生成一个调用方不持有的对象是可以通过autorelease来实现的(例如NSMutableArray的array类方法)。

我的个人理解是:通过autorelease方法,使对象的持有权转移给了自动释放池。所以实现了:调用方拿到了对象,但这个对象还不被调用方所持有。

由这个不符合命名规则的例子来引出思想二:

思想二:非自己生成的对象,自己也能持有

我们现在知道,仅仅通过上面那个不符合命名规则的返回对象实例的方法是无法持有对象的。但是我们可以通过某个操作来持有这个返回的对象:这个方法就是通过retain方法来让指针变量持有这个新生成的对象:

id obj = [NSMutableArray array];//非自己生成并持有的对象
[obj retain];//持有新生成的对象

注意,这里[NSMutableArray array]返回的非自己持有的对象正是通过上文介绍过的autorelease方法实现的。所以如果想持有这个对象,需要执行retain方法才可以。

思想三:不再需要自己持有的对象时释放对象

对象的持有者有义务在不再需要这个对象的时候主动将这个对象释放。注意,是有义务,而不是有权利,注意两个词的不同。

来看一下释放对象的例子:

id obj = [[NSObject alloc] init];//持有新生成的对象
[obj doSomething];//使用该对象做一些事情
[obj release];//事情做完了,释放该对象

同样适用于非自己生成并持有的对象(参考思想二):

id obj = [NSMutableArray array];//非自己生成并持有的对象
[obj retain];//持有新生成的对象
[obj soSomething];//使用该对象做一些事情
[obj release];//事情做完了,释放该对象

可能遇到的面试题:调用对象的release方法会销毁对象吗?
答案是不会:调用对象的release方法只是将对象的引用计数器-1,当对象的引用计数器为0的时候会调用了对象的dealloc 方法才能进行释放对象的内存。

思想四:无法释放非自己持有的对象

在释放对象的时候,我们只能释放已经持有的对象,非自己持有的对象是不能被自己释放的。这很符合常识:就好比你自己才能从你自己的银行卡里取钱,取别人的卡里的钱是不对的(除非他的钱归你管。。。只是随便举个例子)。

两种不允许的情况:
1. 释放一个已经废弃了的对象
id obj = [[NSObject alloc] init];//持有新生成的对象
[obj doSomething];//使用该对象
[obj release];//释放该对象,不再持有了
[obj release];//释放已经废弃了的对象,崩溃
2. 释放自己不持有的对象
id obj = [NSMutableArray array];//非自己生成并持有的对象
[obj release];//释放了非自己持有的对象

思考:哪些情况会使对象失去拥有者呢?
1. 将指向某对象的指针变量指向另一个对象。
2. 将指向某对象的指针变量设置为nil。
3. 当程序释放对象的某个拥有者时。
4. 从collection类中删除对象时。

现在知道了引用计数式内存管理的四个思想,我们再来看一下四个操作引用计数的方法:

alloc/retain/release/dealloc的实现

某种意义上,GNUstep 和 Foundation 框架的实现是相似的。所以这本书的作者通过GNUstep的源码来推测了苹果Cocoa框架的实现。

下面开始针对每一个方法,同时用GNUstep和苹果的实现方式(追踪程序的执行和作者的猜测)来对比一下各自的实现。

GNUstep实现:

alloc方法
//GNUstep/modules/core/base/Source/NSObject.m alloc:

+ (id) alloc
{
    return [s
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值