从嵌套在函数中的闭包中快速抛出

时间:2015-10-19 11:53:45

标签: ios swift2 throws

我有一个抛出错误的函数,在这个函数中我有一个true闭包,我需要从它的完成处理程序中抛出错误。这可能吗?

到目前为止,这是我的代码。

inside a

5 个答案:

答案 0 :(得分:12)

定义抛出的闭包时:

enum MyError: ErrorType {
    case Failed
}

let closure = {
    throw MyError.Failed
}

然后这个闭包的类型是() throws -> (),并且将此闭包作为参数的函数必须具有相同的参数类型:

func myFunction(completion: () throws -> ()) {
}

这个函数你可以调用completion闭包同步:

func myFunction(completion: () throws -> ()) throws {
    completion() 
}

您必须使用throwstry!关键字添加到功能签名或致电完成中:

func myFunction(completion: () throws -> ()) {
    try! completion() 
}

或异步:

func myFunction(completion: () throws -> ()) {
    dispatch_async(dispatch_get_main_queue(), { try! completion() })
}

在最后一种情况下,您将无法捕获错误。

因此,如果completion方法中的eventStore.requestAccessToEntityType封闭且方法本身在其签名中没有throws,或者异步调用completion那么你就不能{{1来自这个闭包。

我建议您使用以下函数实现,它将错误传递给回调而不是抛出它:

throw

答案 1 :(得分:6)

在这种情况下不可能 - 完成处理程序必须使用throws(以及rethrows的方法)声明,而这个不是。

请注意,所有抛出只是Objective-C中NSError **的不同符号(inout error参数)。 Objective-C回调没有inout参数,因此无法传递错误。

您必须使用其他方法来处理错误。

一般情况下,Obj-C中的NSError **或Swift中的throws不能很好地处理异步方法,因为错误处理可以同步工作。

答案 2 :(得分:5)

因为throw是同步的,想要抛出的异步函数必须有一个抛出的内部闭包,例如:

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

答案 3 :(得分:0)

您无法使用throw启用功能,但返回状态或错误的closure! 如果不清楚我可以提供一些代码。

答案 4 :(得分:0)

requestAccessToEntityType以异步方式完成工作。最终运行完成处理程序后,您的函数已经返回。因此,不可能按照建议的方式从闭包中抛出错误。

您应该重构代码,以便授权部分与事件插入分开处理,并且只有在您知道授权状态符合预期/要求时才调用insertEventToDefaultCalendar

如果你真的想在一个函数中处理每个函数,你可以使用信号量(或类似的技术),以便异步代码部分在你的函数方面同步运行。

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
    }
}