0

I am having an app in which I am passing some data between viewControllers with NSUserDefaults.

This is the code below I am using.

[[NSUserDefaults standardUserDefaults]setObject:selectedLists forKey:UserID];

Where selectedLists is NSMutableArray and UserID is NSString.

This code works fine until ios7 and xCode5.

Since I have updated xCode6 and now I am using ios8, my app works fine with xCode 6 and ios 7.1 but the app crashes when I run the app in ios8 devices with the error below.

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector sent to instance 0x7a027060'

I just don't understand what is the problem here.

If anyone has faced this problem with ios8 then please help me.

Thanks in advance.

Value of selectedLists

    {
        CardFace = Word;
        ImportFlag = Import;
        ListID = 1;
        ListName = "Adjectives - Appearance";
        QuizBy = Definition;
        UserID = 1;
    }

and UserId is 1.

EDITED

 *** First throw call stack:
    (
        0   CoreFoundation                      0x06a24df6 __exceptionPreprocess + 182
        1   libobjc.A.dylib                     0x03ac8a97 objc_exception_throw + 44
        2   CoreFoundation                      0x06a2ca75 -[NSObject(NSObject) doesNotRecognizeSelector:] + 277
        3   CoreFoundation                      0x069759c7 ___forwarding___ + 1047
        4   CoreFoundation                      0x0697558e _CF_forwarding_prep_0 + 14
        5   CoreFoundation                      0x068df30f CFStringGetLength + 143
        6   CoreFoundation                      0x069de510 _CFPrefsEncodeKeyValuePairIntoMessage + 64
        7   CoreFoundation                      0x06a275cf -[CFPrefsPlistSource sendMessageSettingValue:forKey:] + 111
        8   CoreFoundation                      0x06954f3a -[CFPrefsPlistSource alreadylocked_setValue:forKey:] + 250
        9   CoreFoundation                      0x06954e22 -[CFPrefsSource setValue:forKey:] + 82
        10  CoreFoundation                      0x06954dc3 ___CFPreferencesSetValueWithContainer_block_invoke + 51
        11  CoreFoundation                      0x0690ef0a +[CFPrefsSource withSourceForIdentifier:user:byHost:container:perform:] + 1274
        12  CoreFoundation                      0x06954d61 _CFPreferencesSetValueWithContainer + 225
        13  CoreFoundation                      0x06a0aa62 _CFPreferencesSetAppValueWithContainer + 66
        14  Foundation                          0x0347f53f -[NSUserDefaults(NSUserDefaults) setObject:forKey:] + 59
        15  Foundation                          0x0353eeba -[NSUserDefaults(NSKeyValueCoding) setValue:forKey:] + 68
        16  Vocab                               0x000eaecc Vocab + 720588
        17  libobjc.A.dylib                     0x03ade7cd -[NSObject performSelector:withObject:withObject:] + 84
        18  UIKit                               0x022a079d -[UIApplication sendAction:to:from:forEvent:] + 99
        19  UIKit                               0x022a072f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
        20  UIKit                               0x023d3a16 -[UIControl sendAction:to:forEvent:] + 69
        21  UIKit                               0x023d3e33 -[UIControl _sendActionsForEvents:withEvent:] + 598
        22  UIKit                               0x023d309d -[UIControl touchesEnded:withEvent:] + 660
        23  UIKit                               0x022f0aba -[UIWindow _sendTouchesForEvent:] + 874
        24  UIKit                               0x022f1595 -[UIWindow sendEvent:] + 791
        25  UIKit                               0x022b6aa9 -[UIApplication sendEvent:] + 242
        26  UIKit                               0x022c68de _UIApplicationHandleEventFromQueueEvent + 20690
        27  UIKit                               0x0229b079 _UIApplicationHandleEventQueue + 2206
        28  CoreFoundation                      0x069487bf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
        29  CoreFoundation                      0x0693e2cd __CFRunLoopDoSources0 + 253
        30  CoreFoundation                      0x0693d828 __CFRunLoopRun + 952
        31  CoreFoundation                      0x0693d1ab CFRunLoopRunSpecific + 443
        32  CoreFoundation                      0x0693cfdb CFRunLoopRunInMode + 123
        33  GraphicsServices                    0x0436424f GSEventRunModal + 192
        34  GraphicsServices                    0x0436408c GSEventRun + 104
        35  UIKit                               0x0229ee16 UIApplicationMain + 1526
        36  Vocab                               0x0004c9bc Vocab + 72124
        37  libdyld.dylib                       0x03ee6ac9 start + 1
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException

Edited On my tableview's didselect method...

UserID = [[users objectAtIndex:indexPath.row] valueForKey:@"UserID"];

where users is an array.

Manthan
  • 3,856
  • 1
  • 27
  • 58
  • What kind of object type are selectedLists and UserID? – E-Riddie Oct 09 '14 at 07:45
  • I suggest you set an exception breakpoint or check the stack trace to confirm which line it is actually crashing on – Paulw11 Oct 09 '14 at 07:46
  • 1
    Also, NSUserDefaults is probably not the most efficient way to pass data between objects anyway - `NSNotificationCenter` is a better choice – Paulw11 Oct 09 '14 at 08:00

5 Answers5

1

You should check the UserID type because crash is not related with changing NSArray to NSMutableArray.

You must be confusing NSNumber with NSString on your userID:

Change your code and instead of userID use the variable userIDString

NSString userIDString;

//If UserID is class of NSNumber turn its value to NSString
if ([UserID isKindOfClass:[NSNumber class]]) 
{ 
    userIDString = [UserID stringValue]; 
} 
else 
    userIDString = UserID ;
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • @Manthan you can't store custom objects's NSMutableArray to userDefaults. You need to represent it as NSData. Ill edit my answer. – E-Riddie Oct 09 '14 at 09:24
  • Ok thanks... but it works for ios6 and ios7 then why this problem comes only for ios8? – Manthan Oct 09 '14 at 09:27
  • I don't know how you really passed these custom objects to NSUserDefaults, but this is a well-known issue that you can't pass the objects to NSUserDefaults. If you have NSDictionaries on you array than the logic changes. – E-Riddie Oct 09 '14 at 09:35
  • Actually i got an existing project and i am making some modifications so i am not aware some changes made by other developer. So sorry for that. – Manthan Oct 09 '14 at 09:39
  • When i write this [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:selectedLists] forKey:UserID]; the app still crashes. – Manthan Oct 09 '14 at 09:42
  • You need to add the to your custom class. For example @interface CustomObject : NSObject – E-Riddie Oct 09 '14 at 09:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62737/discussion-between-eridb-and-manthan). – E-Riddie Oct 09 '14 at 09:44
  • :Yeah there was a problem with database. I solved my problem. Thanks for your help. – Manthan Oct 10 '14 at 06:32
