【问题标题】:removeFromSuperview causes crash (non-ARC)removeFromSuperview 导致崩溃(非 ARC)
【发布时间】:2015-02-08 20:50:43
【问题描述】:

我在 UIViews 和手动内存管理方面遇到了一个奇怪的问题。

我有一个视图 (contentView),它是视图控制器的主视图。

在长按 contentView 后,另一个视图应该会淡入(在它上面)。

当手势结束时,附加视图会淡出。

问题是:

contentView收到长按时,我创建辅助视图,添加到contentView中,然后释放,这是/曾经的常见做法在 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];

    }];

 }

附:我需要使用手动内存管理。这是针对越狱设备的调整。

堆栈跟踪:

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];
}

【问题讨论】:

  • 嗯....感觉好像很久没有 ARC 来了。在我看来,[infoView release]; 应该不加注释。只看代码,我担心[infoView setAlpha:0.f]; 后面跟着[contentView addSubview:infoView];。我一直对alpha0 感到奇怪——例如,如果您使用0.1 的初始alpha 会发生什么。我想知道添加subviewalpha0 是否实际上增加了保留计数?
  • @RoboticCat 我试过更改 alpha。它仍然崩溃:(。我已将堆栈跟踪添加到问题中。
  • 我认为你将不得不切换到 IB 并使用旧方法来跟踪内存问题,使用 NSZombies:developer.apple.com/library/ios/documentation/DeveloperTools/…
  • 忽略 retainCount - 没用:stackoverflow.com/q/1206111/558933。基本上规则是每个[retain] 必须跟[release]。实际上,现在我想起来了,你从来没有retaininfoView(在你alloc/init视图之后)所以发布是不必要的。哎呀。我认为它是必要的。我认为没有release 就可以了。
  • UIViewAnimationBlockDelegate dealloc - CAAnimation 委托被动画保留。所以这就是问题的根源。

标签: ios objective-c memory-management


【解决方案1】:

你在ZPNowPlayingItemInfoView 课堂上遇到了一些问题。什么时候出现这个问题?仅当对象被释放时。当您将 [infoView release] 注释掉时,您的对象永远不会被释放,问题也不会出现 - 但您会遇到内存泄漏。

检查ZPNowPlayingItemInfoView 做了什么,尤其是它的dealloc 方法。你确定你构造正确吗? item 是否始终是有效对象?

看到ZPNowPlayingItemInfoView dealloc方法后,问题就很清楚了——[super dealloc]一定是最后一个调用,而不是第一个。释放对象后,访问其属性是一个未定义的操作。

【讨论】:

  • 我在问题中添加了接口和dealloc方法
  • 这是正确答案!在最后一个操作中发送消息 dealloc 可以解决问题。非常感谢!
【解决方案2】:

当注释掉 release 是一种可行的解决方法时,这表明您已经过于频繁地发布它。这很可能是您注释掉的唯一一个版本。

removeFromSuperview 确实将保留计数减少了 1。

我建议重新访问视图对象的完整生命周期。不过,这可能很棘手。每个保留都需要有一个对应的释放或自动释放。使用 getter (self.myView = subview) 将视图分配给属性会保留它并重新分配另一个视图给属性 (self.myView = someOhterview) 发布 subview。 相反,直接访问 iVar (myView = subview) 不会维护发布/保留周期。 不止于此。添加视图并将其从数组、集合或字典中删除将相应地更改保留计数。

所以去深入了解一下吧。使用仪器观察保留计数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-23
    • 2020-12-02
    • 2013-11-11
    相关资源
    最近更新 更多