在OC中也有一种内存自动释放的机制叫做“自动引用计数”(或“自动释放池”),与C#、Java不同的是,这只是一种半自动的机制,有些操作还是需要我们手动设置的。
自动内存释放使用NSAutoreleasepool声明一个自动释放池,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。这样一来就起到了自动释放的作用,同时对象的销毁过程也得到了延迟(统一调用release方法)。看下面的代码:
HXStudent.h
#import <Foundation/Foundation.h>
#import "HXBook.h"
@interface HXStudent : NSObject
/**
* 人名
*/
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
+ (instancetype)studentWithName:(NSString *)name;
@end
HXStudent.m
#import "HXStudent.h"
@implementation HXStudent
- (instancetype)initWithName:(NSString *)name {
if (self = [super init]) {
_name = name;
}
return self;
}
+ (instancetype)studentWithName:(NSString *)name {
HXStudent *student = [[[self alloc] init] autorelease];// 因为有alloc,所以要autorelease,遵循原则
student.name = name;
return student;
}
- (void)dealloc {
NSLog(@"销毁student,name = %@", _name);
[super dealloc];
}
@end
在main函数中:
int main(int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
HXStudent *student1 = [[HXStudent alloc] init];
[student1 autorelease];// 因为有alloc,所有要autorelease
student1.name = @"shx1";// 由于autorelease是延迟释放,所以这里仍然可以使用student1
HXStudent *student2 = [[[HXStudent alloc] initWithName:@"shx2"] autorelease];// 因为有alloc,所有要autorelease
HXStudent *student3 = [HXStudent studentWithName:@"shx3"];// 内部已经调用了autorelease,所以不需要手动释放,这也符合内存管理原则,因为这里并没有alloc所以不需要release或者autorelease
NSLog(@"在这里之前没有销毁上面三个对象");
// [pool drain];// 添加该行代码,会一个提示(double release)
[pool release];
return 0;
}
如果将断点打在NSLog一行,可以看到程序运行到这里,三个对象还没有被销毁(打断点只是为了观察)。当执行[pool release]完毕的时候,三个对象才会被销毁。因此上面person1,调用完autorelase之后它还存在,因此给name赋值不会有任何问题;在OC中通常如果一个静态方法返回一个对象本身的话,在静态方法中我们需要调用autorelease方法,因为按照内存释放原则,在外部使用时不会进行alloc操作也就不需要再调用release或者autorelase,所以这个操作需要放到静态方法内部完成。
注意:
对象并不是自动被加入到当前pool中,而是需要对对象发送autorelease消息,这样,对象就被加到当前pool的管理里了。
drain只是用于清除pool中对象,不会销毁池,release先调用drain方法清理对象,然后再释放自己内存。
对于自动内存释放简单总结一下:
autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁);
由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放到多个自动释放池;
OC中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法。
管理内存遵循的原则:谁创建谁释放。
以上是在非ARC环境下的自动释放池的内存管理原理。
对于在ARC(自动引用计数)环境下,简单理解就是,系统会在适合的时候,自动帮我们调用retian、release操作,不用我们在手动写这些代码。
在ARC环境下声明自动释放池只需要使用@ autoreleasepool关键字的代码块就可以生产自动释放池,不用alloc。