swift 数组已填充到调度队列中,但当我尝试使用方法中的内容时,它为空

yc0p9oo0  于 2023-01-12  发布在  Swift
关注(0)|答案(1)|浏览(84)

我从API中提取了JSON数据,并使用JSON解码器进行了解码,然后尝试用分派队列中的JSON数据填充数组数据结构,以便在其他方法中使用它,但当我尝试在其他方法或函数中访问数据时,它会显示数组为空
这就是我解码JSON数据并尝试填充分派队列中的数组的方法

if let url = URL(string:"https://parseapi.back4app.com/classes/restaurants"){
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("app id", forHTTPHeaderField: "X-Parse-Application-Id")
            request.setValue("REST API key", forHTTPHeaderField: "X-Parse-REST-API-Key")
            let session = URLSession.shared
            session.dataTask(with: request) { [self] (data, response, err) in
                guard let jsonData = data else {
                    return
                }
                do {
                    let decoder = JSONDecoder()
                    let restaurantList = try decoder.decode(restaurants.self, from: jsonData)
                    self.restaurantStruct = restaurantList
                    DispatchQueue.main.async {
                        self.fillArray()
                    }
                }
                catch let jsonErr {
                    print("Error decoding JSON", jsonErr)
                }
            }
            .resume()
        }

这是我编写的fillArray方法
x一个一个一个一个x一个一个二个x

v8wbuo2f

v8wbuo2f1#

我(还)没有解决方案,但在评论中讨论之后,我认为提供一些代码可能有助于调试过程,而评论对此太有限了。
首先你需要确定你的集合视图是否如你所期望的那样工作。为了确定我们需要一些本地数据,将这些数据添加到你的ViewController源文件中。它们可以是全局的

// debugRestuarantsJSON should be exactly the same format you're 
// expecting from your REST call.
fileprivate let debugRestaurantsJSON = "Paste valid restaurants JSON text here"
fileprivate let debugRestaurantsData = debugRestaurantsJSON.data(using: .utf8)

然后我们将dataTask完成处理程序的大部分内容提取到它自己的方法中,以便我们可以将其用于调试数据:

private func setRestaurantData(from restaurantJSONData: Data?)
{
    guard let jsonData = restaurantJSONData else { return }

    do {
        let decoder = JSONDecoder()
        let restaurantList = try decoder.decode(restaurants.self, from: jsonData)
        self.restaurantStruct = restaurantList
        self.fillArray()
    }
    catch let jsonErr {
      print("Error decoding JSON", jsonErr)
    }
}

请注意,我从对fillArray()的调用中删除了DispatchQueue.main.async,因此如果您更改了fillArray以进行UI调用(例如我在注解中建议调用setNeedsDisplay()的那个),请删除这些调用。
让我们也将dataTask调用封装在一个方法中:

private func requestRestaurantData(from urlString: String)
{
    if let url = URL(string: urlString)
    {
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("app id", forHTTPHeaderField: "X-Parse-Application-Id")
        request.setValue("REST API key", forHTTPHeaderField: "X-Parse-REST-API-Key")
        let session = URLSession.shared
        session.dataTask(with: request)
        { (data, response, err) in
            if let err = err {
                print("URL request failed: \(err.localizedDescription)")
            }
            else { self.setRestaurantData(from: data) }
        }
        .resume()
     }
}

我添加了err不是nil的情况下的检查,以处理网络/HTTP错误,并且它期望URL作为String参数。它还使用我们前面创建的setRestuarantData(from:)方法。除此之外,它与您的代码相同。
我们不再直接调用代码中最初调用dataTask的地方的requestRestaurantData(from:),而是提供一个更高级别的间接调用:

enum RestaurantRequestType {
    case normal, debugSync, debugAsync
}

func requestRestaurantData(_ requestType: RestaurantRequestType = .normal)
{
    switch requestType
    {
        case .normal:
            requestRestaurantData(
                from: "https://parseapi.back4app.com/classes/restaurants"
            )
            
        case .debugSync:
            setRestaurantData(from: debugRestaurantsData)
            
        case .debugAsync:
            DispatchQueue.main.async {
                self.setRestaurantData(from: debugRestaurantsData)
            }
    }
}

现在,在最初调用dataTask的代码中,将其替换为

requestRestaurantData(.normal)

这应该会给予与您已经拥有的完全相同的行为。只是要确保在进行这些更改时没有破坏任何东西。换句话说,您的UICollectionView仍然不包含餐馆数据。这是意料之中的,但是之前工作的其他所有东西应该仍然工作。
然后从.normal更改为.debugSync。这将在requestRestaurantData(_:)返回之前同步填充数组。如果唯一的问题是从异步任务设置视图的数据,则视图现在应该显示调试数据。如果没有,则根本上没有正确设置视图或其数据,必须在继续之前修复。
假设视图显示了调试数据,现在从.debugSync更改为.debugAsync,这将在异步任务中填充数组,因此它的行为更像dataTask,但没有网络访问,延迟也更短。因为它不需要等待网络响应,它只会在主运行循环的下几次迭代中执行,如果没有其他任务排队的话,可能会在下一次迭代中执行。
如果你的视图没有使用.debugAsync更新,我想应该是这样,你知道你没有通知视图,它的委托,或者因为它是UICollectionView,它的dataSource有新的数据,所以它需要更新/重绘。我目前的想法是你需要为你的UICollectionView调用reloadData方法:

private func setRestaurantData(from restaurantJSONData: Data?)
{
    guard let jsonData = restaurantJSONData else { return }

    do {
        let decoder = JSONDecoder()
        let restaurantList = try decoder.decode(restaurants.self, from: jsonData)
        self.restaurantStruct = restaurantList
        self.fillArray()
       
        // ADD THE FOLLOWING LINE - replace yourUICollectionView appropriately
        DispatchQueue.main.async { self.yourUICollectionView.reloadData() }
    }
    catch let jsonErr {
      print("Error decoding JSON", jsonErr)
    }
}

如果这不起作用,那么将有更多的调试要做。评论让我知道。
最后,当你让它工作时,从.debugAsync更改为.normal。假设网络连接和服务器正在运行,你的视图应该工作。如果没有,有一些完全不同的问题,我想。但我很肯定,如果它工作与.debugAsync它将工作与.normal
一旦你让它工作了,你就可以删除调试代码了。不过我会把非调试的提取方法留在原处。

相关问题