0

The project is for iOS 5.0. I have set the automatic reference counting, anyhow I am not able to compile the code if I write a retain statement.

I wrote a switch in a function, because I don't want to create if and a lot of if else branches.

My code portion it is relative simple and seems correct, but only seems to be:

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];

switch (selectedRow) {
        
    case 0://English
        [userDefaults setObject:@"English" forKey:@"language"];
        break;
    case 1:// Deutsch 
        [userDefaults setObject:@"German" forKey:@"language"];   
        break;
    case 2://Français
        [userDefaults setObject:@"French" forKey:@"language"];         
        break;
    case 3://Italiano
        [userDefaults setObject:@"Italian" forKey:@"language"];       
        break;
    case 4://Español
        [userDefaults setObject:@"Spanish" forKey:@"language"];
        break;
        
    default:
        break;
}
// flush:
[userDefaults synchronize];

at runtime I got this message: *** -[MyClass retain]: message sent to deallocated instance 0x6e78580 and it will shown the XCode as the breakline at some case branch's [userDefaults setObject line.

Somewhere I saw a compile when compile the Switch it will create a class. But I am not sure in which language: Java, C#, or Obj-C and because I left my class and I am executing the switch class it will dealloc the userDefaults variable and that is the reason why is deallocated the userDefaults object. Now I am not sure how to write this switch to get working and looks like professional. I wouldn't like to create in each case the userDefaults variable and flush there. The only one solution is to write this switch to if-else?

this is working:

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
    
    switch (selectedRow) {
            
        case 0://English
        {
            [userDefaults setObject:@"English" forKey:@"language"];
        }
            break;
        case 1:// Deutsch 
        {
            [userDefaults setObject:@"German" forKey:@"language"];   
        }
            break;
        case 2://Français
        {
            [userDefaults setObject:@"French" forKey:@"language"];         
        }
            break;
        case 3://Italiano
        {
            [userDefaults setObject:@"Italian" forKey:@"language"];       
        }
            break;
        case 4://Español
        {
            [userDefaults setObject:@"Spanish" forKey:@"language"];
        }
            break;
            
        default:
            break;
    }
    // flush:
    [userDefaults synchronize];

why?

  • not sure why It does worked, but than it crashed again. I have moved the variable inside the code:

    switch (selectedRow) {

          case 0://English
          {
              NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
              [userDefaults setObject:@"English" forKey:@"language"];
              // flush:
              [userDefaults synchronize];
          }
              break;
          case 1:// Deutsch 
          {
              NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
              [userDefaults setObject:@"German" forKey:@"language"]; 
              // flush:
              [userDefaults synchronize];  
          }
              break;
          case 2://Français
          {
              NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
              [userDefaults setObject:@"French" forKey:@"language"]; 
              // flush:
              [userDefaults synchronize];        
          }
              break;
          case 3://Italiano
          {
              NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
              [userDefaults setObject:@"Italian" forKey:@"language"];   
              // flush:
              [userDefaults synchronize];    
          }
              break;
          case 4://Español
          {
              NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
              [userDefaults setObject:@"Spanish" forKey:@"language"];
              // flush:
              [userDefaults synchronize];
          }
              break;
    
          default:
              break;
      }
    

This crashed again and I came here to check the answers

I saw the suggestion below:

NSArray * languages = [NSArray arrayWithObjects: @"English", @"German", @"French",@"Italian", @"Spanish", nil];
    NSString * selectedLanguage = [languages objectAtIndex: selectedRow];
    [[NSUserDefaults standardUserDefaults] setObject: selectedLanguage forKey:@"language"];

this crash at the same line:

[[NSUserDefaults standardUserDefaults] setObject: selectedLanguage forKey:@"language"];
  • what is wrong? can't believe it. *** -[MyClass retain]: message sent to deallocated instance 0x6b532c0

the MyClass is a UIViewContoller. I do a language selection and press some buttons to navigate 1 or 2 screen and I am coming back and do the language selection again and it will crash, but not always. But when it crashing it always at the same line and always the same error message.

What has the

userDefaults setObject 

with navigations?

#--------------------------------------------------- Got the real problem: than easy to find the solution #---------------------------------------------------

I wrote the: userDefaults setObject Also I wrote : this is an ARC project and it fails at MyClass retain, which can't be my code .

The code is a language selection with an action sheet. If I do a search I will have the next link: EXC_BAD_ACCESS invoking a block

  • from there I see I should have a copy generated somewhere instead of a retain. The question is is why is needed the retain at setObject? Well, because it was added a change listener...

//[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"language" options:NSKeyValueObservingOptionNew context:NULL];

the code removed and the content of the callback moved after the switch code it solves the problem - not needed a retain to do a callback

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • it was worked once, and than crashed constantly, than I moved inside the case the declaration, crash again –  Apr 27 '12 at 13:38
  • record your ref count ops in instruments and wait for the error – justin Apr 27 '12 at 14:13

3 Answers3

2

This problem is not specific to Objective-C, but to C/C++ in general. You may want to have a look at this answer.

The problem with your first code is that the Objective-C compiler does some magic related to the instantiation of NSStrings which most likely causes the problem described in the question linked above. In short: you need to put braces around every case statement that instantiates a local variable and @"something" is doing exactly that.

Community
  • 1
  • 1
starbugs
  • 992
  • 1
  • 9
  • 14
1

Partial answer:

In Objective-C switch-case statements, if you declare an ivar in a case, you must surround the case with braces, as you have done in your second example (although the examples I have seen put the break statement inside the braces).

Your case statements do not declare a variable, but perhaps NSUserDefaults is effectively creating an object there, making the braces mandatory. (Edit: As starbugs notes in his answer below, you are instantiating an object when you write @"Language".)

So if your second example works, why not go with it? How does it look unprofessional? Especially considering that extra braces for case-switches are standard for Objective-C.

Wienke
  • 3,723
  • 27
  • 40
  • I don't think that extra braces for case-switches are standard for Objective-C... where does that information come from? – starbugs Apr 27 '12 at 13:12
  • I was first introduced to it by Apress's `Beginning iPhone 3 Development`, by Mark and LaMarche. It has always proven true. If I declare an object inside a switch-case statement without braces, it won't compile. – Wienke Apr 27 '12 at 13:16
  • This is true for declaring scalar variables, too, as well as objects. – Wienke Apr 27 '12 at 13:17
  • This surely is true, but it isn't specific to Objective-C. And if you don't instantiate local vars in a case statement you really shouldn't add another stack frame just because you are used to using the braces. I didn't read the book, but I really hope the authors aren't suggesting it as a style guide. – starbugs Apr 27 '12 at 13:39
  • In fact I don't add braces unless I need to declare a variable there; it's a nuisance since code completion doesn't do it for you. But I didn't see anything in the answers you link to below that suggests it is a bad thing to use the braces and "add another stack frame". Is there a serious inefficiency involved? – Wienke Apr 27 '12 at 14:38
  • I would just consider it bad practice as unnecessary stack frames are unneeded overhead. Whether you experience significant performance losses or not depends on where (and how often) you use the switch statements. – starbugs Apr 27 '12 at 14:56
1

Not an answer to your question, but how about ditching the switch and using:

NSArray * languages = [NSArray arrayWithObjects: @"English", @"German", @"French",
                                                 @"Italian", @"Spanish", nil];
NSString * selectedLanguage = [languages objectAtIndex: selectedRow];
[[NSUserDefaults standardUserDefaults] setObject: selectedLanguage forKey:@"language"];
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160