removeFromSuperview 导致崩溃(非 ARC)
removeFromSuperview causes crash (non-ARC)
我在 UIView 和手动内存管理方面遇到了一个奇怪的问题。
我有一个视图 (contentView),它是视图控制器的主视图。
长按 contentView 后,另一个视图应该淡入(在其顶部)。
手势结束时,附加视图淡出。
问题是:
当contentView收到长按时,我创建辅助视图,将其添加到contentView,然后释放它, is/was 这是 ARC 出现之前的常见做法。
在 iPhone 上运行正常,但在 iPad 上崩溃!
崩溃线是:
[ZPNowPlayingItemInfoView dealloc]
...当我从 contentView 移除辅助视图时触发。
关于为什么会发生这种情况的任何线索?
如果我注释掉 release 行(请参阅我在代码中的注释),它在两种设备上都可以完美运行,但感觉很糟糕.
代码如下:
-(void)longPressDetected:(UILongPressGestureRecognizer*)longPressGR
{
//Content view of the view controller I'm in
UIView *contentView = MSHookIvar<UIView*>(self, "_contentView");
if (longPressGR.state == UIGestureRecognizerStateBegan) {
id item = MSHookIvar<MPAVItem*>(self, "_item");
ZPNowPlayingItemInfoView *infoView =
[[ZPNowPlayingItemInfoView alloc] initWithFrame:
CGRectMake(0,0,contentView.frame.size.width,contentView.frame.size.height)
item:item];
//infoView retain count: 1
[infoView setAlpha:0.f];
[contentView addSubview:infoView];
//infoView retain count: 3 (???)
//iPad goes berserk on this line
//Commented - Works both on iPhone and iPad
//Uncommented - Works only on iPhone
//[infoView release];
//infoView retain count: 2 (if release is uncommented)
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:1.0f];
} completion:^(BOOL finished) {
//infoView retain count: 3
}];
} else if (longPressGR.state == UIGestureRecognizerStateEnded) {
ZPNowPlayingItemInfoView* infoView = nil;
for (UIView *subview in contentView.subviews) {
if ([subview isKindOfClass:[ZPNowPlayingItemInfoView class]]) {
infoView = (ZPNowPlayingItemInfoView*)subview;
break;
}
}
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:0.f];
} completion: ^(BOOL finished){
[infoView removeFromSuperview];
}];
}
P.S。我需要使用手动内存管理。这是对越狱设备的调整。
堆栈跟踪:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x195287bdc 0x19526c000 + 0x1bbdc // objc_msgSend + 0x1c
1 + Musix.dylib 0x10015b19c 0x100154000 + 0x719c // -[ZPNowPlayingItemInfoView dealloc] + 0x48
2 libsystem_blocks.dylib 0x19590d90c 0x19590c000 + 0x190c // _Block_release + 0xfc
3 UIKit 0x188ef8590 0x188eb0000 + 0x48590 // -[UIViewAnimationBlockDelegate dealloc] + 0x44
4 CoreFoundation 0x1845f1374 0x1845ec000 + 0x5374 // CFRelease + 0x208
5 CoreFoundation 0x184601004 0x1845ec000 + 0x15004 // -[__NSDictionaryI dealloc] + 0x8c
6 libobjc.A.dylib 0x19528d720 0x19526c000 + 0x21720 // (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 0x230
7 CoreFoundation 0x1845f4f90 0x1845ec000 + 0x8f90 // _CFAutoreleasePoolPop + 0x18
8 CoreFoundation 0x1846c774c 0x1845ec000 + 0xdb74c // __CFRunLoopRun + 0x5d8
9 CoreFoundation 0x1845f51f0 0x1845ec000 + 0x91f0 // CFRunLoopRunSpecific + 0x188
10 GraphicsServices 0x18d7575a0 0x18d74c000 + 0xb5a0 // GSEventRunModal + 0xa4
11 UIKit 0x188f26780 0x188eb0000 + 0x76780 // UIApplicationMain + 0x5cc
12 Music (*) 0x10006ee28 0x100064000 + 0xae28 // 0x0000adac + 0x7c
13 libdyld.dylib 0x1958e2a04 0x1958e0000 + 0x2a04 // start + 0x0
ZPNowPlayingItemInfoView:
@interface ZPNowPlayingItemInfoView()
@property (nonatomic, retain) MPAVItem* item;
@property (nonatomic, retain) MPUSlantedTextPlaceholderArtworkView *artworkView;
@property (nonatomic, retain) UILabel *artistLabel;
@property (nonatomic, retain) UILabel *albumLabel;
@property (nonatomic, retain) UILabel *songLabel;
@end
ZPNowPlayingItemInfoView dealloc:
-(void)dealloc
{
[super dealloc];
[self.item release];
[self.artworkView release];
[self.artistLabel release];
[self.songLabel release];
}
注释掉 release
是一种可行的解决方法,这表明您发布它的频率太高了。它很可能就是您注释掉的那个版本。
removeFromSuperview
确实将保留计数减少了 1。
我建议重新访问视图对象的完整生命周期。但这可能很棘手。每个 retain 都需要恰好有一个对应的 release 或 autorelease。使用 getter (self.myView = subview
) 将视图分配给 属性 确实会保留它,并将另一个视图重新分配给 属性 (self.myView = someOhterview
) 会释放 subview
。
相反,直接访问 iVar (myView = subview
) 不会维护 release/retain-cycle。
不仅如此。添加视图并将其从数组、集合或字典中删除将相应地更改保留计数。
所以去深入了解一下。使用仪器观察保留计数。
您在 ZPNowPlayingItemInfoView
class 中遇到了一些问题。这个问题什么时候出现?仅当对象被释放时。当您注释掉 [infoView release]
时,您的对象永远不会被释放并且问题不会出现 - 但是您会发生内存泄漏。
检查 ZPNowPlayingItemInfoView
做了什么,尤其是它的 dealloc
方法。你确定你正在正确构建它吗? item
总是一个有效的对象吗?
看到ZPNowPlayingItemInfoView dealloc
方法后,问题就很清楚了——[super dealloc]
一定是最后一次调用,而不是第一次调用。释放对象后,访问其属性是未定义的操作。
我在 UIView 和手动内存管理方面遇到了一个奇怪的问题。
我有一个视图 (contentView),它是视图控制器的主视图。
长按 contentView 后,另一个视图应该淡入(在其顶部)。
手势结束时,附加视图淡出。
问题是:
当contentView收到长按时,我创建辅助视图,将其添加到contentView,然后释放它, is/was 这是 ARC 出现之前的常见做法。
在 iPhone 上运行正常,但在 iPad 上崩溃!
崩溃线是:
[ZPNowPlayingItemInfoView dealloc]
...当我从 contentView 移除辅助视图时触发。
关于为什么会发生这种情况的任何线索?
如果我注释掉 release 行(请参阅我在代码中的注释),它在两种设备上都可以完美运行,但感觉很糟糕.
代码如下:
-(void)longPressDetected:(UILongPressGestureRecognizer*)longPressGR
{
//Content view of the view controller I'm in
UIView *contentView = MSHookIvar<UIView*>(self, "_contentView");
if (longPressGR.state == UIGestureRecognizerStateBegan) {
id item = MSHookIvar<MPAVItem*>(self, "_item");
ZPNowPlayingItemInfoView *infoView =
[[ZPNowPlayingItemInfoView alloc] initWithFrame:
CGRectMake(0,0,contentView.frame.size.width,contentView.frame.size.height)
item:item];
//infoView retain count: 1
[infoView setAlpha:0.f];
[contentView addSubview:infoView];
//infoView retain count: 3 (???)
//iPad goes berserk on this line
//Commented - Works both on iPhone and iPad
//Uncommented - Works only on iPhone
//[infoView release];
//infoView retain count: 2 (if release is uncommented)
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:1.0f];
} completion:^(BOOL finished) {
//infoView retain count: 3
}];
} else if (longPressGR.state == UIGestureRecognizerStateEnded) {
ZPNowPlayingItemInfoView* infoView = nil;
for (UIView *subview in contentView.subviews) {
if ([subview isKindOfClass:[ZPNowPlayingItemInfoView class]]) {
infoView = (ZPNowPlayingItemInfoView*)subview;
break;
}
}
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:0.f];
} completion: ^(BOOL finished){
[infoView removeFromSuperview];
}];
}
P.S。我需要使用手动内存管理。这是对越狱设备的调整。
堆栈跟踪:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x195287bdc 0x19526c000 + 0x1bbdc // objc_msgSend + 0x1c
1 + Musix.dylib 0x10015b19c 0x100154000 + 0x719c // -[ZPNowPlayingItemInfoView dealloc] + 0x48
2 libsystem_blocks.dylib 0x19590d90c 0x19590c000 + 0x190c // _Block_release + 0xfc
3 UIKit 0x188ef8590 0x188eb0000 + 0x48590 // -[UIViewAnimationBlockDelegate dealloc] + 0x44
4 CoreFoundation 0x1845f1374 0x1845ec000 + 0x5374 // CFRelease + 0x208
5 CoreFoundation 0x184601004 0x1845ec000 + 0x15004 // -[__NSDictionaryI dealloc] + 0x8c
6 libobjc.A.dylib 0x19528d720 0x19526c000 + 0x21720 // (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 0x230
7 CoreFoundation 0x1845f4f90 0x1845ec000 + 0x8f90 // _CFAutoreleasePoolPop + 0x18
8 CoreFoundation 0x1846c774c 0x1845ec000 + 0xdb74c // __CFRunLoopRun + 0x5d8
9 CoreFoundation 0x1845f51f0 0x1845ec000 + 0x91f0 // CFRunLoopRunSpecific + 0x188
10 GraphicsServices 0x18d7575a0 0x18d74c000 + 0xb5a0 // GSEventRunModal + 0xa4
11 UIKit 0x188f26780 0x188eb0000 + 0x76780 // UIApplicationMain + 0x5cc
12 Music (*) 0x10006ee28 0x100064000 + 0xae28 // 0x0000adac + 0x7c
13 libdyld.dylib 0x1958e2a04 0x1958e0000 + 0x2a04 // start + 0x0
ZPNowPlayingItemInfoView:
@interface ZPNowPlayingItemInfoView()
@property (nonatomic, retain) MPAVItem* item;
@property (nonatomic, retain) MPUSlantedTextPlaceholderArtworkView *artworkView;
@property (nonatomic, retain) UILabel *artistLabel;
@property (nonatomic, retain) UILabel *albumLabel;
@property (nonatomic, retain) UILabel *songLabel;
@end
ZPNowPlayingItemInfoView dealloc:
-(void)dealloc
{
[super dealloc];
[self.item release];
[self.artworkView release];
[self.artistLabel release];
[self.songLabel release];
}
注释掉 release
是一种可行的解决方法,这表明您发布它的频率太高了。它很可能就是您注释掉的那个版本。
removeFromSuperview
确实将保留计数减少了 1。
我建议重新访问视图对象的完整生命周期。但这可能很棘手。每个 retain 都需要恰好有一个对应的 release 或 autorelease。使用 getter (self.myView = subview
) 将视图分配给 属性 确实会保留它,并将另一个视图重新分配给 属性 (self.myView = someOhterview
) 会释放 subview
。
相反,直接访问 iVar (myView = subview
) 不会维护 release/retain-cycle。
不仅如此。添加视图并将其从数组、集合或字典中删除将相应地更改保留计数。
所以去深入了解一下。使用仪器观察保留计数。
您在 ZPNowPlayingItemInfoView
class 中遇到了一些问题。这个问题什么时候出现?仅当对象被释放时。当您注释掉 [infoView release]
时,您的对象永远不会被释放并且问题不会出现 - 但是您会发生内存泄漏。
检查 ZPNowPlayingItemInfoView
做了什么,尤其是它的 dealloc
方法。你确定你正在正确构建它吗? item
总是一个有效的对象吗?
看到ZPNowPlayingItemInfoView dealloc
方法后,问题就很清楚了——[super dealloc]
一定是最后一次调用,而不是第一次调用。释放对象后,访问其属性是未定义的操作。