我的应用访问用户的日历,查找最近的会议事件并拉出参与者。它适用于我的测试iPad,但在生产中(自iOS 9起),许多用户都遇到了崩溃问题。来自Crashlytics的崩溃报告显示了在使用nil
获取参与者记录(ABRecordRef
)后测试if (record)
记录时的崩溃点。我在App启动时阅读日历,然后每隔30秒阅读一次。
以下是Crashlytics报道的内容
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x000000000000007e
0 libobjc.A.dylib objc_msgSend + 5
1 Simple Meeting Minutes MMeventManager.m line 258
-[MMeventManager checkCalendarEvents]
2 Simple Meeting Minutes MMMasterViewController.m line 315
-[MMMasterViewController checkCalendarEvents]
3 Foundation __NSFireDelayedPerform + 458
11 UIKit UIApplicationMain + 150
12 Simple Meeting Minutes main.m line 16
main
13 libdyld.dylib start + 2
这是来自Crashlytics的文本日志
Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x1c1d0dc6 objc_msgSend + 5
1 Simple Meeting Minutes 0xae457 -[MMeventManager checkCalendarEvents] (MMeventManager.m:258)
2 Simple Meeting Minutes 0xba9cf -[MMMasterViewController checkCalendarEvents] (MMMasterViewController.m:315)
3 Foundation 0x1d86e06f __NSFireDelayedPerform + 458
4 CoreFoundation 0x1cef8637 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 14
5 CoreFoundation 0x1cef8339 __CFRunLoopDoTimer + 832
6 CoreFoundation 0x1cef7d37 __CFRunLoopDoTimers + 188
7 CoreFoundation 0x1cef5dd5 __CFRunLoopRun + 780
8 CoreFoundation 0x1ce491af CFRunLoopRunSpecific + 470
9 CoreFoundation 0x1ce48fd1 CFRunLoopRunInMode + 104
10 GraphicsServices 0x1e5f3b41 GSEventRunModal + 80
11 UIKit 0x221cba53 UIApplicationMain + 150
12 Simple Meeting Minutes 0xaed2b main (main.m:16)
13 libdyld.dylib 0x1c6364eb start + 2
我的代码如下:
-(NSArray *)checkCalendarEvents{
// When app becomes active, check calendar for meeting events
NSMutableArray *calendarEvents = [[NSMutableArray alloc]init];
if (self.eventsAccessGranted && allowCalendarEvents) {
self.arrEvents = self.getEventsOfSelectedCalendar;
if (self.arrEvents.count > 0) {
// get previousEvents
NSArray *previousEvents = [self.meetingModel getPreviousEvents];
NSUInteger eventCounter = 0;
// loop through the calendar event until we get a valid event, then jump out of the loop.
// We just want the first event, other events we will capture on the next alert
for (EKEvent *thisEvent in self.arrEvents)
{
// check if we have had this event before or not
NSString *eventID = thisEvent.eventIdentifier;
MMevent *calendarEvent = [[MMevent alloc]init];
NSMutableArray *calendarAttendees = [[NSMutableArray alloc]init];
if (![previousEvents containsObject:eventID]) {
//not had this event before so proceed
eventCounter++;
NSString *minutesBy;
NSString *lastName;
NSString *firstName;
NSString *fullName;
MMattendee *calendarAttendee = [[MMattendee alloc] init];
// get the organiser details as being the Minutes taker
EKParticipant *organizer = thisEvent.organizer;
// get the meeting organiser details if there is one
if (organizer.name) {
fullName = organizer.name;
// set the minutes by to be the organiser
minutesBy = fullName;
// get name components from fullname
firstName = [[fullName componentsSeparatedByString:@" "] objectAtIndex:0];
if (fullName.length > firstName.length) {
lastName = [fullName substringFromIndex:[fullName rangeOfString:firstName].length + 1];
}else{
// use the first part of the firstName (as it maybe an email address only)
if ([firstName rangeOfString:@"@"].location > 0 ) {
NSString *emailFrontPart = [[firstName componentsSeparatedByString:@"@"] objectAtIndex:0];
// now look for a dot sepatator in email string
// NSLog(@"%d", [emailFrontPart rangeOfString:@"."].location);
if ([emailFrontPart rangeOfString:@"."].location == NSNotFound) {
// no dot separator
firstName = emailFrontPart;
lastName = @" ";
}
else{
// get firstname as a firstpart
firstName = [[emailFrontPart componentsSeparatedByString:@"."] objectAtIndex:0];
// get lastname as the second part
if (lastName.length == 0) {
lastName = [[emailFrontPart componentsSeparatedByString:@"."] objectAtIndex:1];
}
}
}
}
calendarAttendee.firstName = firstName;
calendarAttendee.lastName = lastName;
//get organiser email address
ABAddressBookRef book = ABAddressBookCreateWithOptions(nil, nil);
ABRecordRef record = [organizer ABRecordWithAddressBook:book];
if (record) {
ABMultiValueRef value = ABRecordCopyValue(record, kABPersonEmailProperty);
if(ABMultiValueGetCount(value)) {
if (value
&& ABMultiValueGetCount(value) > 0) {
NSString *emailAddress = (__bridge id)ABMultiValueCopyValueAtIndex(value, 0);
calendarAttendee.emailAddress = emailAddress;
}
}
}
// add organizer to the attendees
if (firstName.length > 0) {
[calendarAttendees addObject:calendarAttendee];
calendarAttendee = NULL;
}
}
// now check for other meeting participants
if (thisEvent.attendees == nil) {
// if there isn't any meeting participants, then ignore the meeting
}else{
// go through the participants and collect there details
for (EKParticipant *participant in thisEvent.attendees){
fullName = @"";
firstName = @"";
lastName = @"";
if (participant.participantType == 1) {
// we have at least one "person" participant.
MMattendee *calendarAttendee = [[MMattendee alloc] init];
// get name
if (participant.name) {
fullName = participant.name;
firstName = [[fullName componentsSeparatedByString:@" "] objectAtIndex:0];
if (fullName.length > firstName.length) {
lastName = [fullName substringFromIndex:[fullName rangeOfString:firstName].length + 1];
}else{
// use the first part of the firstName (as it maybe an email address only)
if ([firstName rangeOfString:@"@"].location > 0 ) {
NSString *emailFrontPart = [[firstName componentsSeparatedByString:@"@"] objectAtIndex:0];
// now look for a dot sepatator in email string
// NSLog(@"%d", [emailFrontPart rangeOfString:@"."].location);
if ([emailFrontPart rangeOfString:@"."].location == NSNotFound) {
// no dot separator
firstName = emailFrontPart;
lastName = @" ";
}
else{
// get firstname as a firstpart
firstName = [[emailFrontPart componentsSeparatedByString:@"."] objectAtIndex:0];
// get lastname as the second part
if (lastName.length == 0) {
lastName = [[emailFrontPart componentsSeparatedByString:@"."] objectAtIndex:1];
}
}
}
}
calendarAttendee.firstName = firstName;
calendarAttendee.lastName = lastName;
}
//get participant's email address
ABAddressBookRef book = ABAddressBookCreateWithOptions(nil, nil);
ABRecordRef record = [participant ABRecordWithAddressBook:book];
if (record) {
ABMultiValueRef value = ABRecordCopyValue(record, kABPersonEmailProperty);
if(ABMultiValueGetCount(value)) {
if (value
&& ABMultiValueGetCount(value) > 0) {
NSString *emailAddress = (__bridge id)ABMultiValueCopyValueAtIndex(value, 0);
calendarAttendee.emailAddress = emailAddress;
}
}
}
// add to the attendees
if (firstName.length > 0) {
// NSLog(@"name = %@",calendarAttendee.firstName);
[calendarAttendees addObject:calendarAttendee];
calendarAttendee = NULL;
}
}
}
} // end of going through the participants
if (calendarAttendees.count > 1) {
// More than just the organiser, so we have a meeting
// get event details and put them into a meeting object
calendarEvent.eventID = thisEvent.eventIdentifier;
calendarEvent.eventTitle = thisEvent.title;
calendarEvent.dateTimeStart = [self.zoneDateFormat stringFromDate:thisEvent.startDate];
calendarEvent.dateTimeStop = [self.zoneDateFormat stringFromDate:thisEvent.endDate];
calendarEvent.location = thisEvent.location;
calendarEvent.attendees = calendarAttendees;
calendarEvent.organiser = minutesBy;
//save the event identifier so that we ignore in future
if (saveEventID) {
[self.meetingModel savePreviousEvent:eventID];
}
// add event to the NSArray
[calendarEvents addObject:calendarEvent];
}
}
} //end of loop for examining the events
}
}
return calendarEvents;
}