资讯专栏INFORMATION COLUMN

Objective-C的内存管理(2)——从MRC到ARC

luck / 2485人阅读

摘要:在里,每个继承自的对象都会记录自身的引用计数,一番加加减减之后,变成就会释放掉。引用计数减少的操作就是了。要知道,返回的对象引用计数是有的,而并不是。

罗里吧嗦颠三倒四,单纯的个人笔记。

MRC

引用计数上一篇已经有大概讲过。在Objective-C里,每个继承自NSObject的对象都会记录自身的引用计数,一番加加减减之后,变成0就会释放掉。
MRC是Mannul Reference Counting的缩写,意思也很简单,这番加加减减都靠手动管理的意思。

使用时的基本原则是:管好自己。每个对象,引用别的对象时加了几次计数最终到了不用的时候就要减几次。不能多也不能少。
这样就聚焦了很多。

导致引用计数增加的操作,显式的retain不多说,剩下的就是四个关键字:alloc、new(以及new开头的方法)、copy、mutableCopy,使用这四个关键字得到的对象,就算你自己加的引用计数,回头要自己减掉。

引用计数减少的操作就是release了。

AutoRelease

AutoReleasePool是个自动释放池,加入其中的对象会延迟到Pool“结束”时释放。
在MRC中,你可以显式创建一个NSAutoReleasePool,显式地将一个对象加入进去,并显式释放AutoReleasePool:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
NSObject *obj = [[[NSObject alloc] init] autorelease];
[pool release];//[pool drain];

在ARC中,需要通过特殊的语法:

@autoreleasepool {
    // Code benefitting from a local autorelease pool.
}

默认地,每个Runloop迭代开始时会创建一个AutoReleasePool,Runloop迭代结束时释放。也就是说,当我们没有显式创建AutoReleasePool时,autorelease的对象会在Runloop迭代结束时释放。
当我们显式地创建AutoReleasePool时,其释放时机是我们决定的(显式调用release/drain或block结束)。

主动使用AutoReleasePool的目的通常是为了控制内存峰值。比如,我有一个大循环,每次循环都会创建比较大的autorelease的临时对象。如果不显式释放,这些临时对象会在整个循环结束后才一起释放,期间可能造成内存占用过高。这种情况下就可以在每次循环内声明autoreleasepool,保证临时对象不会堆积。

ARC

ARC为我们自动地做了很多,屏蔽了很多细节,理论上来说,我们只需要关注对象间的所有权关系即可。
上层机制虽然简单,涉及到的细节还是有很多的,可喜可贺的是,ARC是有标准文档的。简直...

ARC提供的变量修饰符有以下几个:

__strong

__weak

__unsafe_unretaied

__autoreleasing

提供的属性修饰符有:

assign 对应的所有权类型是 __unsafe_unretained。

copy 对应的所有权类型是 __strong。

retain 对应的所有权类型是 __strong。

strong 对应的所有权类型是 __strong。

unsafe_unretained对应的所有权类型是__unsafe_unretained。

weak 对应的所有权类型是 __weak。

(基本类型默认是assign,对象类型默认是strong)

__strong

强引用,不多说了。注意声明变量和属性时若未加说明,默认是强引用。

__weak

弱引用。当对象被释放时,weak修饰的变量会被置为nil。
仔细想想,想要实现这个特性,所有的weak变量都需要放到一个全局的map里,实现成本还是比较高的。

__unsafe_unretained

不做任何额外操作。

__autoreleasing

__autoreleasing标记的变量等价于调用autorelease方法

想到一个小问题:对于函数返回值,ARC是怎么知道要不要加引用计数呢?
看这几行代码:

- (void)testMethod
{
    NSObject *obj = [NSObject new];
    NSArray *array = [NSArray array];
    // do something
}

在ARC中,obj和array用完之后都会被自动释放,但是细想之下其实有不少细节。
要知道,[NSObject new]返回的对象引用计数是有+1的,而[NSArray array]并不是。
这俩玩意儿引用计数差了1,ARC是怎么知道谁要多释放一次的?
在MRC中,我们知道new出来的obj需要手动释放,而array就不需要,是通过方法的关键词进行判断。
但是方法中的关键词不应该是某种约定吗?ARC难道也会去看一个方法是否是以new开头?
看了文档之后发现...ARC还真是这么做的...

Methods in the alloc, copy, init, mutableCopy, and new families are implicitly marked __attribute__((ns_returns_retained)).

回想起来,在MRC时代,这些关键词应该是止步于约定的。而ARC或许是为了平滑过渡,把曾经的约定变成了语法规范,emmm,感觉这么搞不是很好啊。