1

During runtime UserId is probably of type NSNumber, even if in your code it is typed NSString.

Check how UserId is created. Use the debugger to check the runtime type.

The newly posted code shows that your retrieve the UserId from the users array which probably consists of dictionaries. In those dictionaries there's a value for the key UserID. It depends on how the array of dictionaries is created (json deserialization?) but it seems that the type of object referred to by the UserID key is a number type, not a string. So all you have to do is convert this number into a string:

UserID = [[[users objectAtIndex:indexPath.row] valueForKey:@"UserID"] stringValue];

Now your key is always a string.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • yes user id is 1 so it takes it is nsnumber but takes it as nsstring – Manthan Oct 09 '14 at 10:15
  • @Manthan The key can't be an `NSNumber`, it must be an `NSString`. Again, check how the key is created and convert it to a string, if necessary. – Nikolai Ruhe Oct 09 '14 at 10:17
  • Ok i'll try and let you know. – Manthan Oct 09 '14 at 10:23
  • I have taken userid as nsstring but it returns nsnumber then what should i do? – Manthan Oct 09 '14 at 11:06
  • The bug is in code you did not post yet. As I said, check the code that creates the `UserId` object. Where do you get it from? Add this information/code to your question or we won't be able to post a solution. – Nikolai Ruhe Oct 09 '14 at 12:12
  • Try and replace this `UserID = [[users objectAtIndex:indexPath.row] valueForKey:@"UserID"];` with `UserID = [NSString stringWithFormat:[[users objectAtIndex:indexPath.row] valueForKey:@"UserID"]intValue];` If it doesn't work, user integerValue (or any other autocompletion that is integer related) UserID should now be a string. – Gil Sand Oct 09 '14 at 13:07
  • @NikolaiRuhe: Thanks. I solved this with your help. +1 – Manthan Oct 10 '14 at 06:33
0

I think can't store NSMutableArrays in NSUserDefaults since iOS8 (according to different posts). Try casting it into an array before (or create a new one).

Like this for example :

NSArray *arr = [NSArray arrayWithArray:selectedLists];

And store arr in NSUserDefaults.

Bhumit Mehta
  • 16,278
  • 11
  • 50
  • 64
Gil Sand
  • 5,802
  • 5
  • 36
  • 78
  • You can store NSMutableArray to NSUserDefaults but when you get it, the array is not a mutable object, so you have to create new one. – E-Riddie Oct 09 '14 at 07:50
  • I've had the exact same crash in an app and using NSArray fixed my problem – Gil Sand Oct 09 '14 at 07:50
  • http://stackoverflow.com/questions/26029704/nsuserdefaults-pause-crash-on-ios8 Apparently this is new to iOS8, maybe? – Gil Sand Oct 09 '14 at 07:52
  • Same issue here when IOS 8 released (NSMutable > NSUserDefaults) and replace mymutable by a non mutable array solve my problem... – Smiless Oct 09 '14 at 07:52
  • I just ran a test where I added an NSMutableArray to NSUserDefaults and retrieved it as an array - no problems – Paulw11 Oct 09 '14 at 07:58
  • Well i can't say more than what i've said before, i've had an app that stopped crashing by just changing the mutablearray to a non-mutable one, and apparently i'm not the only one. But maybe this is just fixing another problem that isn't the array type but something else. I hear you, this is strange/weird though. – Gil Sand Oct 09 '14 at 08:00
  • I tried it too in Xcode 6.1 GM, works perfectly by adding NSMutableArray. – E-Riddie Oct 09 '14 at 08:06
  • My array had only strings - it seems more likely it is related to the incorrect storage of custom objects – Paulw11 Oct 09 '14 at 08:08
  • @Zil: Thanks for your help too... +1 for you to. – Manthan Oct 10 '14 at 06:43
0

Try to encode your data in NSData and save it in NSUserDefaults and retrieved back, once needed. Have a look at this thread and this one.

Community
  • 1
  • 1
NeverHopeless
  • 11,077
  • 4
  • 35
  • 56
0

Should not use NSUSerdefault to keep a collection of custom objects. You should save them using NSkeyedUnarchiver/NSkeyedarchiver. Ref: NSCoding.

HaiN
  • 917
  • 11
  • 31