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. HDU 2136 Largest prime factor 參考代码

    #include <iostream> #include <vector> #include <cmath> using namespace std; const ...

  2. Struts2 学习笔记 11 Result part2

    之前学习了result type 和global result 我们现在来说一下 1.Dynamic Result动态结果集.先来看一下小项目的目录 首页的两个链接访问user/user?type=1 ...

  3. TListView列表拒绝添加重复信息

      //TListView列表拒绝添加重复信息 procedure TForm1.Button1Click(Sender: TObject);var  i: Integer;begin  if (Tr ...

  4. [AngularJS] ui-router: Abstract States

    ui-router has the powerful ability to define abstract states, or states that can't be navigated to, ...

  5. PHP获取解析URL方法

    们要经常对url里面所带的参数进行解析,如果我们知道了url传递参数名称,例如: /index.php?name=tank&sex=1#top 我们就可以通过$_GET['name'],$_G ...

  6. IntellijIdea中常用的快捷键

    快速查找类:Ctrl+N 提示:Ctrl+Space 提示:Ctrl+Shift+Space 查看documentation:Ctrl+Q 查找类.方法.变量的引用:Alt+F7 定位类.方法.变量的 ...

  7. 线程控制之线程和fork

    当线程调用fork时,就为子进程创建了整个进程地址空间的副本.回忆http://www.cnblogs.com/nufangrensheng/p/3509492.html中讨论的写时复制,子进程与父进 ...

  8. PAT 1004

    1004. Counting Leaves (30) A family hierarchy is usually presented by a pedigree tree. Your job is t ...

  9. 小白日记40:kali渗透测试之Web渗透-SQL手工注入(二)-读取文件、写入文件、反弹shell

    SQL手工注入 1.读取文件[load_file函数] ' union  SELECT null,load_file('/etc/passwd')--+ burpsuite 2.写入文件 ' unio ...

  10. PHP.3-DIV+CSS标准网页布局准备工作(上)

    DIV+CSS标准网页布局准备工作(上) 概述 使用"DIV+CSS"对网站进行布局符合W3C标准,采用这种方式布局通常是为了说明与HTML表格定位方式的区别.因为现在的网站设计标 ...