内存管理
方法removeFromSuperView: 到底如何工作? 当我想重新初始化视图时,我遇到了内存访问错误的问题,
- (id)init {
if (!(self = [super init]))
return nil;
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
NSLog(@"retainCount :%d", [_mainView retainCount]);
UIButton *reInitButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f,0.0f,90.0f,35.0f)];
[reInitButton addTarget:self action:@selector(buttonDidTapped:) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubView:_mainView];
NSLog(@"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(@"retainCount :%d", [_mainView retainCount]);
return self;
}
- (void)buttonDidTapped:(id)sender {
[_mainView removeFromSuperView]; //crash during second times press the button
NSLog(@"retainCount :%d", [_mainView retainCount]);
_mainView = [[UIView alloc] initWithFrame[[UIScreen mainScreen] bounds]];
[[self view] addSubView:_mainView];
NSLog(@"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(@"retainCount :%d", [_mainView retainCount]);
}
每次有任何保留或分配或释放关键字时,我都有 NSLog。结果很奇怪。
//init
retainCount : 1
retainCount : 2
retainCount : 1
//1st time pressed button
retainCount : 1 //remove super view didn't decrease
retainCount : 2
retainCount : 1
//2nd time pressed button
retainCount : 0 //crash. Memory bad access
奇怪的是为什么它在第一次按下时没有崩溃?
How is method removeFromSuperView: really works?
I got a problem of memory bad access when I want to reinit the view
- (id)init {
if (!(self = [super init]))
return nil;
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
NSLog(@"retainCount :%d", [_mainView retainCount]);
UIButton *reInitButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f,0.0f,90.0f,35.0f)];
[reInitButton addTarget:self action:@selector(buttonDidTapped:) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubView:_mainView];
NSLog(@"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(@"retainCount :%d", [_mainView retainCount]);
return self;
}
- (void)buttonDidTapped:(id)sender {
[_mainView removeFromSuperView]; //crash during second times press the button
NSLog(@"retainCount :%d", [_mainView retainCount]);
_mainView = [[UIView alloc] initWithFrame[[UIScreen mainScreen] bounds]];
[[self view] addSubView:_mainView];
NSLog(@"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(@"retainCount :%d", [_mainView retainCount]);
}
I have NSLog every times there are any retain or alloc or release keyword. And the result is very weird.
//init
retainCount : 1
retainCount : 2
retainCount : 1
//1st time pressed button
retainCount : 1 //remove super view didn't decrease
retainCount : 2
retainCount : 1
//2nd time pressed button
retainCount : 0 //crash. Memory bad access
The weird thing is why it didn't crash on 1st time pressed??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为你的问题就在这里:
你已经删除了对
_mainView
的引用,但是,根据我的阅读,这是一个你将保留并继续调用方法的成员变量。那是无效的。一旦你调用了-release
,你实际上就告诉系统你不会再使用该对象,并且你不能用一个对象做任何有用的事情。指向该对象的过时指针,就像稍后调用-removeFromSuperView
时所做的那样。如果您想继续保留
_mainView
并在其上调用代码,则需要保留引用。也许您应该将释放移至对象的-dealloc
方法。或者,您可以在按钮方法中-release
它,并在下次需要时重新创建一个新视图。作为一个有用的提示,许多程序员喜欢在释放对象后将其重置为 NULL(或 objC 语言中的
nil
),以提醒您不能使用又是那个物体。如果你-release
某些东西,你最好是认真的。最后,我建议你谷歌搜索“引用计数”一词并仔细阅读;它是比 NSObject 的细节更通用的习惯用法,考虑基础知识以及如何用另一种语言(例如 C)实现它可能会很有用。这将帮助你推理关于引用计数对象更好。
I think your problem is here:
You've dropped your reference to
_mainView
, and yet, by my reading, that's a member variable that you'll keep around and continue to invoke methods on. That's not valid. Once you've called-release
, you've essentially told the system you're not going to use that object again, and you can't do anything useful with a stale pointer to that object, like you do when you call-removeFromSuperView
on it later.If you want to continue to keep
_mainView
around and call code on it, you need to keep a reference. Perhaps you should move the release to your object's-dealloc
method. Alternatively you could-release
it in the button method and re-create a new view the next time you need to.As a helpful tip, a lot of programmers like to reset objects to
NULL
(ornil
in objC-speak) after releasing them, as a reminder that you can't use that object again. If you-release
something, you'd better mean it.Lastly, I suggest you Google the term "reference counting" and read up on it; it's a more generic idiom than the specifics of
NSObject
, and it is likely to be useful to think of the basics and how you might implement this in another language, like say, C. This will help you reason better about reference counted objects.切勿使用 RETAINCOUNT。很抱歉将其大写,但我一生都无法弄清楚为什么人们仍然使用它。这是内存管理的错误参考。使用仪器或类似工具代替。
NEVER USE RETAINCOUNT. Sorry for putting that in caps, but I can't figure out for the life of me why people still use it. It's a faulty reference for memory management. Use instruments or similar instead.
此时您不应该访问
_mainView
。这可能很难解释,所以请耐心听我说。我们将计算,但不是绝对保留计数,只是代码对对象的声明。您为一个对象分配内存并使用
_mainView
指向它:您对该对象有 1 次所有权声明。当您将其添加为另一个视图的子视图时,该视图同样会声明所有权,但这不是您的,而是视图的。事实上,它使
_mainView
中的对象保留下来是一个意外,您不应该依赖它。然后释放该对象:您已经放弃了所有权声明 - 您现在拥有 0 项声明,并且您不应再尝试访问该对象。你不拥有它。同样,由于另一个视图正在使用它而导致它仍然存在的事实,以及您仍然有指向它的指针的事实,都是意外*,您不应该依赖它们。
当需要处理按钮按下时,您正在访问一个您没有所有权的对象:
这会导致崩溃,这可能是意料之外的,但也并非不合理。通过让你的所有权声明变为 0,你告诉系统“我不再需要这个对象。在此之后我不会访问它。如果它消失,我将不会受到影响。”但事实上,您确实需要它保留在身边,并且您确实需要访问它。
那么,您应该做的是将行:移动
到按钮操作内部,紧接在调用
removeFromSuperview
之后。*在这种情况下,可以通过在释放它后设置
_mainView = nil;
来避免第二个问题,但这并不能解决更大的问题。You shouldn't be accessing
_mainView
at that point. This may be hard to explain, so bear with me. We're going to count, but not absolute retain count, just your code's claims on the object.You allocate memory for an object and point at it with
_mainView
:You have 1 claim of ownership to that object. When you add it as the subview of another view, that view likewise makes a claim of ownership, but that's not yours, it's the view's. The fact that it makes the object in
_mainView
stick around is an accident, and you shouldn't rely on it. Then you release the object:You have relinquished your ownership claim -- you now have 0 claims, and you should no longer try to access this object. You don't own it. Again, the fact that it still exists because another view is using it, and the fact that you still have a pointer to it, are accidents*, and you should not rely on them.
When it comes time to handle your button press, then, you are accessing an object over which you have no ownership:
and this causes a crash, which may not be expected, but it is not unreasonable. By letting your claims of ownership go to 0, you told the system "I don't need this object anymore. I'm not going to access it after this point. If it disappears, I will not be affected." In fact, though, you do need it to stay around, and you do need to access it.
What you should do, then, is move the line:
to inside the button action, right after the call to
removeFromSuperview
.*The second of which could be avoided by setting
_mainView = nil;
after you release it, in this case, but that won't solve the greater problem.