iOS中的Reference Counting详解

news/2024/7/20 14:22:35 标签: 移动开发, 内存管理, xcode

0x00 问题的引入

  • 前一阵子表哥给了我一道知乎的iOS开发岗位面试题,听说还是那种类似于“一票否决”的题目,考察应试者的编程能力。我仔细一看是关于MRC的一道题,也就是在考察Reference Counting。(代码为了方便运行测试,略有改动,但是核心思路无变化)
// 应用背景:MRC模式
// 请说出所有NSLog的输出值,并解释理由。

#import <Foundation/Foundation.h>

@interface Zhihu : NSObject

+ (int) setKanShanToZhihu;

@end

@implementation Zhihu

+ (int) setKanShanToZhihu {
    NSMutableArray *zhihu=[[[NSMutableArray alloc]init]retain];
    NSObject *kanshan=[[NSObject alloc]init];
    [kanshan retain];
    [zhihu addObject:kanshan];
    NSLog(@"%d",(int)[kanshan retainCount]);
    [kanshan retain];
    [kanshan release];
    [kanshan release];
    NSLog(@"%d",(int)[kanshan retainCount]);
    [zhihu removeAllObjects];
    NSLog(@"%d",(int)[kanshan retainCount]);
    [kanshan release];
    return (int)[kanshan retainCount]+(int)[zhihu retainCount];
}

@end

int main(int argc, const char * argv[]) {
    NSLog(@"%d",[Zhihu setKanShanToZhihu]);
    return 0;
}
  • 大家可以去编译一下这个题目,新建一个Xcode工程,在Compile Source中加入-fno-objc-arc,关闭ARC,运行结果是3 2 1 3

0x01 MRC和ARC

  • 最早的时候Objective-C和C++一样,也是手动管理内存的。不过OC使用的是Reference Counting(引用计数)的方式,也就是说,同一个内存空间,引用计数显示了目前有多少个指针正在指向这个内存空间。显然,当引用计数等于0的时候,这块内存就不再有用了,系统就会将其空间释放。这里面OC就提供了一些方法,允许程序员管理引用计数。
//对该对象的引用计数+1,返回一个新的指针指向该内存
- (instancetype) retain;
//对该对象的引用计数-1
- (oneway void) release;
//输出该对象的引用计数数值
- (NSUInteger) retainCount;
  • 大家也看出来了,这样管理也很麻烦,程序员需要关注大量的指针问题,还有可能出现强引用循环的问题。所以从iOS 5.0开始,苹果引入了ARC机制,ARC即自动引用计数(Automatic Reference Counting),这才把iOS开发者们从引用计数中解放出来,而原来的方式就称为MRC了,即手动引用计数(Manual Reference Counting)。
  • 既然iOS 5之后就可以用ARC了,现在App Store的最低支持版本已经是iOS 8.0了,为什么知乎的面试题还要考MRC呢?显然是为了考察iOS面试者对于引用计数机制的了解啦。

0x02 题目解答

  • 知乎这个面试题确实很考验iOS面试者的编程功底,将手动引用计数的管理应用到了极致。
  • 首先第1句创建了一个可变长度的数组,变量名字为zhihu
 NSMutableArray *zhihu=[[[NSMutableArray alloc]init]retain];
  • 这里我们发送alloc消息,就会分配一块内存,再发送init消息进行初始化,这样本身就会返回一个指针,引用计数变为1。但是偏偏又调用了一次retain,这时候引用计数又会加1,变为2。所以这一个语句使得引用计数+2。
  • 第2句和第3句正常创建了一个对象kanshan,引用计数为1,随后又进行了retain,引用计数为2。
NSObject *kanshan=[[NSObject alloc]init];
[kanshan retain];
  • 第4句通过向zhihu数组发送addObject消息,将kanshan对象加入数组。
[zhihu addObject:kanshan];
  • 这里因为向数组发送了addObject消息,数组中就也会保存一个指针指向这片内存,kanshan的引用计数再次加1。所以第一次输出的结果为3
  • 然后连续经过三句话,引用计数先加1后减2,结果当然是减1。
[kanshan retain];
[kanshan release];
[kanshan release];
  • 所以第二次输出的结果为2
  • 随后又向zhihu发送了removeAllObjects消息,清空了整个数组
[zhihu removeAllObjects];
  • 这时候kanshan的引用计数也会受到影响,因为它不再保存在数组中,所以引用计数减1,第三次输出的结果是1
  • 最后又调用了一次release
