内存管理
一、retain与release
每个对象内部都保存了一个与之相关联的整数,称为引用计数器。
当使用alloc、new、copy创建一个对象时,对象的引用计数器被设置为1。
给对象发送一条retain消息,可以使引用计数器值+1。
给对象发送一条release消息,可以使引用计数器值-1。
给对象发送一条retainCount消息,可以获得当前的引用计数器值。
当一个对象的引用计数器值为0时,该对象将被销毁,其占用的内存将被回收,OC也会自动向对象发送一条dealloc消息,一般会重写dealloc方法释放相关资源,一定不要直接调用dealloc方法。
Student.m
1 #import "Student.h" 2 3 @implementation Student 4 5 - (void)dealloc 6 { 7 NSLog(@"%@被销毁了", self); 8 [super dealloc]; //一定要调用super的dealloc方法,且最好放在最后面调用 9 } 10 11 @end
main.m
1 #import <Foundation/Foundation.h> 2 #import "Student.h" 3 4 int main(int argc, const char * argv[]) 5 { 6 7 @autoreleasepool { 8 9 Student *stu = [[Student alloc] init]; 10 NSLog(@"stu.retainCount = %zi", stu.retainCount); //retainCount=1 11 12 [stu retain]; 13 NSLog(@"stu.retainCount = %zi", stu.retainCount); //retainCount=2 14 15 [stu release]; 16 NSLog(@"stu.retainCount = %zi", stu.retainCount); //retainCount=1 17 18 [stu release]; //retainCount=0,对象销毁 19 20 [stu release]; //EXC BAD ACCESS,会发生野指针错误,也就是说访问了不属于你的内存 21 22 } 23 return 0; 24 }
控制台输出:
2013-04-27 23:17:40.958 Test3[1612:303] stu.retainCount = 1 2013-04-27 23:17:40.963 Test3[1612:303] stu.retainCount = 2 2013-04-27 23:17:40.965 Test3[1612:303] stu.retainCount = 1 2013-04-27 23:17:40.966 Test3[1612:303] <Student: 0x100109a10>被销毁了 2013-04-27 23:17:40.972 Test3[1612:303] <Student: 0x100109a10>被销毁了 Test3(1612,0x7fff79b12180) malloc: *** error for object 0x100109a10: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug
二、对象之间的内存管理
Book.h
1 #import <Foundation/Foundation.h> 2 3 @interface Book : NSObject 4 5 @end
Book.m
1 #import "Book.h" 2 3 @implementation Book 4 5 - (void)dealloc 6 { 7 NSLog(@"%@被销毁了", self); 8 [super dealloc]; 9 } 10 11 @end
Student.h
1 @class Book; 2 3 @interface Student : NSObject { 4 Book *_book; 5 } 6 7 @property Book *book; 8 9 @end
Student.m
1 #import "Student.h" 2 #import "Book.h" 3 4 @implementation Student 5 6 - (Book *)book { 7 return _book; 8 } 9 10 - (void)setBook:(Book *)book { 11 if (_book != book) { //避免重复设置同一个Book导致book release后无法retain 12 [_book release]; //release旧值 13 _book = [book retain]; //retain新值 14 } 15 } 16 17 - (void)dealloc 18 { 19 NSLog(@"%@被销毁了", self); 20 [_book release]; //释放book 21 [super dealloc]; 22 } 23 24 @end
main.m
1 #import <Foundation/Foundation.h> 2 #import "Student.h" 3 #import "Book.h" 4 5 int main(int argc, const char * argv[]) 6 { 7 8 @autoreleasepool { 9 10 Book *book1 = [[Book alloc] init]; 11 NSLog(@"book1.retainCount = %zi", book1.retainCount); //book1.retainCount=1 12 Book *book2 = [[Book alloc] init]; 13 NSLog(@"book2.retainCount = %zi", book2.retainCount); //book2.retainCount=1 14 15 Student *stu = [[Student alloc] init]; 16 NSLog(@"stu.retainCount = %zi", stu.retainCount); //stu.retainCount=1 17 18 stu.book = book1; 19 NSLog(@"book1.retainCount = %zi", book1.retainCount); //book1.retainCount=2 20 21 stu.book = book1; 22 NSLog(@"book1.retainCount = %zi", book1.retainCount); //book1.retainCount=2 23 24 stu.book = book2; 25 NSLog(@"book1.retainCount = %zi", book1.retainCount); //book1.retainCount=1 26 NSLog(@"book2.retainCount = %zi", book2.retainCount); //book2.retainCount=2 27 28 [stu release]; //stu.retainCount=0,对象销毁 29 NSLog(@"book1.retainCount = %zi", book1.retainCount); //book1.retainCount=1 30 NSLog(@"book2.retainCount = %zi", book2.retainCount); //book2.retainCount=1 31 32 [book1 release]; //book1.retainCount=0,对象销毁 33 [book2 release]; //book2.retainCount=0,对象销毁 34 35 } 36 return 0; 37 }
控制台输出:
2013-04-28 01:03:35.229 Test3[1865:303] book1.retainCount = 1 2013-04-28 01:03:35.231 Test3[1865:303] book2.retainCount = 1 2013-04-28 01:03:35.233 Test3[1865:303] stu.retainCount = 1 2013-04-28 01:03:35.235 Test3[1865:303] book1.retainCount = 2 2013-04-28 01:03:35.236 Test3[1865:303] book1.retainCount = 2 2013-04-28 01:03:35.237 Test3[1865:303] book1.retainCount = 1 2013-04-28 01:03:35.238 Test3[1865:303] book2.retainCount = 2 2013-04-28 01:03:35.239 Test3[1865:303] <Student: 0x100109f70>被销毁了 2013-04-28 01:03:35.240 Test3[1865:303] book1.retainCount = 1 2013-04-28 01:03:35.241 Test3[1865:303] book2.retainCount = 1 2013-04-28 01:03:35.242 Test3[1865:303] <Book: 0x100109a40>被销毁了 2013-04-28 01:03:35.243 Test3[1865:303] <Book: 0x100102540>被销毁了
三、@property(retain)
每写一个setter方法都要写release和retain,可想而知非常麻烦,因此有了更简单的方法
Student.h
1 @class Book 2 3 @interface Student : NSObject 4 5 //这里的retain代表:在setter方法中release旧值,retain新值 6 @property(retain) Book *book; 7 //在实现类中会自动生成以下代码 8 //- (Book *)book { 9 // return _book; 10 //} 11 12 //- (void)setBook:(Book *)book { 13 // if (_book != book) { 14 // [_book release]; 15 // _book = [book retain]; 16 // } 17 //} 18 19 @end
Student.m
1 #import "Student.h" 2 #import "Book.h" 3 4 @implementation Student 5 6 7 - (void)dealloc 8 { 9 NSLog(@"%@被销毁了", self); 10 [_book release]; //释放book,这句代码不可省略 11 [super dealloc]; 12 } 13 14 @end
四、内存管理原则
1、谁创建,谁释放。如果通过alloc、new、copy来创建一个对象,那么必须调用release或者autorelease。
2、一般来说,除了alloc、new、copy之外的方法创建的对象都被声明了autorelease。
3、谁retain,谁release。只要调用了retain,无论这个对象是如何生成的,都要调用release。