1

I have an app that works fine in iOS 4, but there is a crash when scrolling a UITableView when the same exact code is compiled using iOS5 and XCode 4.2. The offending code is below:

    - (NSString *)getDefaultIconName {
        NSInteger value = [self.iconId characterAtIndex:0] % 4;
        NSString *returnValue = nil;

        switch (value) {
            case 0:
                returnValue = @"default_icon_1";
                break;
            case 1:          
                returnValue = @"default_icon_2";
                break;
            case 2:
                returnValue = @"default_icon_3";
                break;
            case 3:
            default:
                returnValue = @"default_icon_4";
                break;
        }

        return returnValue;
    }

This method is called from within a subclass of UITableViewCell that is created or re-used in a call to cellForRowAtIndexPath. When the table is created and the cells are shown, this call returns a correct string. When I scroll down the table, this call returns an invalid reference, which causes my attempt to retain the string in another class to crash with EXEC_BAD_ACCESS. In the debugger, I can see that the UITableViewCell exists correctly and all values are set properly except for the return value for this call, which says Invalid CFStringRef.

Oddly, if I place an NSLog statement printing out the returnValue before returning, it does not crash. The same is true if I put a check to see if returnValue isKindOfClass:[NSString class] before returning it.

A third thing I noticed is that if I compile with code optimization turned off, it also does not crash.

I want to make sure i fix this correctly in the app so that the problem does not occur again in the future.

edit: Sorry, the returnValue missing a * was a typo.

DrewJ
  • 13
  • 4

4 Answers4

2

Add a * to returnValue when you are declaring it. Right now it's not a pointer.

larsacus
  • 7,346
  • 3
  • 26
  • 23
  • Ah, yes, you should declare `returnValue` like this `NSString *returnValue = nil;` (unless this is simply a typo). – shookster Nov 07 '11 at 17:55
  • Oh, looks like someone already said this on the comment on the original question. – larsacus Nov 07 '11 at 17:57
  • Sorry, the * was a typo when posting the question. – DrewJ Nov 07 '11 at 18:35
  • Then it sounds like it is definitely your optimization settings. Try running the compiler with `-mno-thumbs` flag enabled, although i'm not entirely sure what it does specifically. That's a solution for optimization-related crashes i've seen floating around twitter. – larsacus Nov 07 '11 at 18:53
1

There's nothing obviously wrong with the code as you've pasted it, so look for problems (memory corruption, over-released objects) elsewhere.

Also, rather than a switch() statement, you could index into an array of return values:

NSInteger value = [self.iconId characterAtIndex:0] % 4;
NSString *icons[4] = {@"default_icon_1",
   @"default_icon_2",
   @"default_icon_3",
   @"default_icon_4"};

return icons[value];
David Gelhar
  • 27,873
  • 3
  • 67
  • 84
  • David, the code you provided actually does prevents the crashing. However, using an if-else to replace the switch does not. Also, just adding NSLog(returnValue); before returning also prevents the crash. Do you have any idea why that might be, or why your fix works? – DrewJ Nov 07 '11 at 18:46
  • It's only "fixing" the problem by accident -- there's really nothing wrong with what you were doing in the first place. All the things you're seeing -- problem goes away when you turn off optimization, or when you add NSLog statements -- point to some kind of memory corruption problem (for example, continuing to use an object after you've release()d it). – David Gelhar Nov 07 '11 at 18:53
  • I will see if I can reproduce this scenario in a small test environment and open a bug report with apple. Thanks. – DrewJ Nov 07 '11 at 20:13
0

It could be because in case 3 you are returning returnValue when it is nil? If so, set returnValue to your default value.

Also make sure you don't divide by zero.

ader
  • 5,403
  • 1
  • 21
  • 26
  • `case 3` falls through to the `default` case. – Jonathan Grynspan Nov 07 '11 at 17:52
  • 1
    The code definitely does not divide by 0, it's a mod 4 operation. 0 % 4 will return 0. Also, case 3 does not have a break, so it is returning the default value, not nil. – DrewJ Nov 07 '11 at 18:39
  • doh! of course, I missed the absent break. only mentioned the divisible by zero as I recall upgrading a project to ios5 I had warnings about this (when I ran analyze) that I had to fix. – ader Nov 08 '11 at 10:51
-2

switch case does not work with Objective-C objects. Use if-else instead.

Jef
  • 2,134
  • 15
  • 17
  • 1
    NSInteger is not an NSObject. It is simply a typedef for an integer type. So no problem here. – AliSoftware Nov 07 '11 at 17:49
  • Yes, `NSInteger` is simply a typecast int whose size depends on architecture. `NSNumber` is an objective-c object. http://stackoverflow.com/questions/4445173/when-to-use-nsinteger-vs-int – larsacus Nov 07 '11 at 17:49
  • My apologies - read this as 'switch case' does not work with Objective-C. – barfoon Nov 07 '11 at 17:59
  • While what I say is correct, it is redundant in this case because of course NSInteger isn't an object. I stand corrected. – Jef Nov 07 '11 at 18:03
  • I actually tried using an if-else construct instead of the switch-case and the same problem occurred. I think the problem has something to do with the way iOS 5 is optimizing the code on compile. – DrewJ Nov 07 '11 at 18:40