[kanshan release];
return (int)[kanshan retainCount]+(int)[zhihu retainCount];
  • 按理来说kanshan的引用计数应该降为0了,被释放。但是在发送retainCount消息的时候,为了避免对已经释放的内存发送消息,系统会自动持有一个指向该块内存的指针。而zhihu的引用计数还是为2,所以,最后返回值的总计数值是3

0x03 API文档中对retainCount消息的说明

  • 在API文档中,苹果直戳了当的表示:
Do not use this method.
  • 让我们不要使用这个方法,苹果的理由是这样表述的:
This method is of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
  • 这个函数对调试内存管理问题没有用处,因为所有的在framework中的对象都会保留了一个对象以便于持有对这个对象的引用。同时在autorelease pool中对象也可能被延迟释放。所以说我们不可能从这里获取有关内存管理的有用信息。
  • 所以,即使我们把kanshan的引用计数降为0,系统仍然会保留一个指针指向kanshan,以持有一个对kanshan的引用,这样我们在调用retainCount的时候才不会出现错误。

0x04 总结

  • 实际上苹果费尽心思让我们不要使用MRC,不要去考虑引用计数的问题。为了解决强引用循环的问题,苹果甚至设计了weak指针。在最新的Swift语言中,也必须进行一些unsafe的生命才允许你手动管理内存。但是作为一名合格的iOS开发人员,个人认为还是有必要了解一些关于引用计数的知识的,这也是知乎出这道题的意义。

http://www.niftyadmin.cn/n/1774622.html

相关文章

Taro、Weex、Hippy 齐聚IMWebConf 2018!

IMWebConf 2018 前端大会&#xff0c;10 月 14 日重磅来袭&#xff01; 想了解 2018 前端前沿技术和发展趋势&#xff1f;想挖掘前端更深远的价值&#xff1f;就在这个秋季&#xff0c;第七届 IMWebConf 大会重磅来袭&#xff0c;我们邀请您一起参加这场盛宴&#xff0c;与前端…

C++实现to_string函数--int to string

to_string()函数返回字符串形式&#xff0c; 例如&#xff1a; #include<iostream> #include<string> using namespace std;int main() {int i123;//aastring sto_string(134) "abc";string sto_string(i) "abc";cout<<s<<endl…

汽车传感器:车载雷达与摄像头

为了减少由于交通事故引起的人员伤亡&#xff0c;利用雷达和摄像头技术提高汽车的主动安全性能已经成为当今汽车制造业努力的方向。而目前汽车主要靠它的“眼睛”来加以实现&#xff0c;主流的汽车“眼睛”主要有四类&#xff1a;超声波雷达、毫米波雷达、激光雷达以及高清摄像…

入职第一天:前端leader手把手教我入门Vue服务器端渲染(SSR)

继前段时间西安电面之后顺利拿到了OFFER&#xff0c;今天&#xff08;5月2号&#xff09;是我入职第一天&#xff0c;在简短的内部培训了一上午后&#xff0c;前端leader让我先了解下什么是vue的服务器端渲染&#xff08;SSR&#xff09;。 SSR&#xff0c;英文全称叫 Server s…

[ USACO 2018 OPEN ] Out of Sorts (Silver)

\(\\\) \(Description\) 运行以下代码对一长为\(N\)的数列\(A\)排序&#xff0c;不保证数列元素互异&#xff1a; cnt 0 sorted false while (not sorted):cnt cnt 1sorted truefor i 0 to N-2:if A[i1] < A[i]:swap A[i], A[i1]sorted false 求退出循环后\(cnt\)的值…

字节序:MSB和LSB

MSB: 最高有效位 (大端模式) Most Significant Bit LSB: 最低有效位 (小端模式) Least Significant Bit 在很多协议对于数据的传递中存在这大小端模式&#xff0c;所以接收方或者解析方一定要清楚数据的组合模式&#xff0c;是大端还是小端&#xff0c;以免出现数据解析错误。…

2017-2018-2 20155203《网络对抗技术》Exp6 信息搜集与漏洞扫描

1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法。 2.实践内容 &#xff08;1&#xff09;各种搜索技巧的应用 通过搜索引擎进行信息搜集敏感信息 在百度搜索栏中输入filetype:关键字 site:edu.cn 大多数搜索到的文件都需要下载&#xff0c;打开后可以发现大多是一些…

C++11--auto类型说明符

编程时常常需要把表达式的值赋给变量&#xff0c;这就要求在声明变量的时候清楚地知道表达式的类型。然而要做到这一点并非那么容易&#xff0c;有时根本做不到。 c11新标准引入了auto类型说明符&#xff0c;用它就能让编译器替我们去分析表达式所属类型。 例如&#xff1a; i…