Question

My app is working perfectly on the simulator but when I test the app on the device I eventually get a random EXC_BAD_ACCESS and the app crashes.

After a few days of testing I think I have found the code that is causing this error. At some point I need to add some subviews to a controller's main view, then the user interacts with the app and those subviews are removed from their superview and new subviews are added. If I don't ever remove the subviews the app does not crash but if I remove them, the app eventually gets an EXC_BAD_ACCESS and crashes.

It seems like the subviews removed get a release msg when they already are completely released or something like that... This the first app where I'm using ARC so I'm probably missing something...

Here's the code involved:

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

Here is how the subviews are created. I'm not particularly proud of this code and it needs refactoring but I need to understand why is not working before:

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

This is working for a while and then crashes. I mean, the views are removed and added a few times and it seems like everything is OK but after a while the app gets the EXC_BAD_ACCESS.

Any help will be eternally appreciated!

PS: Sorry for my English

EDIT: I can't use Zombies on the device and I can't see the stacktrace.

This is what I get if I type "bt" on the lldb after I get the EXC_BAD_ACCESS:

 * 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  
Was it helpful?

Solution

Discussion from comments -

You're right, zombies will work only in simulator - though I suspect the crash will reproduce in simulator as well. Very few cases where app will crash on device, but not on simulator. Maybe give it another try ensuring you use simulator of the same iOS version. I couldn't find any info on _UIImageViewPretiledImageWrapper, but that will be the key to solving this.

This answer suggests _UIImageViewPretiledImageWrapper is related to -[UIImage resizableImageWithCapInsets:].

Comments from OP:

At some point I was creating a UIImageView based on a resizable UIImage but I was setting the image before setting the final size of the UIImageView. This seems to somehow end up corrupting memory due to the resizing of the UIImage

OTHER TIPS

I think as usually if app works on simulator and doesn't on the device, depends of the memory. Please check the memory warning.

Just FYI.

I also had a crash similar to this and the fix was actually not in the code, but in the asset itself.

As Apple Documentation states the resizable area of the asset needs to be 1px by 1px, so ensure that it is.

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets

During scaling or resizing of the image, areas covered by a cap are not scaled or resized. Instead, the pixel area not covered by the cap in each direction is tiled, left-to-right and top-to-bottom, to resize the image. This technique is often used to create variable-width buttons, which retain the same rounded corners but whose center region grows or shrinks as needed. For best performance, use a tiled area that is a 1x1 pixel area in size.

Move WordView *wordXXX to interface declaration to make it instance variable.

This looks issue with memory releasing. declare the

WordView *wordView

in .h file. So it wll create a strong reference

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top