【问题标题】:Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444, some times it work致命错误:索引超出范围:文件 Swift/ContiguousArrayBuffer.swift,第 444 行,有时可以
【发布时间】:2021-08-01 00:39:25
【问题描述】:

下午好。我正在做我最后一年的项目,在我的项目中,我使用Google Maps APICollectionView 中显示结果。

如果我测试打印数组,结果是成功的,我得到显示的数据。 有时应用程序运行良好,如果我运行它,它会运行并运行,75% 的时间我会收到致命错误:索引超出范围:文件 Swift/ContiguousArrayBuffer.swift,第 444 行。

任何帮助都非常感谢,非常感谢。

    import Foundation

// MARK: - BloodBanksData
struct BloodBanksData: Codable {
    let results: [Result]

    enum CodingKeys: String, CodingKey {
        case results
    }
}

// MARK: - Result
struct Result: Codable {
    let geometry: Geometry
    let name: String
    let openingHours: OpeningHours?
    let photos: [Photo]?
    let rating: Double
    let vicinity: String

    enum CodingKeys: String, CodingKey {
        case geometry, name
        case openingHours = "opening_hours"
        case photos
        case rating
        case vicinity
    }
}

// MARK: - Geometry
struct Geometry: Codable {
    let location: Location
}

// MARK: - Location
struct Location: Codable {
    let lat, lng: Double
}

// MARK: - OpeningHours
struct OpeningHours: Codable {
    let openNow: Bool

    enum CodingKeys: String, CodingKey {
        case openNow = "open_now"
    }
}

// MARK: - Photo
struct Photo: Codable {
    let photoReference: String

    enum CodingKeys: String, CodingKey {
        case photoReference = "photo_reference"
    }
}

我的模特:

import Foundation
struct BloodBanksModel {
    let name: String
    let photo: String
    let open_now: Bool
    let longitude: Double
    let latitude: Double
    let vincinity: String
    let rating: Double
}

我的经理类:

import Foundation

class BloodBanksManager {
    var bloodBanksArray = [BloodBanksModel]()
    
    
    //MARK: - Decoding JSON
    func performRequest(){
        if let url = URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=XX,XX&radius=1000.0&rankby=prominence&sensor=true&key=XXXXX&keyword=blood") {
            
            let session = URLSession(configuration: .default)
            
            let task = session.dataTask(with: url) { (data, response, error) in
                if error != nil {
                    print(error!)
                    return
                }
                
                if let safeData = data {
                    self.parseJSON(bloodBankData: safeData)
                }
            }
            task.resume()
        }
    }
    
    func parseJSON(bloodBankData: Data) {
        let decoder = JSONDecoder()
        
        do {
        let decodedData = try decoder.decode(BloodBanksData.self, from: bloodBankData)
            for i in 0...decodedData.results.count - 1 {
                bloodBanksArray.append(BloodBanksModel(name: decodedData.results[i].name, photo: decodedData.results[i].photos?[0].photoReference ?? decodedData.results[0].photos![0].photoReference, open_now: decodedData.results[i].openingHours?.openNow ?? false, longitude: decodedData.results[i].geometry.location.lng, latitude: decodedData.results[i].geometry.location.lat, vincinity: decodedData.results[i].vicinity, rating: decodedData.results[i].rating))
            }
            
        } catch {
            print(error)
        }
    }
    
}

我的视图控制器:

   var bloodBanksManager = BloodBanksManager()
    override func viewDidLoad() {
            super.viewDidLoad()
    ...
    bloodBanksManager.performRequest()
    ...
    }
// MARK: - UICollectionViewDataSource Methods
extension LandingViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: K.BloodBanks.bloodBankCellIdentifier, for: indexPath) as! BloodBanksCell
 cell.bloodBankName.text = self.bloodBanksManager.bloodBanksArray[indexPath.row].name
        cell.bloodBankImageView.sd_setImage(with: URL(string: "https://maps.googleapis.com/maps/api/place/photo?photoreference=\(bloodBanksManager.bloodBanksArray[indexPath.row].photo)&sensor=false&maxheight=1000&maxwidth=1000&key=XXX"), placeholderImage: #imageLiteral(resourceName: "bloodbank4"))
        
        return cell
    }
    
}

【问题讨论】:

  • 何时调用collectionView.reloadData() 为什么numberOfItemsInSection 硬编码为3?不应该将其链接到bloodBanksManager.bloodBanksArray.count 吗?

标签: ios json swift


【解决方案1】:

您应该将numberOfItemsInSection 方法更改为:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.bloodBanksManager.bloodBanksArray.count
}

通过返回 3,您假设您的数组总是三个项目长,因此每次您的 bloodBanksArray 超过 3 个项目时都会引发异常。

编辑 - 代码优化

  1. 您可以尝试通过解码为数组来优化您的 parseJSON 函数,避免 for 循环:
func parseJSON(bloodBankData: Data) {
    let decoder = JSONDecoder()
        
    do {
        bloodBanksArray = try decoder.decode([BloodBanksData].self, from: bloodBankData)
    } catch {
        print(error)
    }
}
  1. 您可以像这样更改cellForItemAt 方法,避免对同一行项目进行多次访问。
    此外,为 placeholderImage 使用全局变量,因为它始终相同。
    最后,尝试减小maxheightmaxwidth 参数的值:
let placeholderImage = #imageLiteral(resourceName: "bloodbank4")

...

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: K.BloodBanks.bloodBankCellIdentifier, for: indexPath) as! BloodBanksCell
    let bloodBankItem = self.bloodBanksManager.bloodBanksArray[indexPath.row]
    cell.bloodBankName.text = bloodBankItem.name

    let maxImageSize = 500
    guard let imgUrl = URL(string: "https://maps.googleapis.com/maps/api/place/photo?photoreference=\(bloodBankItem.photo)&sensor=false&maxheight=\(maxImageSize)&maxwidth=\(maxImageSize)&key=XXX") else {
        return cell
    }
    cell.bloodBankImageView.sd_setImage(with: imgUrl, placeholderImage: placeholderImage)
        
    return cell
}

加载缓慢的其他原因可能是: 3. 网速慢 4. 用于加载图片的库没有使用延迟加载,更多关于延迟加载的信息请参见UITableView/UICollectionViewthis

【讨论】:

  • 哇,我现在感觉好傻。非常感谢你!完美运行。我的目标是只显示 3 个退货,因为我有一个“查看全部”按钮来显示所有内容,但现在我想出了如何去做!还有一件事,如果你不介意我问,有没有办法让它们“更快”出现?
  • “更快”是什么意思?列表显示速度是否缓慢或图像需要时间渲染?
  • 图像/文本显示大约需要 5 秒
  • @mohamad 我已经根据一些建议编辑了我的答案。
【解决方案2】:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }

应该变成这样:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.bloodBanksManager.bloodBanksArray.count
}

我假设您的 bloodBanksArray 有 3/4 的时间有 3 个或更少的项目,但它没有的时候会引发错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    相关资源
    最近更新 更多