Я пытался использовать какой-то ответ json вне метода, но он возвращает пустой массив при выполнении метода, но внутри блока работает, как ожидалось, есть ли способ, которым мой метод вернет ожидаемое значение, вот мой пример кода:

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. К вашему сведению, я новичок в этом и попробовал решения на основе ответов на подобные вопросы, но не сработал, любой совет высоко ценится, спасибо.

1
mizan rahman 28 Май 2017 в 22:53

2 ответа

Лучший ответ

Рассмотрим исходную проблему , затем предоставим лучшее решение:

< Сильный > семафоры . Вы можете запустить свой сигнал слишком рано. Надежная идиома для безопасной сигнализации DispatchSemaphore при возврате из функции / замыкания заключается в использовании оператора defer. Например:

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

Это гарантирует, что вы всегда запускаете signal() независимо от точки выхода, избегая взаимоблокировок.

Сказав это, это может быть далеко не лучшим решением ...

Обработчики завершения . Вы разработали свой метод getCarouselContent для блокировки вызывающего кода до тех пор, пока не будет выполнен сетевой запрос, что может занять (очень!) Много времени. Если вы планируете вызывать этот метод из своего основного потока своего приложения, это, безусловно, приведет к очень плохому UX. Давайте посмотрим, что Apple говорит об этом:

Обязательно ограничьте тип работы, которую вы выполняете в главном потоке вашего приложения. Основной поток - это то, где ваше приложение обрабатывает сенсорные события и другой пользовательский ввод. Чтобы ваше приложение всегда реагировало на пользователя, вы никогда не должны использовать основной поток для выполнения долгосрочных или потенциально неограниченных задач, таких как задачи, имеющие доступ к сети. Вместо этого вы всегда должны перемещать эти задачи в фоновые потоки.

Распространенный шаблон заключается в том, чтобы передать блок завершения в ваш метод 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)")
}
1
Paulo Mattos 30 Май 2017 в 00:24

Забудьте семафоры, чтобы обойти асинхронное поведение метода. Научитесь понимать асинхронный шаблон и использовать обработчик завершения:

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
vadian 28 Май 2017 в 20:36