iOS夯实:内存管理

文章转自 内存管理

最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考。

有不正确的地方,多多指正。

目录:

基本信息

Objective-C 提供了两种内存管理方式。

  1. MRR (manual retain-release) 手动内存管理
    这是基于reference counting实现的,由NSObject与runtime environment共同工作实现。

  2. ARC (Automatic Reference Counting)自动引用技术
    ARC使用了基于MBR同样的reference counting机制,区别在于系统在编译的时候帮助我们插入了合适的memory management method。

Good Practices能有效的避免内存相关问题

基于内存,主要有两种错误

  1. 清空或覆盖了还在使用的内存
    这种清空通常会导致应用崩溃,甚至用户数据遭到改写
  2. 没有清空已经不需要的内存会导致内存泄露
    会导致系统性能下降,应用遭到系统终止

实际上,我们不应该只从reference counting的角度来管理内存,这样会让我们纠结于底层细节。我们应该站在object ownership and object graphs的角度来管理内存

可以用一幅这样的图来阐明:

旧时代的细节

一.基本内存管理准则

基本的内存管理准则:Cocoa为我们提供了这些准则

  • You own any object you create
    我们通过名字前缀为“alloc”, “new”, “copy”, or “mutableCopy”的方法创建对象

  • You can take ownership of an object using retain
    通过retain来获取对象的ownership,使用retain主要有两种场景

    1. 在accessor method 或 init method。来获取你想存储的对象的所有权
    2. 在某些场景里避免一个对象被移除,我们可以对它进行retain
  • When you no longer need it, you must relinquish ownership of an object you own
    当我们需要释放一个对象所有权时,我们通过对它发送release或autorelaese消息。

  • You must not relinquish ownership of an object you do not own
    不要释放你没有拥有的对象所有权

关于dealloc:

NSObject为我们提供了一个dealloc方法,当对象被销毁时,系统会自动调用它。这个方法的作用主要是清空对象自身内存与它所持有的资源。例如:

 @interface Person : NSObject
@property (retain) NSString *firstName;
@property (retain) NSString *lastName;
@property (assign, readonly) NSString *fullName;
@end
@implementation Person
- (void)dealloc
[_firstName release];
[_lastName release];
[super dealloc];
}
@end

需要强调的是:永远不要自己调用dealloc方法
在dealloc的最后需要调用super class的dealloc

二.实践中的内存管理准则

1. 使用存取方法来简化内存管理。

如果代码中都是一堆retain release,必然不是一个好的情况。在存取方法里面进行retain和release的操作能够简化内存管理。例如:
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}

2. 使用存取方法来设置Property value

对比如下代码,第一种使用了存取方法来设置,第二种直接对实例变量操作。显然我们应该采用第一种,使用第二种情况,简单的情况还好,如果情况一旦复杂,就非常容易出错。并且直接对实例变量操作,不会引发KVO通知。

- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[self setCount:zero];
[zero release];
} - (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[_count release];
_count = zero;
}

3. 不要在初始化方法和dealloc方法中使用Accessor Methods

苹果在《Advanced Memory Management Programming Guide》指出:

Don’t Use Accessor Methods in Initializer Methods and dealloc The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc. To initialize a counter object with a number object representing zero, you might implement an init method as follows:

- init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}

唯一不需要使用Accessor Methods的地方是initializer和dealloc. 在苹果官方文档中没有解释为什么。经过一番查阅后,最主要的原因是此时对象的状况不确定,尚未完全初始化完毕,而导致一些问题的发生。

例如这个类或者子类重写了setMethod,里面调用了其他一些数据或方法,而这些数据和方法需要一个已经完全初始化好的对象。而在init中,对象的状态是不确定的。

举个例子,一个子类重写了set方法,在里面进行了一些子类特有的操作,而此时如果父类在init直接使用Accessor Methods,就会导致问题的发送。

其它一些问题还有,像会触发KVO notification等。[^1] [^2]

总之,记住在开发中记住这个principle最重要。

