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 -> ()) { } 

这个函数可以调用completionclosures同步:

 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方法中的completionclosures和方法本身在其签名中没有throws ,或者如果completionasynchronous调用,则不能从此闭包中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 ! 如果不清楚,我可以给一些代码。

requestAccessToEntityTypeasynchronous执行其工作。 当完成处理程序最终运行时,你的函数已经返回。 因此,不可能像你所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 } } 
Interesting Posts