OC 内存管理问题

AirT
  • 98

最近学习 OC 内存管理遇到个问题

main.m

int main(int argc, const char * argv[]) {
  Person *p1 = [[Person alloc] init];
  Person *p2 = [[Person alloc] init];
  Dog *d = [[Dog alloc] init];

  p1.dog = d;
  p2.dog = d;
  [d release];

  p1.dog = nil;
  p2.dog = nil;
  return 0;
}

Dog.m

@implementation Dog

- (void)dealloc
{
  NSLog(@"Dog 被销毁了");
  [super dealloc];
}

@end

Person.h

@class Dog;
@interface Person : NSObject

@property(retain) Dog *dog;

@end

上面的代码运行后,可以打印出 Dog 被销毁了。但是如果把 main.m 改成下面的代码就没有打印了。

int main(int argc, const char * argv[]) {
  Person *p1 = [[Person alloc] init];
  Person *p2 = [[Person alloc] init];
  Dog *d = [[Dog alloc] init];

  p1.dog = d;
  p2.dog = p1.dog;  // 只改了这一行
  [d release];

  p1.dog = nil;
  p2.dog = nil;
  return 0;
}

感觉这个问题可能和内存管理没什么关系,可能是变量引用的问题,初学 OC 希望大佬们指点下~

回复
阅读 890
1 个回答
✓ 已被采纳

问题应该出在这里

@property(retain) Dog *dog;

dog属性声明为了atomic类型,为了保证原子性,dog的set和get方法应该做过处理。
我感觉mrc下的atomic属性的set和get方法应该类似下面这样实现。

- (Dog *) dog {
    Dog *d = nil;
     @synchronized(self) {
        d = [[_dog retain] autorelease];
    }
     return d;
}

- (void)setDog:(Dog *)dog {
    @synchronized(self) {
        [dog retain];
        [_dog release];
        _dog = dog;
    }
}

所以在调用p2.dog = p1.dog;的时候,会先调用get方法(p1.dog),内部会先对dog retain一次,引用计数+1, 然后放进自动释放池,在调用set(p2.dog)时,又会retain一次。所以这一句代码引用计数其实是+2了, 所以最后在release和nil之后,引用计数仍然为1,不会释放。

按道理说在get里面+1的那一次应该被减回去的,但是因为main方法中的autoreleasepool被删掉了,所以标记的autorelease没有起到作用。

综上,atomic的get方法中为了安全多+1并没有被autorelease掉,所以导致引用计数无法被置0,属性无法释放,也就不会打印dealloc.

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