【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方法來銷燬這個對象。this

Student.h文件:atom

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文件:spa

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,直到這個對象被釋放。code

內存釋放的原則

對象之間引用能夠是複雜的,有時候會出現相互引用既循環引用的狀況,因此對象的釋放不能牢牢依靠自動化來完成。內存釋放的原則是誰建立,誰釋放。以下:對象

Book.h:blog

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方法;
相關文章
相關標籤/搜索