专栏首页Elton的技术分享博客Cocoa内存管理的简单规则[翻译]

Cocoa内存管理的简单规则[翻译]

看了一篇mmalcolm crawford写的文章,觉得不错, 原文在此。比较清楚的讲解了Cocoa的内存管理。 对于Mac和iPhone的开发有很大帮助。 特翻译并略做修改以方便理解,希望与大家共勉

对于一个新的Cocoa开发者来说,刚接触到内存管理的时候,一定很困惑。 下面给出了一些简单的规则,可以让你舒服些。如果你没有很好的使用这些规则的话,通常会带来内存泄露的问题或者运行时的异常。

Cocoa过去没有垃圾回收机制,iPhone现在也没有。所以你必须自己来通过-retain, -release and -autorelease这些命令使用引用计数(reference counting)技术来管理内存。

方法

描述

-retain

给一个对象的引用计数加1

-release

给一个对象的引用计数减1

-autorelease

在将来的某些时候将一个对象的引用计数减1

-alloc

分配一块内存给对象,引用计数器将设为1

-copy

拷贝一个对象,将返回引用计数为1的一个对象

引用计数规则

  1. \u000a```在你的方法中使用了-copy\u000a```, \u000a```-alloc\u000a``` 和 \u000a```-retain来申请内存,就要对应的使用\u000a```\u000a```-release\u000a``` 和 \u000a```-autorelease来释放内存;\u000a```
  2. 当对象使用便捷方法创建的时候(如:NSString类的 stringWithString方法),则这个对象将被视为已经使用了\u000a```autorelease,在将来某时自动释放内存;\u000a```
  3. 如果你定义了实例变量,则在你的类中实现\u000a```-dealloc这个方法来释放他们。\u000a```

例子:

-alloc / -release

- (void)printHello
{
	NSString *string;
	string = [[NSString alloc] initWithString:@"Hello"];
	NSLog(string);
	// 我们使用alloc来创建了一个string 所以要release它
	[string release];
}

便捷构造方法

- (void)printHello
{
    NSString *string;
    string = [NSString stringWithFormat:@"Hello"];
    NSLog(string);
    // 我们构建这个string的时候,使用了便捷构造方法( convenience constructor )
    // 所以我们认为它是 autoreleased的
}

永远使用accessor方法

有时候你会觉得使用accessor方法会比较教条和无聊,但是使用他们会降低内存泄露的机会。

如果对于实例变量你也使用 -retain 和-release 来管理内存的话,那就错了。

例子 在接口中定义了一个实例变量

@interface Counter : NSObject
{
	NSNumber *count;
}

在实现中加入accessor方法

- (NSNumber *)count
{
	return count;
	// 不需要retain或者release
	// 我们只是返回这个变量的值
}

- (void)setCount:(NSNumber *)newCount
{

	// 如果每个人都使用上述同样的内存管理规则
	// 我们就得认为newCount是autoreleased的。
	// 因为我们想使用这个变量,所以我们必须retain它
	// 避免它被释放掉。
	[newCount retain];

	// 因为我们只想在这个方法中改变这个类中的count值
	// 所以要通过这个方法,先释放掉之前的内存
	// 在Objective-C中[nil release]也是被允许的
	// 我们必须将次调用放在 [newCount retain] 的后面
	// 因为如果当count和newCount都指向同一个对象的时候
	// 我们会错误的释放它
	[count release];

	// 关联新的引用
	count = newCount;
}

因为我们的类有实例变量,所以需要实现-dealloc方法来释放内存。

- (void)dealloc
{
	[self setCount:nil];
	[super dealloc];
}

假设我们需要一个重置count的值的方法,我们有两个选择

便捷构造方法convenience constructor

- (void)reset
{
	NSNumber *zero = [NSNumber numberWithInt:0];
	[self setCount:zero];
}

使用-alloc方法

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

常见错误

下面的问题在一些简单的环境下可能会正常工作,但是避开使用accessor方法,在某些时候几乎可以肯定会带来内存泄露的问题。

没有使用accessor方法

- (void)reset
{
	NSNumber *zero = [[NSNumber alloc] initWithInt:0];
	[count release]
	count = zero;
}

变量没有释放

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

当使用alloc创建一个新对象的时候,retain count是1,如果我们没有在这个方法中使用-release方法,这个number对象将永远不能被释放,将会造成内存泄露。

已经释放内存的变量再次调用release

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

这将导致当你第一次访问count的时候发生错误。 便捷构造方法将返回一个autoreleased对象,所以你不必使用release。 在使用autoreleased后这样做,将减少count的计数到0,这个对象将被释放。当你之后视图访问count的时候,你将向一个自由对象(free object)发送调用消息(message),通常你将得到一个SIGBUS 10的错误。

经常让人混淆迷惑的问题 - 数组和其他集合类

