1

我的应用程序在模拟器上运行良好,但是当我在设备上测试应用程序时,我最终得到一个随机的 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  
4

5 回答 5

0

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

于 2013-05-21T08:25:09.190 回答
0

仅供参考。

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

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

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets

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

于 2013-10-25T10:16:30.457 回答
0

评论中的讨论 -

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

这个答案表明_UIImageViewPretiledImageWrapper-[UIImage resizableImageWithCapInsets:].

来自 OP 的评论:

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

于 2013-05-21T20:45:09.117 回答
-1

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

字视图 *字视图

在 .h 文件中。所以它将创建一个强大的参考

于 2013-05-21T08:56:20.313 回答
-1

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

于 2013-05-21T08:49:15.560 回答