objective-c的set方法内存管理

基本数据类型如int不需要进行内存管理,但是凡是继承了NSObject的对象就需要进行内存管理。

引用计数器是oc内存管理一个很重要的部分。

alloc即开辟新内存时将计数器设置为1

retain:计数器+1

release:计数器-1

当计数器为0时,会自动销毁内存,系统自动调用dealloc方法。(ps: 这里可类比C++的析构函数,但又不尽相同。)

内存管理的有一条黄金法则:谁创建,谁释放。

关于set方法的内存管理策略,主要归纳为以下两点:

1. 重写set方法。

2. 重写dealloc方法。

 

很多时候,会出现一个类中含有另一个类的变量,或者说一个对象是另一个对象的属性。比如有一个Student类和一个Book类。Student类中含有Book变量。当Student的一个对象被释放的时候,对应的Book对象也会被释放。而如果还想在main函数中使用Book对象就需要重新创建了,这变得很麻烦。为了解决这个问题,需要在Student对象引用Book对象的时候将引用计数+1。而一般这种引用时出现在set方法中,所以在set方法中要先retain:

- (void)setBook:(Book *)book {
        _book = [book retain];
}

这样,从以下这一块可以看出,它是符合“谁创建,谁释放”这个法则的。

Student *stu = [[Student alloc] init];
Book *book = [[Book alloc] init];//book: 1

stu.book = book;//set方法时计数器+1,所以book: 2

[book release];//book: 1
...
...
...
[stu release];

在[book release]释放后,因为其计数为1,stu对象的book对象仍能被使用:

[stu METHOD];

但是book对象使用后,又没有给它释放内存。所以要在Student.m中重写dealloc方法:(dealloc方法是继承于父类的NSObject中的)

- (void)dealloc {
    [_book release];
    //[self.book release];与上面效果一样
    
    [super dealloc];//这句一定要写在最后
}

这样在最后调用[stu release]的时候便可以将book计数器置为0,于是book对象得到释放。

 

但还有一个问题,当Book有多个对象时,在最后释放对象stu之前,对象stu中的变量_book得到的引用是最后使用的一个Book对象。所以在main函数中[stu release]时调用的重写的dealloc方法中[_book release]只会将最后使用的Book对象内存释放,而之前的Book对象的内存则不能被释放了。

void test(Student *stu) {

  Book *book = [[Book alloc] init];//book: 1
  stu.book = book;//book: 2
    
  [book release];//book: 1

    
  Book *book2 = [[Book alloc] init];//book2: 1
  stu.book = book2;//book2: 2
    
  [book2 release];//book2: 1
}
int main()
{
    Student* stu = [[Student alloc] init];//stu: 1
    test(stu);

    [stu release];// stu: 0, book2: 0, book: 1
}

 

因此,在每次使用Book对象给stu的变量_book赋值(即使用set方法时),要释放上一个_book引用的内存。

所以要修改set方法如下:

- (void)setBook:(Book *)book {
        [_book release];//释放上一个引用的内存,即使_book为nil,也不会报错
        _book = [book retain];
}

 

另外,还要防止新赋的book和原来的_book相同的情况,比如以下重复赋相同值:

void test(Student *stu) {
    Book *book = [[Book alloc] init];
    stu.book = book;
    
    [book release];

    stu.book = book;

}

如果出现这种情况,以上代码会导致野指针错误。

因此需要添加一个判断条件:

- (void)setBook:(Book *)book {
    if (_book != book) {
        [_book release];
        _book = [book retain];
    }
    
}

这样内存就不会泄漏了。

 

 

PS:在学习过程中,编译器出现“EXC_BAD_ACCESS” 表示访问了已被释放的内存,即野指针错误。

 

 

 

posted @ 2014-01-28 03:17  xiaovid  阅读(463)  评论(0)    收藏  举报