当一个对象被添加到一个array, dictionary, 或者 set等这样的集合类型中的时候,集合会retain它。 对应的,当集合类被release的时候,它会发送对应的release消息给包含在其中的对象。 因此,如果你想建立一个包含一堆number的数组,你可以像下面示例中的几个方法来做

NSMutableArray *array;
    int i;
    // ...
    for (i = 0; i < 10; i++)
    {
        NSNumber *n = [NSNumber numberWithInt: i];
        [array addObject: n];
    }

在这种情况下, 我们不需要retain这些number,因为array将替我们这么做。

NSMutableArray *array;
    int i;
    // ...
    for (i = 0; i < 10; i++)
    {
        NSNumber *n = [[NSNumber alloc] initWithInt: i];
        [array addObject: n];
        [n release];
    }

在这个例子中,因为你使用了-alloc去建立了一个number,所以你必须显式的-release它,以保证retain count的平衡。因为将number加入数组的时候,已经retain它了,所以数组中的number变量不会被release


本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.prosight.me/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • 【译】3条简单的React状态管理规则

    React组件内部的状态是在渲染之间保持不变的封装数据。useState()是React钩子,负责管理功能组件内部的状态。

    张张
  • XCode 常用快捷键

    EltonZheng
  • iOS - 老生常谈内存管理(二):从 MRC 说起

    MRC全称Manual Reference Counting,也称为MRR(manual retain-release),手动引用计数内存管理,即开发者需要手动...

    师大小海腾
  • 通过一个简单的ABAP报表窥探ABAP内存分配和管理机制

    Jerry Wang
  • Objective-C内存管理指南

    本文翻译自Advanced Memory Management Programming Guide

    Helloted
  • Cocoa包管理器之Carthage详解及CocoaPods中心化+Carthage的二进制化

    上篇博客详细的聊了CocoaPods的相关内容,今天我们就来介绍另一个Cocoa的包管理器Carthage。在上家公司用Swift开发工程时,用的就是Carth...

    lizelu
  • ios 笔试题3

         *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5,&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小...

    py3study
  • Swift 新语言开发「建议收藏」

    几个小时前熬夜看了WWDC,各种激动,今年非常有料啊!当看到Swift出来的时候,瞬间傻眼。又要学习新语言了。

    全栈程序员站长
  • 《Quartz 2D编程指南》电子签名、图片处理(水印、裁剪以及屏幕截图)、常见图形的绘制(饼图、柱状图、雪花、手势密码、画板)

    Everything you can draw using Cocoa can also be drawn using Quartz.

    公众号iOS逆向
  • IOS框架概览

    iOS是执行在iPhone、iPod Touch或iPad上的操作系统,之前叫做iPhone OS,iOS与Mac OS X有共同的基础架构和底层技术。但iOS...

    全栈程序员站长
  • Mac开发跬步积累(一):Cocoa Drawing 之 NSImage imageNamed: 到底做了什么?

    首先,NSImage提供了支持多种格式图像数据进行管理的api, 但是NSImage对被其管理的实际图像数据几乎是一无所知的,这是因为NSImage并没有直接与...

    代码行者
  • iOS学习——iOS 整体框架及类继承框架图

      整理自:IOS 整体框架类图值得收藏  一 整体框架 在iOS开发过程中,对iOS整体框架的了解和学习是必不可少的一个环节,今天我们就好好来了解一下iOS...

    mukekeheart
  • iOS中Cocoa框架·Runtime及isa指针知识·填坑

    是什么因素使一个程序成为Cocoa程序呢?不是编程语言,因为在Cocoa开发中你可以使用各种语言;也不是开发工具,你可以在命令行上就可以创建Cocoa程序。Co...

    陈满iOS
  • 老司机带你走进Core Animation 之CADisplayLink

    今天说点啥呢?上次老司机说过,带你走进CoreAnimation,那今天就趁热打铁,继续讲讲核心动画相关的东西吧。那今天要讲的就是CADisplayLink。

    老司机Wicky
  • 多线程(五):通信

    虽然一个好的设计可以最大限度地减少所需的通信量,但是在某些时候,线程之间的通信变得非常必要 (一个线程的工作是为你的应用程序工作,但是如果这个工作的结果从未被使...

    Helloted
  • 高级内存管理指南

    nimomeng
  • 研学社•架构组 | CoCoA:大规模机器学习的分布式优化通用框架

    机器之心原创 作者:Yanchen Wang 参与:panda 去年,Michael I. Jordan 实验室发表论文《CoCoA: A General Fr...

    机器之心
  • Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    需要注意的是CADisplayLink必须要添加到可以执行的RunLoop中才会执行, 当添加到某一个RunLoop后如果该RunLoop暂停或者该RunLoo...

    周希
  • 关于Swift

    Swift是一种新的建立在C和Objective-C的基础之上用来开发iOS和OS X应用,完全兼容C。 Swift采用了安全的编程模式并增添了现代的功能使编程...

    用户3004328

扫码关注腾讯云开发者

领取腾讯云代金券