一个关于内存管理的疑问

a522256746 2015-04-11 10:02:07
加精
下面有三个文件分别是main.m,Car.h,Car.m.
我创建了一个Car类,然后在Car中写了test方法,我在arc开启下运行的。但是我对test中的对象的内存有些疑问。

引用c指向了一个我手动创建的对象,而test方法本质上又是函数,那么在函数结束时,返回了car对象的地址。而c是一个局部变量,那么c就会被回收,c一旦被回收那么car对象没有强引用,那么car对象也被回收。既然car对象被回收,那么接收返回值的那个引用不是指向了一个僵尸对象么?那为什么我打开了僵尸对象检测还是不会报错?难道我上面的分析有什么地方出错了么?

希望能得到大家的帮助谢谢。

main.m
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {

Car *c = [[Car alloc] init];
Car *d = [c test];
[d test];

}
return 0;
}


Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int year;
- (Car *)test;
@end


Car.m
#import "Car.h"

@implementation Car
- (NSString *)description
{
return [NSString stringWithFormat:@"name:%@,year:%d",self.name,self.year];
}

- (Car *)test
{
Car *c = [[Car alloc] init];
return c;
}

- (void)dealloc
{
NSLog(@"Deallocated");
}
@end
...全文
2000 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
hwsdau 2015-04-26
  • 打赏
  • 举报
回复
大神 向你们学习!!
flykingz1 2015-04-15
  • 打赏
  • 举报
回复
学习学习学习学习学习学习
伊顺鸣 2015-04-15
  • 打赏
  • 举报
回复
可以了解了呢
guanjunuser 2015-04-15
  • 打赏
  • 举报
回复
挺高深的 学习了
hslinux 2015-04-13
  • 打赏
  • 举报
回复
这个问题,其实跟C/C++里面的“野指针”问题类似,或者说一致。 大概可以这样理解: A对象被释放了,对于系统来说,就是把A对象原来占据的内存标识为可用内存。对于程序来说,如果使用者不主动把A对象赋值为Nil,当这块内存没有被系统重新分配出去给其他对象时,该内存中实际上还是A对象原来的数据,这个时候,”对被释放了“的A对象做操作是没有问题的。但是使用者无法保证每次调用的时候,A对象所指向的内存没有被破坏。
szq880708 2015-04-13
  • 打赏
  • 举报
回复
不错,能学习到东西
dujiehong 2015-04-13
  • 打赏
  • 举报
回复
好详细,很厉害呢
xjs541888 2015-04-13
  • 打赏
  • 举报
回复
只能感觉到都是大神,目前我的技术实在插不上话
Smile_Couson 2015-04-13
  • 打赏
  • 举报
回复
坐而论道,学习了,果断Mark,否则就是损失。
ReyZhang 2015-04-13
  • 打赏
  • 举报
回复
引用 3 楼 a522256746 的回复:
[quote=引用 1 楼 zhanglei5415 的回复:] autoreleasepool 管理的是自动释放池中的对象。所谓的自动释放池中的对象不是说使用@autorelesaepool {} 作用域内的对象,而是对象通过发送autorelease消息后,这个对象才被加入到自动释放池中。 所以在ARC上,编译器会在适当的位置给 c指向的对象发送release 消息。对于Car 类中的test 方法返回的又是一个 Car类的实例,这个test方法在ARC环境下,会在返回Car实例时发送一个autorelease消息。也就是这个d指向的对象是一个autorelease对象 ,在@autoreleasepool {} 释放时,会向自动释放池中的所有autorelease对象发送release消息,也就是延迟释放,由自动释放池来管理对象什么时候被释放。 所以你上面的代码,在ARC下应该是没有问题的。 如果上述分析有误,还请指正。
等了好久终于有人来了,其实我去cocoachina也开了个同样的帖子。 传送门 那便都没什么人,太冷清了,这里好歹有版主压阵,而且是两个。 回归正题,你的想法跟我在cocoachina那的猜想一样。只有放入了autoreleasepool才能达到延迟释放的效果。 我的问题: 1.不知有没有书详细介绍了这一点,可以让我研究一下。 2.arc下会对对象做release或者autorelease对吧(难道都是autorelease没有release)?那它是如何判断的。是因为返回了局部引用所以就加上autorelease吗? 3.arc下自动内存管理,如果我想使用一个对象,我需要担心对象内存被释放掉了,是个nil引用吗?(我的意思是写代码的时候我需不需要去考虑对象的内存,什么时候被释放了,什么都是还没被释放)[/quote] zhangao帮你分析的已经很透彻了。这里我再简单的说说 1. 可以看看《Objective-C高级编程:iOS与OS X多线程和内存管理》这本书,本人手上就有一本 2. 其实在非ARC时代,像类中test 方法的情况应该说是内存管理原则的一个体现。 即:所有使用其他函数返回的对象,没有所有权。可以想一下:方法内的对象不能发送release直接被释放,这会影响到它的调用者,但如果不释放也会有内存问题,所以处理这种情况时我们会给函数返回的对象发送autorelase消息,以达到延迟释放。它的调用者,在使用时,可以使用retain 来持有(retain的目的是为了防止这个对象在某一时刻被释放),在使用完毕后再发送release消息来释放,以保证它的引用计数在release后的结果为0 3. 在ARC环境下,如果没有特殊的声明,对于id类型和对象类型默认的所有权修饰符是 _strong,即:自己生成并持有对象。虽然ARC会智能的帮助我们解决内存问题,但最好还是知道怎么回事。
laoer_2002 2015-04-13
  • 打赏
  • 举报
