内存管理(1)
参考自:http://www.cnblogs.com/kenshincui/p/3870325.html
在大多数语言中,例如C#,Java都是用的垃圾回收机制去管理内存,而Objective-C主要用的是MRC和ARC。
GC管理内存例子:
using System;
class Program
{
static void Main(string[] args)
{
test();
}
private static void test()
{
object o = new object();
}
}
在Test()方法中,通过new Object()创建了一个对象,o是对象的引用(存储了对象的地址),它是一个局部变量,作用返回是Test()方法的内部。当执行完Test()方法之后o就会被释放,此时由于没有变量在引用new Object()这个对象,因此垃圾回收会回收这个对象所占用的空间。
以上是垃圾回收机制的原理简介。那么我们现在来看一下Objective-C内存管理机制----
Objective-C内存管理是利用的对象引用计数器来进行的。我们可以在NSObject.h中发现一个retainCount属性,其声明如下:
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
这个整数叫“引用计数器”,当一个对象在创建之后他的引用计数器为1,当调用这个对象的alloc,retain,new,copy方法之后引用计数器在原来的基础上加1,当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。下面让我们来一个测试,测试之前首先设置项目在非ARC环境中:
- 设置项目不使用ARC:
Project--->Build Settings--->搜索garbage,找到Objective-C Automatic Reference Counting设置为No即可。
接下来看一下代码:
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,assign)int age;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)dealloc {
NSLog(@"Invoke Person's dealloc method");
[super dealloc];
}
@end
在ViewController中。
- (void)viewDidLoad {
[super viewDidLoad];
Person *obj = [[Person alloc] init];
obj.name = @"zhangsan";
obj.age = 20;
NSLog(@"引用计数retainCount:%lu
",(unsigned long)[obj retainCount]);
[obj release];
NSLog(@"%@
",obj);
NSLog(@"%@",obj.name);
// Do any additional setup after loading the view, typically from a nib.
}
当我们调用release方法,obj指向的对象就会被销毁,但是此时变量obj中还是存放着Person对象的地址。如果不设置obj=nil的话,obj就是一个野指针,它指向的内存已经不属于这个程序了,因此是很危险的。
如果我们不设置obj=nil,此时如果再调用对象release会报错,但是如果此时p已经是空指针了,则在oc中给空指针发送消息是不会报错的。
我们可以通过dealloc方法来查看一个对象是否被回收,如果没有回收则可能造成内存泄露。如果一个对象被释放后,那么最后引用它的变量需要我们手动设置为nil,否则可能造成野指针错误。需要注意的是:oc给空对象发送消息是不会引起错误的。
野指针形式错误在Xcode中通常的表现为:Thread 1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)错误。因为你访问了一块已经不属于你的内存。
对于一些属性的解释:
如果不进行设置,默认的参数有:atomic,readwrite,assign。
使用场景:
- 一般情况下如果在多线程开发中一个属性可能会被两个以及以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高。
- readwrite方法会生成getter、setter方法,如果使用readonly则只生成getter方法;关于set方法处理需要特别说明:有以下三种生成代码:
- assign:用于基本数据类型(delegate也是)
- (void)setA:(int)a { _a = a; }
- retain:通常用于非字符串对象。
- (void)setA:(Car *)a { if(_a!=a) { [_a release]; _a = [a retain]; } }
- copy:通常用于字符串对象。
- (void)setA:(NSString *)a { if(_a!=a) { [_a release]; _a = [a copy]; } }
- assign:用于基本数据类型(delegate也是)
自动释放池
在OC中另一种内存自动释放的机制叫做:“自动引用计数”,也叫自动释放池。自动内存释放使用@autoreleasepool关键字声明了一个代码块,如果一个对象在初始化时调用的autorelease方法,那么当代码块执行完后,在块中调用过autorelease方法的对象都会自动调用一次release方法,这样就起到了自动释放的作用,同时对象的销毁过程也得到了延迟。
对自动内存释放简单总结:
- autorelease方法不会改变对象的引用计数器,只是将这个对象放到了自动释放池中。
- 自动释放池实质就是当自动释放池销毁后调用对象的release方法,但是也不一定能够销毁对象,因为可能此时引用计数器大于1。
- 由于自动释放池最后同意销毁对象,因此如果一个操作比较占用内存,最好不要放到自动释放池或者放到多个自动释放池。
- objc中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法。