在开发中,我发现还是有人因为疏忽或不知道而直接在init方法里面使用self.property = XXX来进行赋值,留下了一些隐患。Swift去除了直接和instance variable打交道的途径。统一使用property进行管理,并对init进行严格的规定,提升了安全性,解决了人为因素导致的错误。欢迎对Swift有兴趣的同学继续阅读。从Objective-C到Swift

[^1]: stackoverflow [^2]: objc-zen-book

4. 使用弱引用来避免引用环

我们都知道在ARC,弱引用通过声明property的attribute为weak来实现。

而在MRR中则是通过引用对象,但是不retain它来实现(A weak reference is a non-owning relationship where the source object does not retain the object to which it has a reference)

在Cocoa中,典型需要使用弱引用的有delegate,data source, notification observer

我们需要注意处理好弱引用对象,如果对已经被销毁的对象发送信息,则会导致crash.通常被弱引用的对象当它将被销毁时,负责通知其它object.

像notification center保存了observer的弱引用,因此当被弱引用observer准备结束生命周期时,observer需要通知notification center,unregister自己。

4. 不要让你正在使用的对象被移除

观察以下两种代码,第一种因为没有retain,对象可能会被移除,而第二种是正确的写法

heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid. heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];

5. Collections类拥有其收集的的对象的所有权

例如NSArray,dictionary等。他们负责其收集的对象的所有权,因此我们不需要retain存进去的对象。 例如下面的allocedNumber就不需要retain了。

for (i = 0; i < 10; i++) {
NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger:i];
[array addObject:allocedNumber];
[allocedNumber release];
}

6. 最后,以上这些ownership policy是基于retain count实现的

  • 当你创建一个对象,它的retain count为1。
  • 当你对一个对象发送retain信息,它的retain count +1
  • 当你对一个对象发送release信息,他的retain count -1
    当你对一个对象发送autorelease信息,在当前autorelease pool block结束时,retain count -1
  • 当一个对象的retain count 降至0,它就被dealloced了。

三.使用Autorelease Pool block

autorelease pool 为我们提供了一个机制,避免当我们解除一个对象所有权时,对象被立刻销毁(例如从一个方法里返回一个对象)

一个autorelease pool block对象是这样子的:

@autoreleasepool {
// Code that creates autoreleased objects.
}

在block的最后,所有收到过autorelease消息的对象都会接收到release消息。

在ARC的新时代里面,autorelease pool block主要用于处理避免内存峰值,只是我们不需要再手动添加autorelease的代码了

例如以下这个例子(有点生编硬造,主要为了阐明一下)
如果我们没有添加autoreleasepool,我们最后可需要释放10000*10000个对象,而不是每次循环都分别释放掉10000个对象

- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
@autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(@"number = %p", number);
}
}
}
}

新时代

详情亲看:iOS夯实:ARC时代的内存管理

 

iOS夯实:内存管理的更多相关文章

  1. 理解 iOS 的内存管理

    远古时代的故事 那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新.那个时候大约是 2010 年,国内 iOS 开发刚刚兴起,tinyfool 大叔的大名已经如雷贯耳 ...

  2. iOS ARC内存管理

    iOS的内存管理机制,只要是iOS开发者,不管多长的时间经验,都能说出来一点,但是要深入的理解.还是不简单的.随着ARC(自动管理内存)的流行.iOS开发者告别了手动管理内存的复杂工作.但是自动管理内 ...

  3. iOS之内存管理(ARC)

    iOS的内存管理,相信大家都不陌生,之前是使用的MRC,由开发人员手动来管理内存,后来使用了ARC,来由系统管理内存.本文主要讲讲Autorelease,Core Foundation对象在内存管理方 ...

  4. 说说iOS与内存管理(上)

    http://www.cocoachina.com/ios/20150625/12234.html 说起内存管理,看似老生常谈,而真正掌握内存管理的核心其实并不简单.ARC/MRR以及“谁分配谁就负责 ...

  5. iOS的内存管理和引用计数规则、Block的用法以及三种形式(stack、malloc、global)

    学习内容 iOS的内存管理和引用计数规则 内存管理的思考方式 自己生成的对象自己持有 非自己生成的对象自己也能持有 自己持有的对象不需要时释放 非自己持有的对象不能释放 ARC有效时,id类型和对象类 ...

  6. iOS - OC 内存管理

    1.OC 基本内存管理模型 1.1 自动垃圾收集 在 OC 2.0 中,有一种称为垃圾收集的内存管理形式.通过垃圾收集,系统能够自动监测对象是否拥有其他的对象,当程序执行需要空间的时候,不再被引用的对 ...

  7. 总结 IOS 7 内存管理

    [iOS7的一些总结].iOS中的内存管理 我们知道,为了更加方便地处理内存管理问题,将开发人员从繁琐的内存的分配和释放工作中解放出来而专注于产品和逻辑,iOS提供了一种有效的方法, 即自动引用计数A ...

  8. iOS的内存管理

    在Objective-C 这种面向对象的语言里,内存管理是个重要的概念.要想用一门语言写出内存使用效率高而且又没有bug的代码,就得掌握其内存管理模型的种种细节. 一旦理解了这些规则,你就会发现,其实 ...

  9. IOS ARC内存管理,提高效率避免内存泄露

    本文转载至 http://blog.csdn.net/allison162004/article/details/38756263 Cocoa内存管理机制 (1)当你使用new.alloc.copy方 ...

  10. 【iOS系列】-iOS中内存管理

    iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对 ...

随机推荐

  1. Ubuntu的力量何在?

    = 怎样正确评价Ubuntu,这不是一个简单问题.Ubuntu的 力量何在?它的意义何在?这都是须要认真研究的. 实际上,Uuntu 14.04 LTS公布之后,并没有引起预期的热烈反响.这是什么原因 ...

  2. Ubuntu Kylin14.04终于可以使用root登陆了

    Ubuntu Kylin14.04怎样使用root登陆? 方法:找到/usr/share/lightm/ightm.conf.d         用gedit或者vi 打开50-unity-greet ...

  3. 让WPS支持VHDL的关键词加粗

    WPS的VBA在这里下载:http://bbs.wps.cn/forum.php?mod=viewthread&tid=22347925 语法高亮是参考Word的,这篇文章:http://bl ...

  4. Uestc_suibian 暑假集训总结

    唉,终于组队了,终于可以只BB了,我就BB,我就不上! 和Xiper以及chenxh组队了- 下面是总结: day1 第一天吃饱喝足,然后就上路了,我一开始就看到了C题大水题,但是我不敢想象这道题居然 ...

  5. Codeforces Gym 100002 C "Cricket Field" 暴力

    "Cricket Field" Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/1000 ...

  6. Codeforces Gym 100650D Queens, Knights and Pawns 暴力

    Problem D: Queens, Knights and PawnsTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu ...

  7. osg 笔记一 (转)

    场景图形采用一种自顶向下的,分层的树状数据结构来组织空间数据集,以提高渲染的效率 场景图形树结构的顶部是一个根节点,从根节点向下延伸,各个组节点中均包含了几何信息和用于控制其外观的渲染状态信息.根节点 ...

  8. Android传感器编程带实例

    看了程序人生 网站的 编程高手的编程感悟 深有感触,好像也是一个android 程序员写的,推荐大家也看看.话不多说,还是言归正传吧. 一.前言 我很喜欢电脑,可是笔记本还是太大,笔记本电脑再小还是要 ...

  9. Masonry+Infinite-Scroll实现无刷新无分页完美瀑布流(转)

    一.Masonry 是基于Jquery插件,用于对CSS布局的可移动层进行重新布局.Masonry愿意石工,可以这样形象的理解,页面上很多大小不一的移动层可以想象成散乱的石头,经过Masonry这个石 ...

  10. Yii框架学习 新手教程(一)

    本人小菜鸟一仅仅,为了自我学习和交流PHP(jquery,linux,lamp,shell,javascript,server)等一系列的知识,小菜鸟创建了一个群.希望光临本博客的人能够进来交流.寻求 ...