【OC基础】03-OC内存管理

概述

跟Java和C#类似,OC创建的对象在堆上。与Java和C#不同的是Java和C#有垃圾回收机制,所以不需要程序员手动释放堆上的内存。而OC没有垃圾回收机制,必须手动管理内存的创建和释放。下面介绍一下OC内存管理内存管理的方式。

引用计数器

OC管理内存的方式类似C++中的智能指针,创建一个对象时,在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后初始化它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。

Student.h文件:

1 #import <Foundation/Foundation.h>
2 
3 @interface Student : NSObject
4 
5 #pragma mark - 属性
6 @property (nonatomic,copy)NSString *name;
7 @property (nonatomic,assign)float age;
8 
9 @end

Student.m文件:

1 #import "Student.h"
2 
3 @implementation Student
4 
5 -(void)dealloc{
6     NSLog(@"Invoke Student dealloc method");
7     [super dealloc];
8 }
9 @end

main.m文件:

 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         
 8         // insert code here...
 9         Student *stu = [[Student alloc]init];
10         stu.name=@"lisi";
11         stu.age=12;
12         NSLog(@"retainCount=%lu",[stu retainCount]);
13         //结果为1
14         [stu retain];
15         NSLog(@"retainCount=%lu",[stu retainCount]);
16         //结果为2
17         [stu release];
18         NSLog(@"retainCount=%lu",[stu retainCount]);
19         //结果为1
20         [stu release];
21         stu=nil;
22         [stu release];
23     }
24     return 0;
25 }

我们可以看出,在创建一个对象之后,初始化引用计数器为1,retain之后,计数器增加1,然后每release一次,计数器都会减1,直到这个对象被释放。

内存释放的原则

对象之间引用可以是复杂的,有时候会出现相互引用既循环引用的情况,所以对象的释放不能紧紧依靠自动化来完成。内存释放的原则是谁创建,谁释放。如下:

Book.h:

1 #import <Foundation/Foundation.h>
2 
3 @interface Book : NSObject
4 @property (nonatomic,copy) NSString *bookName;
5 -(void)read;
6 -(id)initWithName:(NSString*) bname;
7 @end

Book.m

 1 #import "Book.h"
 2 
 3 @implementation Book
 4 -(void)read{
 5     NSLog(@"now read this book %@...",self.bookName);
 6 }
 7 -(id)initWithName:(NSString*) bname{
 8     [super init];
 9     _bookName=bname;
10     return self;
11 }
12 -(void)dealloc{
13     NSLog(@"Invoke book %@ dealloc method...",self.bookName);
14     [super dealloc];
15 }
16 @end

Student.h

 1 #import <Foundation/Foundation.h>
 2 #import "Book.h"
 3 @interface Student : NSObject{
 4     Book *_book;
 5 }
 6 
 7 #pragma mark - 属性
 8 @property (nonatomic,copy)NSString *name;
 9 @property (nonatomic,assign)float age;
10 -(id)initWithNameAndAge:(NSString*)sname andAge:(float) sage;
11 -(void)setBook:(Book*)book;
12 -(Book*)book;
13 @end

Student.m

 1 #import "Student.h"
 2 
 3 @implementation Student
 4 
 5 -(id)initWithNameAndAge:(NSString *)sname andAge:(float)sage{
 6     self.name=sname;
 7     self.age=sage;
 8     return self;
 9 }
10 -(void)setBook:(Book *)book{
11     if (_book!=book) {
12         [_book release];
13         _book = [book retain];
14     }
15 }
16 
17 -(Book*)book{
18     return _book;
19 }
20 
21 -(void)dealloc{
22     NSLog(@"Invoke Student dealloc method");
23     [_book release];
24     [super dealloc];
25 }
26 @end

main.m

 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         
 8         // insert code here...
 9         Student *stu = [[Student alloc]initWithNameAndAge:@"sili" andAge:12];
10       
11         Book *book1 = [[Book alloc]initWithName:@"AAA"];
12         [book1 release];
13         [stu.book read];
14         
15         Book *book2 = [[Book alloc]initWithName:@"BBB"];
16         [stu setBook:book2];
17         [book2 release];
18         [stu.book read];
19         [stu release];
20     }
21     return 0;
22 }

运行结果:

1 2015-05-03 11:59:24.939 first[1814:303] Invoke book AAA dealloc method...
2 2015-05-03 11:59:24.941 first[1814:303] now read this book BBB...
3 2015-05-03 11:59:24.942 first[1814:303] Invoke Student dealloc method
4 2015-05-03 11:59:24.942 first[1814:303] Invoke book BBB dealloc method...

属性参数

其实我们可以直接利用OC提供的属性来达到同样的效果:

@property (nonatomic,retain)Book *book;

属性包含的所有参数和用法如下:

如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)。一般情况下如果是基本类型使用assign,非字符串对象使用retain,字符串对象使用copy。原子性和读写属性根据需要设置即可。

自动释放池

自动内存释放使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。

 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         
 8         // insert code here...
 9         Student *stu = [[Student alloc]initWithNameAndAge:@"lisi" andAge:12];
10         Student *stu1 = [[[Student alloc]initWithNameAndAge:@"wanger" andAge:14]autorelease];
11         [stu1 retain];
12         [stu autorelease];
13     }
14     return 0;
15 }

打印结果:

1 2015-05-03 12:27:39.949 first[1938:303] Invoke Student dealloc lisi method

我们可以看出,stu对象被完全释放,而stu1没有被释放,这是因为每个对象的release方法只被调用了一次,而stu1的引用计数为2,所以在代码块结束的时候stu1对象不会被释放(造成了内存泄露)。对于自动内存释放有以下几点需要注意:

  1. autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
  2. 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(如果对象的引用计数器>1,就无法销毁);
  3. 由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放到多个自动释放池;
  4. ObjC中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法;
原文地址:https://www.cnblogs.com/desheng-Win/p/4474529.html