回复
大神
qq_27361139 2015-04-13
  • 打赏
  • 举报
回复
来自:http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-storestrong 显然,如果有这句代码:c = nil; 那么底层是通过调用objc_storeStrong(&c, nil)来完成释放工作的,arc 做的释放也是类似,也就是说OC 通过objc_storeStrong来释放强引用的对象。 在 test 这个函数里,编译器对objc_storeStrong函数调用了三次,为什么是三次调用?前两次好理解,分别释放c 和 c1,第三次其实是那个 nil,nil 与 NULL 不同,nil 是 id 类型的对象,(id)0。 所以说 arc 其实是分编译时和运行时的,你想看编译时做了哪些事情,你一方面可以看看源码,一方面可以自己多做测试,我提到的《Objective-C高级编程:iOS与OS X多线程和内存管理》这本书你也应该看看。 3. 需要关心,不一定要体现在代码里,而是你写代码的时候你大脑是清楚的知道你使用的对象此刻是不是 nil,有没有可能是 nil 的。
baidu_27360285 2015-04-13
  • 打赏
  • 举报
回复
只能感觉到都是大神,目前我的技术实在插不上话
sdshf2008 2015-04-12
  • 打赏
  • 举报
回复
只能感觉到都是大神,目前我的技术实在插不上话
Bannings 2015-04-12
  • 打赏
  • 举报
回复
引用 3 楼 a522256746 的回复:
[quote=引用 1 楼 zhanglei5415 的回复:]
autoreleasepool 管理的是自动释放池中的对象。所谓的自动释放池中的对象不是说使用@autorelesaepool {} 作用域内的对象,而是对象通过发送autorelease消息后,这个对象才被加入到自动释放池中。
所以在ARC上,编译器会在适当的位置给 c指向的对象发送release 消息。对于Car 类中的test 方法返回的又是一个 Car类的实例,这个test方法在ARC环境下,会在返回Car实例时发送一个autorelease消息。也就是这个d指向的对象是一个autorelease对象 ,在@autoreleasepool {} 释放时,会向自动释放池中的所有autorelease对象发送release消息,也就是延迟释放,由自动释放池来管理对象什么时候被释放。
所以你上面的代码,在ARC下应该是没有问题的。 如果上述分析有误,还请指正。


等了好久终于有人来了,其实我去cocoachina也开了个同样的帖子。
传送门
那便都没什么人,太冷清了,这里好歹有版主压阵,而且是两个。
回归正题,你的想法跟我在cocoachina那的猜想一样。只有放入了autoreleasepool才能达到延迟释放的效果。
我的问题:
1.不知有没有书详细介绍了这一点,可以让我研究一下。
2.arc下会对对象做release或者autorelease对吧(难道都是autorelease没有release)?那它是如何判断的。是因为返回了局部引用所以就加上autorelease吗?
3.arc下自动内存管理,如果我想使用一个对象,我需要担心对象内存被释放掉了,是个nil引用吗?(我的意思是写代码的时候我需不需要去考虑对象的内存,什么时候被释放了,什么都是还没被释放)[/quote]
1. 这个很好验证,在《Objective-C高级编程:iOS与OS X多线程和内存管理》这本书还没有出来之前,我们是通过直接看它编译后的汇编码来验证的,比如 test 函数:

我在init 这里打了断点,还没有执行 return 的时候,你通过下面这个操作把反汇编打开:

然后看 test 方法的真正实现:

两个重点的地方我已经标出来了,objc_retain和objc_autoreleaseReturnValue是两个函数,你可以网上看它们的源码,当然我们其实已经达到验证的目的了。

