您的位置:首页 > 移动开发 > IOS开发

iOS 指南系列: 内存管理

2012-04-19 17:30 239 查看
在审核其它人的代码的过程中,总能发现内存管理的问题,尤其是如果你是从java/c#转型过来的话,当然现在这个阶段你会发现ARC实际上也很有帮助的。

When I review code from other developers, it seems that the most common mistakes are centered around memory management in Objective-C. If you are used to a language that handles
memory management for you like Java or C#, things can be quite confusing!

you’ll learn how memory management works in Objective-C by getting some hands-on experience. We’ll talk about how reference counting works, and go through each of the memory-management keywords by building a real-world example – an app about your favorite types
of sushi!

This tutorial is intended for beginner iOS developers, or intermediate developers in need of a refresher on this topic.

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {
NSArray * _sushiTypes;
}

@end


By declaring this, each instance of RootViewController will have space to store a pointer to an NSArray, which is the Objective-C class you use for arrays that don’t change after initialization. If you need to change the array after initialization (for example,
add an item into it later on), you should use NSMutableArray instead.

如果我们事例化这个对象,数组空间未分配,只是有一个指针已存在了。如果要修改的数组,最好使用NSMutableArray

You may be wondering why we named the variable with an underscore in front. This is just something I personally like to do that I think makes things easier. I’ll talk more about why I like do this when I write a follow-up tutorial about Objective-C properties.
But for now note that all we’ve done so far is add an instance variable – we haven’t done anything with properties yet – and we’ve named it with an underscore just as a personal preference, it doesn’t do anything special.

我们使用下划线,表示局部变量,这个是个人倾向问题。

- (void)viewDidLoad {
[super viewDidLoad];

_sushiTypes = [[NSArray alloc] initWithObjects:@"California Roll",
@"Tuna Roll", @"Salmon Roll", @"Unagi Roll",
@"Philadelphia Roll", @"Rainbow Roll",
@"Vegetable Roll", @"Spider Roll",
@"Shrimp Tempura Roll", @"Cucumber Roll",
@"Yellowtail Roll", @"Spicy Tuna Roll",
@"Avocado Roll", @"Scallop Roll",
nil];
}


Now we’re getting into memory management! Objects you create in Objective-C are reference-counted.
This means that each object keeps track of how many other objects have a pointer to it. Once the reference count goes down to zero, the memory for the object can be safely released.

通过以上的代码调用alloc,我们有了内存区域了,任何的object-c对象,多有一个引用计数,听起来和com有点一样的,一般会采用alloc,然后init,这个等价于直接new,这和C++的概念完全一致。当我们完成alloc+init之后,对象的ref counter加1.在object-C中,使用release来给ref counter减1

So your job, as a programmer, is to make sure that the reference count is always accurate. When you store a pointer to an object somewhere (like in an instance variable), you need to increment the reference count, and when you’re done with it you need to decrement
the reference count.
- (void)viewDidUnload {
[_sushiTypes release];
_sushiTypes = nil; //view卸载,设置空
}

- (void)dealloc {
[_sushiTypes release]; //减一
_sushiTypes = nil;
[super dealloc];
}
我们看下面的代码,第一行给了一个指针一个值,作为指针本身而言,没有所谓的ref counter的,就认为是一个4bytes的变量;但第二行,我们申请了内存区域,这个对象就有ref count=1.通过第4行,release,这样这个内存对象才会被clean

NSString * sushiName = [_sushiTypes objectAtIndex:indexPath.row]; // 1
NSString * sushiString =
[[NSString alloc] initWithFormat:@"%d: %@",
indexPath.row, sushiName]; // 2
cell.textLabel.text = sushiString; // 3
[sushiString release]; // 4
当我们使用auto的时候,希望程序在之后自己release,给予权利,一般至少在一个scope内,对象还是可以使用的,但可能在后续使用该对象中会遇到问题

When you autorelease an object, it’s kind of like saying “I want you to be released at some point in the future, like the next time the run loop gets called. But for now I want to be able to use it.”
我们再看下面的代码,其中stringwithformat,而不是init函数了。这个时候我们获得的是什么?pointer?
NSString * sushiName = [_sushiTypes objectAtIndex:indexPath.row]; // 1
NSString * sushiString =
[NSString stringWithFormat:@"%d: %@",
indexPath.row, sushiName]; // 2
cell.textLabel.text = sushiString; // 3


这里有个隐含的东西,nsstring的函数stringwithformat实际上会返回一个string但是带有autorelease的。那目前在这个scope中使用sushistring是完全可以的,但如果你想保存这个string(把指针赋给另外一个变量),然后在其它地方使用,这样很容易导致问题的,最好不要这么做,如果真需要,那么使用retain//

这个如何判断?其实也很简单的,如果你使用的方法:

1. 名字以init或者copy开头,那么返回的对象是没有autorelease pending的,也意味着,你需要自己去release/管理

2. 名字是其它打头的,那么返回的是autorelease的,你可以现在使用它,但最好不要保存,或者显示retain对象。

如果是你i自己写的函数,那么还真的你自己完全了解了:)

autorelease这个东西,就相当于随时会发生-1,但你不知道什么时候,如果要计算匹配,那么把autorelease算一次release/ 所有release的数目=ref count

[_lastSushiSelected release];
_lastSushiSelected = nil;


一般最后一个release就是放在系统的dealloc的

第二篇:用工具帮助检测内存管理的好坏
http://www.raywenderlich.com/2696/how-to-debug-memory-leaks-with-xcode-and-instruments-tutorial
不管你对内存管理理解有多深,总有时候你会犯些错误,如果代码又非常多,除非你想瞬间白发,否则不会一行行去分析的





我们在之前的指南系列中也提到了,memory AV是很难调试的,程序员看到这个就头大了,again.胡乱尝试/念个歌谣是不管用的,这里还是强调之前的几个方法: EXC_BAD_ACCESS

启用first chance exception断点

使用静态分析(在build settings)/确保fix所有的warning

Set the NSZombieEnabled argument(in xcode3) in your executable options, which sometimes helps narrow down the cause;如果Xcode4,在schema中就可以设置了

Run with Apple Instruments such as Leaks to look for memory issues

Set a breakpoint in your code and step through until you narrow down where it’s crashing

Tried and true “comment out code till it works” then backtrack from there :]

profile的工具还是很多的,也有专门for leak检查的





选择call tree,可以清晰看到导致leak object的函数,尝试fix他们吧。
1.在xcode4以后画面有所不一样,product-》profile或者command+I

2.默认情况下,profile的是release版本的build



The hint here is that the variable you're assigning to is not a pointer type. That is,
you write "CGRect foo = CGRectMake(...)", rather than "CGRect *foo = CGRectMake(...)". If you were doing the latter, then it *would* be necessary to worry about the lifetime of the piece of memory that 'foo' pointed to. But you're not.

(There are some subtleties to it, but they're all C subtleties, not ObjC subtleties. :) In practice, a C function returning a struct is like a void function with a hidden "struct *" argument into which it writes its return value. The compiler will generate
short-lived stack- allocated storage in the calling function if needed. And a struct passed as an argument will, effectively, be broken apart as if you'd passed each of its members individually. Both of these are implementation details you shouldn't rely on,
but it's useful to know them when you're debugging. I think this is a C89 thing --- the language hasn't always supported struct return and argument types.)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: