Crash when writing array of NSMutableDictionary to NSUserDefault

We have an function to keep writing some log locally (in NSUserDefault) and then send to server in batch

After the app is released, we received some crash report like these We are not able to reproduce the case. What would be the possible cause?

Thanks.

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Last Exception Backtrace:
0   CoreFoundation                	0x18147805c __exceptionPreprocess + 220 (NSException.m:200)
1   libobjc.A.dylib               	0x199992f54 objc_exception_throw + 60 (objc-exception.mm:565)
2   CoreFoundation                	0x181581538 _CFThrowFormattedException + 116 (CFObject.m:2072)
3   CoreFoundation                	0x18140e064 -[__NSArrayM objectAtIndex:] + 220 (NSArrayM.m:142)
4   CoreFoundation                	0x1813ec404 CFArrayApplyFunction + 76 (CFArray.c:625)
5   CoreFoundation                	0x1813fbba4 __CFPropertyListIsValidAux + 536 (CFPropertyList.c:248)
6   CoreFoundation                	0x181421c5c CFPropertyListCreateDeepCopy + 100 (CFPropertyList.c:789)
7   CoreFoundation                	0x181457a34 createDeepCopyOfValueForKey + 164 (CFPrefsSource.m:752)
8   CoreFoundation                	0x18149a79c -[CFPrefsSource setValues:forKeys:count:copyValues:removeValuesForKeys:count:from:] + 332 (CFPrefsSource.m:772)
9   CoreFoundation                	0x1814c1628 -[CFPrefsSource setValue:forKey:from:] + 72 (CFPrefsSource.m:803)
10  CoreFoundation                	0x18146d968 __76-[_CFXPreferences setValue:forKey:appIdentifier:container:configurationURL:]_block_invoke + 60 (CFXPreferences.m:862)
11  CoreFoundation                	0x181436318 __108-[_CFXPreferences(SearchListAdditions) withSearchListForIdentifier:container:cloudConfigurationURL:perform:]_block_invoke + 404 (CFPrefsSearchListSource.m:1728)
12  CoreFoundation                	0x18140e338 normalizeQuintuplet + 356 (CFPrefsSearchListSource.m:74)
13  CoreFoundation                	0x1814073bc -[_CFXPreferences withSearchListForIdentifier:container:cloudConfigurationURL:perform:] + 152 (CFPrefsSearchListSource.m:1600)
14  CoreFoundation                	0x1814b8060 -[_CFXPreferences setValue:forKey:appIdentifier:container:configurationURL:] + 128 (CFXPreferences.m:858)
15  CoreFoundation                	0x18143b80c _CFPreferencesSetAppValueWithContainerAndConfiguration + 136 (CFXPreferences.m:1970)
16  Foundation                    	0x182c44754 -[NSUserDefaults(NSUserDefaults) setObject:forKey:] + 84 (NSUserDefaults.m:228)
17  myApp                         	0x10072b528 -[MyEventManager writeEventToLocal] + 104 (MyEventManager.m:80)
18  myApp                         	0x10072b490 -[MyEventManager logEvent:] + 764 (MyEventManager.m:74)

Here are some of our code:

MyEventManager

variable

@property (nonatomic, strong) NSMutableArray        *pendingArray;

init (to load previous event)

- (instancetype)init {
  self = [super init];
   
  NSArray *oldEvents = [[NSUserDefaults standardUserDefaults] objectForKey:@"events"];
  if(oldEvents == nil) {
    self.pendingArray = [[NSMutableArray alloc] init];
  } else {
    self.pendingArray = [[NSMutableArray alloc] initWithArray:oldEvents];
  }
   
  return self;
}

Log Event function

- (void)logEvent:(NSMutableDictionary *)eventDict {
    // Add extra info
    // timestamp
    NSDate *currentDate = [[TimeManager sharedManager] serverDate];
    NSTimeInterval timeStamp = [currentDate timeIntervalSince1970];
    [eventDict setValue:[NSString stringWithFormat:@"%.0f", timeStamp * 1000] forKey:@"ts"];  // "1641179036724"
    
    // Local unique id
    [eventDict setValue:[NSString stringWithFormat:@"%i_%.0f", self.idx, timeStamp * 1000] forKey:@"id"];  // "23_1641179036724"
    self.idx++;
    
    // Add to array
    [self.pendingArray addObject:eventDict];
    
    if (self.idx % 5 == 0) {
        [self writeEventToLocal];
    }
}

- (void)writeEventToLocal {
    [[NSUserDefaults standardUserDefaults] setObject:self.pendingArray forKey:@"events"];
}

This is interesting:

3   CoreFoundation                	0x18140e064 -[__NSArrayM objectAtIndex:] + 220 (NSArrayM.m:142)
4   CoreFoundation                	0x1813ec404 CFArrayApplyFunction + 76 (CFArray.c:625)

AFAICT frame 3 indicates that the array’s -objectAtIndex: method has thrown an ‘index out of bounds’ exception, but it’s not obvious how that could happen given that it’s being called by CFArrayApplyFunction.

To dig further into this I need a full crash report. Please post one. See Posting a Crash Report for advice on how to do that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for reply. I also notice about the objectAtIndex, but I was putting the whole array into NSUserDefault instead of reading / modifying an object inside the array. Not sure what happened.

Would it related to calling this too frequent?

Please find the full crash report in attachment.

Thanks.

Please find the full crash report in attachment.

Hmmm, that’s not a full crash report. The first field in the file you posted is Exception Type and I’d expect to see a bunch of fields before that (starting with Process and including critical stuff like OS Version).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Here is the full report, starting from "Incident Identifier"

Thanks again.

Here is the full report

Perfect! Thank you.

In your app, is there any chance that -logEvent: can be called from anything other than the main thread / queue?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

All callings are from main thread.

OK.

At this point I’m running out of suggestions as to how this could be your fault. The remaining possibility that I can see is a memory management bug. If you run your test suite under the standard memory debugging tools, and specifically zombies, does it turn up anything?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Oh, I should also say that the nature of your -logEvent: event method suggests that you’re off piste when it comes to user defaults. The user defaults system is intended to be used to store preferences, not as part of a logging system. iOS has dedicated logging APIs that we generally recommend. Or, if your requirements are more specific, you could use standard file system APIs for this log.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Crash when writing array of NSMutableDictionary to NSUserDefault
 
 
Q