Block内存管理

在ARC之后,内存管理的问题减少了很多,但仍然有一些遗留。其中最重要的一部分就是Block相关的内存管理。
参考Objective-C中Block的循环引用问题

Bridge

Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。
Objective-C对象和CF对象是可以直接转换的:

CFStringRef aCFString = (CFStringRef)aNSString;
NSString *aNSString = (NSString *)aCFString;

然而ARC是不支持CF对象的内存管理的,这就需要我们关注谁来释放转换后的对象的问题。

在MRC中,相对来说比较简单,CFRelease和release方法是等效的,择机使用即可。
这里主要关注ARC下的情况。
根据不同需求,有3种转换方式

__bridge                  (不改变对象所有权)

__bridge_retained 或者 CFBridgingRetain()               (解除 ARC 所有权)

__bridge_transfer 或者 CFBridgingRelease()             (给予 ARC 所有权)

1. __bridge

__bridge不改变对象所有权。

OC对象转CF,仍由ARC管理,不需要显式释放

NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];
CFStringRef aCFString = (__bridge CFStringRef)aNSString;
// do something

CF对象转OC,仍由CF管理,需要显式释放

CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);
NSString *aNSString = (__bridge NSString *)aCFString;
// do something
CFRelease(aCFString);
2. __bridge_retained

所有权给CF,因此要调用CFRelease显式释放

NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];
CFStringRef aCFString = (__bridge_retained CFStringRef) aNSString;
// do something
CFRelease(aCFString); 
3. __bridge_transfer

所有权给ARC,因此无需手动管理

CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);
NSString *aNSString = (__bridge_transfer NSString *)aCFString;
// do something

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/17321.html

相关文章

  • Objective-C 内存管理——你需要知道一切

    摘要:现在苹果推荐使用来进行内存管理。是苹果引入的一种自动内存管理机制,会根据引用计数自动监视对象的生存周期,实现方式是在编译时期自动在已有代码中插入合适的内存管理代码以及在做一些优化。根据苹果官方文档,函数是整个的入口,用来创建对象单例和。 前言 有关 iOS 内存管理的文章相信大家都看过不少了,我自己也一样。不过网上大部分文章都没有解决对于内存管理的一些关键性的疑惑,对于初学者来说并不是...

    Tangpj 评论0 收藏0
  • iOS内存管理

    摘要:内存块头部管理引用计数的好处少量代码皆可完成能够统一管理引用计数内存块与对象内存块。中的或者等,还是需要自己手动进行内存管理。必须遵守内存管理的方法命名规则。但是附有修饰的变量不属于编译器的内存管理对象,可能造成内存泄露或者崩溃。 1.引用计数式内存管理的思考方式 自己生成的对象,自己持有 非自己生成的对象,自己也能持有 不在需要自己持有的对象时释放 非自己持有的对象无法释放 2....

    adam1q84 评论0 收藏0
  • iOS中Reference Counting详解

    摘要:所以从开始,苹果引入了机制,即自动引用计数,这才把开发者们从引用计数中解放出来,而原来的方式就称为了,即手动引用计数。但是作为一名合格的开发人员,个人认为还是有必要了解一些关于引用计数的知识的,这也是知乎出这道题的意义。 0x00 问题的引入 前一阵子表哥给了我一道知乎的iOS开发岗位面试题,听说还是那种类似于一票否决的题目,考察应试者的编程能力。我仔细一看是关于MRC的一道题,也就是...

    Hujiawei 评论0 收藏0
  • Objective-C内存管理(1)——内存管理概述

    摘要:基于某些机制实现半自动管理这里的某些机制其实通常就是引用计数。早年,选择的是退一步,完全让程序员来管理引用计数的加减,称为,显然管理成本偏高。 概述 应用程序开发中,内存管理是个重要的话题。简单而言,语言层面的内存管理基本有三类: 1. 纯粹的手动管理 如C和曾经的C++。 char *some_string = malloc(BUFFER_SIZE); // do something...

    zzbo 评论0 收藏0
  • 所谓,引用计数

    摘要:每个对象有一个与之相关联的整数,称作它的引用计数器。当该代码结束访问时,将该对象的引用计数器值减。图中红色表示引用计数的活动。当然,引用计数也有一些缺点无法完全解决循环引用导致的内存泄露问题。即使只读操作,也会引起内存写操作引用计数的修改。 博文链接: http://ifujun.com/suo-wei-yin-yong-ji-shu/ 简介 在大部分关于Objective-C的书中,...

    zhaofeihao 评论0 收藏0

发表评论

0条评论

luck

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<