使用ARC添加子视图时随机EXC_BAD_ACCESS

时间:2013-05-21 07:57:10

标签: ios objective-c automatic-ref-counting exc-bad-access subviews

我的应用程序在模拟器上完美运行但是当我在设备上测试应用程序时,我最终得到一个随机的EXC_BAD_ACCESS并且应用程序崩溃了。

经过几天的测试后,我想我找到了导致此错误的代码。在某些时候,我需要将一些子视图添加到控制器的主视图中,然后用户与应用程序交互,并从超级视图中删除这些子视图并添加新的子视图。如果我没有删除子视图,应用程序不会崩溃,但如果我删除它们,应用程序最终会获得EXC_BAD_ACCESS并崩溃。

似乎删除的子视图在他们已经完全发布或类似的东西时获得了一个发布消息...这是我使用ARC的第一个应用程序,所以我可能错过了一些东西......

以下是涉及的代码:

#define kWordXXX 101
#define kWordYYY 102

...

// This is called after the user interaction, it removes the
// old subviews (if they exist) and add new ones
- (void)updateWords
{    
    [self removeWords];

    if (self.game.move.wordXXX) {
        WordView *wordXXX = [self wordViewForTypeXXX];
        wordXXX.tag = kWordXXX;
        // self.wordsView is the view where the subviews are added
        [self.wordsView addSubview:wordXXX]; 
    }

    if (self.game.move.wordYYY) {
        WordView *wordYYY = [self wordViewForTypeYYY];
        wordYYY.tag = kWordYYY;
        [self.wordsView addSubview:wordYYY];
    }
}

// Remove the old words if they exist
- (void)removeWords
{    
    WordView *wordXXX = (WordView *)[self.wordsView viewWithTag:kWordXXX];
    WordView *wordYYY = (WordView *)[self.wordsView viewWithTag:kWordYYY];

    if (wordXXX) {
        [wordXXX removeFromSuperview];
    }

   if (wordYYY) {
       [wordYYY removeFromSuperview];
   }
}

以下是创建子视图的方式。我对这段代码并不特别自豪,它需要重构,但我需要理解为什么以前没有工作:

- (WordView *)wordViewWithFrame:(CGRect)frame andType:(WordType)type
{
    WordView *wordView = nil;

    if (type == SystemWord) {
        frame.origin.y += 15;
        wordView = [[SystemWordView alloc] initWithFrame:frame];
        } else if (type == StartWord) {
        wordView = [[StartWordView alloc] initWithFrame:frame];
        } else if (type == UserWord) {
        wordView = [[UserWordView alloc] initWithFrame:frame];
    } else {
        wordView = [[RivalWordView alloc] initWithFrame:frame];
    }

    return wordView;
}

- (WordView *)wordViewForTypeXXX
{
    WordType type = self.game.move.wordType;
    WordView *wordView = nil;
    CGRect wordViewFrame = CGRectMake(0,
                                      0,
                                      self.scoreView.frame.size.width,
                                      35);

    wordView = [self wordViewWithFrame:wordViewFrame andType:type];

    wordView.word = self.game.move.word;

    return wordView;
}

- (WordView *)wordViewForTypeYYY
{
    WordType type = self.game.move.wordType;
    CGFloat y = self.game.move.word ? 35 : 0;
    WordView *wordView = nil;
    CGRect wordViewFrame = CGRectMake(0,
                                      y,
                                      self.scoreView.frame.size.width,
                                      35);

    wordView = [self wordViewWithFrame:wordViewFrame andType:type];

    wordView.word = self.game.move.word;

    if (self.game.move.word && [wordView isKindOfClass:[PlayerWordView class]]) {
        ((PlayerWordView *)wordView).points = [NSNumber     numberWithInteger:self.game.move.points];
    }

    return wordView;
}

这工作了一段时间然后崩溃了。我的意思是,视图被删除并添加了几次,似乎一切正常,但一段时间后,应用程序获得了EXC_BAD_ACCESS。

任何帮助都将永远受到赞赏!

PS:对不起我的英文

编辑:我无法在设备上使用Zombies,我看不到堆栈跟踪。

这是我得到EXC_BAD_ACCESS之后在lldb上输入“bt”的结果:

 * thread #1: tid = 0x2503, 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x11d52465)  
  frame #0: 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16  
  frame #1: 0x3473f6fe Foundation`probeGC + 62  
  frame #2: 0x34745706 Foundation`-[NSConcreteMapTable removeObjectForKey:] + 34  
  frame #3: 0x360b3d5c UIKit`-[_UIImageViewPretiledImageWrapper dealloc] + 80  
  frame #4: 0x3bb75488 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 168  
  frame #5: 0x33e16440 CoreFoundation`_CFAutoreleasePoolPop + 16  
  frame #6: 0x347ea184 Foundation`__NSThreadPerformPerform + 604  
  frame #7: 0x33ea8682 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14  
  frame #8: 0x33ea7ee8 CoreFoundation`__CFRunLoopDoSources0 + 212  
  frame #9: 0x33ea6cb6 CoreFoundation`__CFRunLoopRun + 646  
  frame #10: 0x33e19ebc CoreFoundation`CFRunLoopRunSpecific + 356  
  frame #11: 0x33e19d48 CoreFoundation`CFRunLoopRunInMode + 104  
  frame #12: 0x379dd2ea GraphicsServices`GSEventRunModal + 74  
  frame #13: 0x35d2f300 UIKit`UIApplicationMain + 1120  
  frame #14: 0x0005bd40 Dr. Cuaicap`main(argc=1, argv=0x2fda8d10) + 116 at main.m:16  
  frame #15: 0x3bfafb20 libdyld.dylib`start + 4  

5 个答案:

答案 0 :(得分:0)

我认为通常如果app在模拟器上运行而不在设备上,则取决于内存。请检查内存警告。

答案 1 :(得分:0)

评论讨论 -

  

你是对的,僵尸只能在模拟器中工作 - 尽管我怀疑   崩溃也将在模拟器中重现。极少数情况下   应用程序将在设备上崩溃,但不会在模拟器上崩溃。也许再给它一个   尝试确保您使用相同iOS版本的模拟器。我不能   在_UIImageViewPretiledImageWrapper上找到任何信息,但那将是   解决这个问题的关键。

This回答建议_UIImageViewPretiledImageWrapper-[UIImage resizableImageWithCapInsets:]相关。

OP的评论:

  

在某些时候,我正在创建一个基于可调整大小的UIImageView   UIImage但我在设置最终尺寸之前设置了图像   UIImageView。这似乎以某种方式最终破坏了由于的内存   调整UIImage的大小

答案 2 :(得分:0)

仅供参考。

我也遇到了与此类似的崩溃,修复实际上不在代码中,而是在资产本身。

正如Apple文档所述,资产的可调整大小区域需要1px乘1px,因此确保它是。

  

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets

     

在缩放图像或调整图像大小时,标题所覆盖的区域不会缩放或调整大小。相反,在每个方向上没有被盖子覆盖的像素区域是平铺的,从左到右和从上到下,以调整图像的大小。此技术通常用于创建可变宽度按钮,这些按钮保留相同的圆角但其中心区域根据需要增大或缩小。为获得最佳性能,请使用大小为1x1像素区域的平铺区域。

答案 3 :(得分:-1)

将WordView * wordXXX移动到接口声明以使其成为实例变量。

答案 4 :(得分:-1)

这看起来与内存释放有关。 声明

  

WordView * wordView

在.h文件中

。所以它会创建一个强大的参考