2. 这个也很简单,你其实多做点测试就能看出一二了,比如我们把 test 方法改成这样子:

- (Car *)test {
Car *c = [[Car alloc] init];
c = nil;

Car *c1 = [[Car alloc] init];
return nil;
}

改成这样是为了更容易理解,其中 c 是我们手动释放的,c1需要 arc 来释放,看看它们的汇编:

在第二个 alloc 之前有一个调用objc_storeStrong函数的动作,而且在 test 里,objc_storeStrong总共被调用了三次,我们先看看objc_storeStrong函数的实现:

来自:http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-storestrong
显然,如果有这句代码:c = nil; 那么底层是通过调用objc_storeStrong(&c, nil)来完成释放工作的,arc 做的释放也是类似,也就是说OC 通过objc_storeStrong来释放强引用的对象。
在 test 这个函数里,编译器对objc_storeStrong函数调用了三次,为什么是三次调用?前两次好理解,分别释放c 和 c1,第三次其实是那个 nil,nil 与 NULL 不同,nil 是 id 类型的对象,(id)0。
所以说 arc 其实是分编译时和运行时的,你想看编译时做了哪些事情,你一方面可以看看源码,一方面可以自己多做测试,我提到的《Objective-C高级编程:iOS与OS X多线程和内存管理》这本书你也应该看看。

3. 需要关心,不一定要体现在代码里,而是你写代码的时候你大脑是清楚的知道你使用的对象此刻是不是 nil,有没有可能是 nil 的。
a522256746 2015-04-12
  • 打赏
  • 举报
回复
引用 1 楼 zhanglei5415 的回复:
autoreleasepool 管理的是自动释放池中的对象。所谓的自动释放池中的对象不是说使用@autorelesaepool {} 作用域内的对象,而是对象通过发送autorelease消息后,这个对象才被加入到自动释放池中。 所以在ARC上,编译器会在适当的位置给 c指向的对象发送release 消息。对于Car 类中的test 方法返回的又是一个 Car类的实例,这个test方法在ARC环境下,会在返回Car实例时发送一个autorelease消息。也就是这个d指向的对象是一个autorelease对象 ,在@autoreleasepool {} 释放时,会向自动释放池中的所有autorelease对象发送release消息,也就是延迟释放,由自动释放池来管理对象什么时候被释放。 所以你上面的代码,在ARC下应该是没有问题的。 如果上述分析有误,还请指正。
等了好久终于有人来了,其实我去cocoachina也开了个同样的帖子。 传送门 那便都没什么人,太冷清了,这里好歹有版主压阵,而且是两个。 回归正题,你的想法跟我在cocoachina那的猜想一样。只有放入了autoreleasepool才能达到延迟释放的效果。 我的问题: 1.不知有没有书详细介绍了这一点,可以让我研究一下。 2.arc下会对对象做release或者autorelease对吧(难道都是autorelease没有release)?那它是如何判断的。是因为返回了局部引用所以就加上autorelease吗? 3.arc下自动内存管理,如果我想使用一个对象,我需要担心对象内存被释放掉了,是个nil引用吗?(我的意思是写代码的时候我需不需要去考虑对象的内存,什么时候被释放了,什么都是还没被释放)
Bannings 2015-04-12
  • 打赏
  • 举报
回复
“而c是一个局部变量”这个说法不准确,c 是一个指向堆中内存的局部指针,test 的作用域结束后,c 这个指针被释放了,但是堆中的对象放到了栈顶的 autoreleasepool 里
ReyZhang 2015-04-12
  • 打赏
  • 举报
回复
autoreleasepool 管理的是自动释放池中的对象。所谓的自动释放池中的对象不是说使用@autorelesaepool {} 作用域内的对象,而是对象通过发送autorelease消息后,这个对象才被加入到自动释放池中。 所以在ARC上,编译器会在适当的位置给 c指向的对象发送release 消息。对于Car 类中的test 方法返回的又是一个 Car类的实例,这个test方法在ARC环境下,会在返回Car实例时发送一个autorelease消息。也就是这个d指向的对象是一个autorelease对象 ,在@autoreleasepool {} 释放时,会向自动释放池中的所有autorelease对象发送release消息,也就是延迟释放,由自动释放池来管理对象什么时候被释放。 所以你上面的代码,在ARC下应该是没有问题的。 如果上述分析有误,还请指正。

29,027

社区成员

发帖
与我相关
我的任务
社区描述
主要讨论与iOS相关的软件和技术
社区管理员
  • iOS
  • 大熊猫侯佩
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