Swift从闭包中嵌套一个函数
我有一个函数,抛出一个错误,在这个函数inside a
闭包,我需要从它的完成处理程序中抛出错误。 那可能吗 ?
这是我的代码到目前为止。
enum CalendarEventError: ErrorType { case UnAuthorized case AccessDenied case Failed } func insertEventToDefaultCalendar(event :EKEvent) throws { let eventStore = EKEventStore() switch EKEventStore.authorizationStatusForEntityType(.Event) { case .Authorized: do { try insertEvent(eventStore, event: event) } catch { throw CalendarEventError.Failed } case .Denied: throw CalendarEventError.AccessDenied case .NotDetermined: eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in if granted { //insertEvent(eventStore) } else { //throw CalendarEventError.AccessDenied } }) default: } }
在这种情况下,这是不可能的 – 完成处理程序将不得不用throws
声明(和方法rethrows
),这不是。
请注意,所有抛出对于Objective-C中的NSError **
(inout错误参数)来说只是一个不同的符号。 Objective-Ccallback函数没有inout参数,所以没有办法把错误传递出去。
您将不得不使用不同的方法来处理错误。
一般来说,Obj-C中的NSError **
或者Swift中的asynchronous方法都不能很好地发挥,因为error handling是同步的。
由于投掷是同步的,一个要抛出的asynchronous函数必须有一个内部的抛出,如:
func insertEventToDefaultCalendar(event :EKEvent, completion: (() throws -> Void) -> Void) { let eventStore = EKEventStore() switch EKEventStore.authorizationStatusForEntityType(.Event) { case .Authorized: do { try insertEvent(eventStore, event: event) completion { /*Success*/ } } catch { completion { throw CalendarEventError.Failed } } case .Denied: completion { throw CalendarEventError.AccessDenied } case .NotDetermined: eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in if granted { let _ = try? self.insertEvent(eventStore, event: event) completion { /*Success*/ } } else { completion { throw CalendarEventError.AccessDenied } } }) default: break } }
然后,在呼叫站点,你这样使用它:
insertEventToDefaultCalendar(EKEvent()) { response in do { try response() // Success } catch { // Error print(error) } }
当你定义抛出:
enum MyError: ErrorType { case Failed } let closure = { throw MyError.Failed }
那么这个闭包的types是() throws -> ()
,并且把这个闭包作为参数的函数必须具有相同的参数types:
func myFunction(completion: () throws -> ()) { }
这个函数可以调用completion
closures同步:
func myFunction(completion: () throws -> ()) throws { completion() }
你必须添加throws
关键字function签名或呼叫完成与try!
:
func myFunction(completion: () throws -> ()) { try! completion() }
或asynchronous:
func myFunction(completion: () throws -> ()) { dispatch_async(dispatch_get_main_queue(), { try! completion() }) }
在最后的情况下,你将无法捕捉到错误。
因此,如果eventStore.requestAccessToEntityType
方法中的completion
closures和方法本身在其签名中没有throws
,或者如果completion
asynchronous调用,则不能从此闭包中throw
。
我build议你以下实现你的函数,将错误传递给callback,而不是抛出:
func insertEventToDefaultCalendar(event: EKEvent, completion: CalendarEventError? -> ()) { let eventStore = EKEventStore() switch EKEventStore.authorizationStatusForEntityType(.Event) { case .Authorized: do { try insertEvent(eventStore, event: event) } catch { completion(CalendarEventError.Failed) } case .Denied: completion(CalendarEventError.AccessDenied) case .NotDetermined: eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in if granted { //insertEvent(eventStore) } else { completion(CalendarEventError.AccessDenied) } }) default: } }
你不能用throw
来创build函数,但是返回一个包含状态或者错误的closure
! 如果不清楚,我可以给一些代码。
requestAccessToEntityType
asynchronous执行其工作。 当完成处理程序最终运行时,你的函数已经返回。 因此,不可能像你所build议的那样从封闭中抛出一个错误。
您可能应该重构代码,以便授权部分与事件插入分开处理,并只在您知道授权状态如预期/要求时才调用insertEventToDefaultCalendar
。
如果你真的想在一个函数中处理everyhing,你可以使用一个信号(或类似的技术),以使asynchronous代码部分在你的函数方面同步。
func insertEventToDefaultCalendar(event :EKEvent) throws { var accessGranted: Bool = false let eventStore = EKEventStore() switch EKEventStore.authorizationStatusForEntityType(.Event) { case .Authorized: accessGranted = true case .Denied, .Restricted: accessGranted = false case .NotDetermined: let semaphore = dispatch_semaphore_create(0) eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in accessGranted = granted dispatch_semaphore_signal(semaphore) }) dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) } if accessGranted { do { try insertEvent(eventStore, event: event) } catch { throw CalendarEventError.Failed } } else { throw CalendarEventError.AccessDenied } }