如何将DispatchSemaphore与Alamofire和SwiftyJSON一起使用?

时间:2017-05-28 19:53:23

标签: json swift asynchronous swifty-json

我试图在方法之外使用一些json响应,但是当执行方法时它返回空数组,但是在块内部按预期工作,我的方法是否会返回预期值,这是我的示例代码:< / p>

func getCarouselContent(url: String) -> [String] {
    var ids: [String] = []
    let headers = ["api_key": "My_API_KEY"]

    let semaphore = DispatchSemaphore(value: 0)
    Alamofire.request(url, headers: headers).validate().responseJSON { 
        (response) in
        semaphore.signal()
        switch response.result {
        case .success(let value):
            let json = JSON(value)
            let data = json["data"]["relationships"]["slides"]["data"]
            for child in data.array! {
                let id = child["id"].string!
                print(id) // this prints all the id
                ids.append(id)
            }
        case .failure(let error):
            print(error)
        }
    }
    semaphore.wait()
    return ids 
}

我正在使用alamofire和swiftyjson来解析json。仅供参考我是新手,并尝试过类似问题的回答,但没有成功,任何建议都非常感谢,谢谢。

2 个答案:

答案 0 :(得分:2)

忘记信号量以解决方法的异步行为。学习理解异步模式并使用完成处理程序:

func getCarouselContent(url: String, completion: ([String])->())  {
       var ids = [String]()
       let headers = ["api_key": "My_API_KEY"]

        Alamofire.request(url, headers: headers).validate().responseJSON{ response in
           switch response.result {
            case .success(let value):
                let json = JSON(value)

                let data = json["data"]["relationships"]["slides"]["data"]
                for child in data.array! {
                    let id = child["id"].string!
                    print(id) // this prints all the id
                    ids.append(id)
                }
            case .failure(let error):
                print(error)
            }
            completion(ids)
        }
}

并称之为:

getCarouselContent(url: <someURL>) { identifiers in

    print(identifiers)
}

答案 1 :(得分:1)

要解决原始问题然后提供更好的解决方案:

<强>信号量即可。您可能很快就会发出信号。当从函数/闭包返回时,用于安全地发信号通知DispatchSemaphore的强大习惯是使用defer语句。例如:

Alamofire.request(url, headers: headers).validate().responseJSON {    
    (response) in {
    defer { 
        semaphore.signal() // Executed before leaving current scope.
    } 
    ...
}

这可确保始终独立于您的退出点触发signal(),从而避免死锁。

话虽如此,这可能远非最佳解决方案......

完成处理程序。您将getCarouselContent方法设计为阻止调用代码,直到完成网络请求,这可能需要很长时间。 如果您计划从您的应用主线程调用此方法,这肯定会导致非常糟糕的用户体验。让我们看看Apple says about this

  

请务必限制您在应用主线程上执行的工作类型。主线程是您的应用处理触摸事件和其他用户输入的位置。为确保您的应用始终对用户做出响应,您绝不应使用主线程执行长时间运行或可能无限制的任务,例如访问网络的任务。相反,您应该始终将这些任务移到后台线程上。

围绕此问题的常见模式是将完成块传递给getCarouselContent方法。当JSON响应最终到达时,该块将被传递结果。例如:

func getCarouselContent(url: String, completion: @escaping ([String]) -> Void) {
    let headers = ["api_key": "My_API_KEY"]
    Alamofire.request(url, headers: headers).validate().responseJSON { 
        (response) in
        var ids = [String]()
        switch response.result {
        case .success(let value):
            let json = JSON(value)
            let data = json["data"]["relationships"]["slides"]["data"]
            for child in data.array! {
                ids.append(child["id"].string!)
            }
        case .failure(let error):
            print(error)
        }
        completion(ids)
    }
}

并将其称为:

getCarouselContent(url: someUrl) {
    (ids) in 
    print("IDs received: \(ids)")
}