正常来讲,苹果的SDK是不会在Swift的非Optional的类型返回空指针的。如果我们遇到了这个情况,就应该立刻向苹果提交错误报告,让苹果修复这个API。文中提到的这个API,我已经向苹果提交了错误报告,期待苹果能尽快修复。下面来谈谈遇到这种情况要如何处理。
今天发现的坑是这个,func calendarItem(withIdentifier identifier: String) -> EKCalendarItem
,它是EKEventStore
的实例的方法,可以用来同时查询EKEvent
和EKReminder
匹配对应id的值。这个id指的是EKCalendarItem
的calendarItemIdentifier
。
实际上如果只是想获得EKEvent
,我们应该使用func event(withIdentifier identifier: String) -> EKEvent?
,可以看到,这个方法的返回值是Optional<EKEvent>
类型的,因此不会有空指针的问题。
代码示例:
在获得了访问日历和提醒事项的权限后,我们先创建一个日历和一个事件,保存这个日历和事件,然后删除事件。最后通过事件id来查询这个事件。由于此时事件已经被删除了,打印这个事件会导致程序崩溃。这个例子的意义在于,由于现在我们的日历、提醒事项都是同步的,很有可能的当前设备的事件,在其它设备被删掉了,而此时如果你还用原来的id查询,程序就有可能崩溃,因此需要额外的处理。
import UIKit
import EventKit
class ViewController: UIViewController {
var store:EKEventStore!
override func viewDidLoad() {
super.viewDidLoad()
store = (UIApplication.shared.delegate as! AppDelegate).store
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [unowned self] (timer) in
let status = EKEventStore.authorizationStatus(for: .event)
if status == .authorized {
timer.invalidate()
// add a new event calendar
let calendar = EKCalendar(for: .event, eventStore: self.store)
calendar.title = "issue test"
calendar.source = self.store.sources.filter({ $0.sourceType == .local || $0.sourceType == .calDAV}).first!
try! self.store.saveCalendar(calendar, commit: false)
// add a new event to above calendar
let event = EKEvent(eventStore: self.store)
event.calendar = calendar
event.title = "Have a rest"
event.startDate = Date()
event.endDate = Date().addingTimeInterval(30 * 60)
try! self.store.save(event, span: .thisEvent)
// commit
try! self.store.commit()
// store the id of the event
let id = event.calendarItemIdentifier
// remove the event
try! self.store.remove(event, span: .thisEvent, commit: true)
// get the event
let item = self.store.calendarItem(withIdentifier: id)
print(item)
}
}
}
}
由于我们添加了事件之后,又对事件进行了删除。此时再用事件的id获取项目时,项目实际应该返回nil,但是由于苹果设计这个API出现了问题,返回的不是Optional
,而是确定的类型,这导致该值为一个空指针。此时,你就算想使用try catch,也是不行的,因为它并没有throw。此时你对它做的大部分操作,都会导致程序崩溃。难道就没有路可以走了吗?其实也不是。
虽然它不是Optional类型,但是它是确定类型的空指针,这个是苹果API的错误,是从Objective-c转换到Swift时出现的。我们自己写的Swift不会允许这样的情况出现。
某种角度来说,它其实也是个nil,因此,我们可以使用if let as?来进行匹配
// get the event
let item = self.store.calendarItem(withIdentifier: id)
if let event = item as? EKEvent {
print(event)
}
else if let reminder = item as? EKReminder {
print(reminder)
}
else {
print("item is a null pointer")
}
这样就可以临时处理掉这个空指针的问题了。