【问题标题】:Kingfisher caches the data in RAMKingfisher 将数据缓存在 RAM 中
【发布时间】:2017-06-03 11:09:21
【问题描述】:

我确实喜欢新闻提要,我遇到了以下问题,例如,如果用户上传了 300 多条新闻,那么应用程序已经占用了 300 多兆的内存。在测试期间,我确实得到了didReceiveMemoryWarning,它只帮助完全清理了dataSource。我还使用Kingfisher 来缓存图像。这种情况的最佳方法是什么?缓存第一个数据,如果用户将返回顶部(最新数据),然后从缓存中加载它们,或者是否有更好的方法?谢谢。

更新:这是新闻 JSON 模型。

["peopleProperties": ["numberOfPeopleDescription": "Nobody here", "numberOfPeople": 0, "availableSeats": 0], "isPrivateStatus": false, "additionalInfo": ["note": " "], "ownerID": "", "ticketsInfo": ["tickets": []], "isTest": false, "isNewPendingRequest": false, "dateProperties": ["isEditable": true, "iso8601": “”,“天”:“”,“endTimeStamp”:0.0,“isFlexDate”:真,“isFlexTime”:真,“timeStamp”:0.0],“boolProperties”:[“isPartnerGeneratedCard”:假,“isAutoGeneratedCard”: true, "isUserCreatedCard": false, "isAdminCreatedCard": false], "location": ["formattedAddress": "692 N Robertson Blvd (at Santa Monica Blvd), West Hollywood, CA 90069,United States", "fullLocationName": “692 N Robertson Blvd”,“坐标”:[“经度”:-118.38528500025966,“纬度”:34.083373986214625]],“id”:“”,“photoURLsProperties”:[“placePhotoURLs”:[“example”],“ placeLogoURLs": []], "services": ["serviceURL": "", "serviceID": "41cf5080f964a520a61e1fe3", "index": 1], "version": 1, "title": "Th e Abbey Food & Bar", "ownerName": "", "phones": [:]]

更新 1. 有时会导致应用崩溃。我的测试控制器

import UIKit
import SVProgressHUD
class CardTestTableViewController: UITableViewController {

    // MARK: - Managers

    fileprivate let firCardDatabaseManager = FIRCardDatabaseManager()
    fileprivate let apiManager = ableCardsAPIManager()

    // MARK: - API Manager's properties

    fileprivate var firstCardsCount = 0
    fileprivate var isSecondTypeRequestLaunch = false

    /// Main cards array
    fileprivate var cardsModels = [CardModel]()
    fileprivate var firCardsModels = [CardModel]()
    fileprivate var backendCardsModels = [CardModel]()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        definesPresentationContext = true
        requestAllData()

        // table view
        registerCells()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(false, animated: false)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        debugPrint("didReceiveMemoryWarning")
        cardsModels.removeAll()
        tableView.reloadData()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if cardsModels.count > 0 {
            debugPrint("cardsModels.first!.toJSON()", cardsModels.first!.toJSON(), "cardsModels.first!.toJSON()")

        }
        return cardsModels.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = cardTestTableViewCell(tableView, indexPath: indexPath)

        let lastElement = cardsModels.count - 15
        if indexPath.row == lastElement {
            secondRequest(indexPath.row)
        }

        return cell
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 298
    }


}

extension CardTestTableViewController {

    fileprivate func registerCells() {
        let nib = UINib(nibName: CardTestTableViewCell.defaultReuseIdentifier, bundle: Bundle.main)
        tableView.register(nib, forCellReuseIdentifier: CardTestTableViewCell.defaultReuseIdentifier)
    }


}

// MARK: - Cells 

extension CardTestTableViewController {

    fileprivate func cardTestTableViewCell(_ tableView: UITableView, indexPath: IndexPath) -> CardTestTableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: CardTestTableViewCell.defaultReuseIdentifier, for: indexPath) as! CardTestTableViewCell

        let card = cardsModels[indexPath.row]

        cell.setupData(card)

        return cell
    }

}

// MARK: - Requests

extension CardTestTableViewController {

    @objc fileprivate func requestAllData() {
        let requestGroup = DispatchGroup()
        if let topViewController = UIApplication.topViewController() {
            if topViewController.isKind(of: CardViewController.self) {
                SVProgressHUD.show()
            }
        }
        firCardsModels.removeAll()
        backendCardsModels.removeAll()

        requestGroup.enter()
        firCardDatabaseManager.getCardModelsByUserLocation(success: { [weak self] (userCardsModels) in
            debugPrint("Finish +++ fir", userCardsModels.count)
            self?.firCardsModels = userCardsModels
            requestGroup.leave()
        }) { (error) in
            // TODO: - Think about it: Do not show an error, because we have cards with FourSquare
            debugPrint("FIRCardDatabaseManager error", error.localizedDescription)
            requestGroup.leave()
        }

        requestGroup.enter()
        apiManager.requestCards(10, secondRequestLimit: 50, isFirstRequest: true, success: { [weak self] (cards) in
            self?.backendCardsModels = cards
            requestGroup.leave()
        }) { (error) in
            requestGroup.leave()
        }

        requestGroup.notify(queue: .main) { [weak self] in
            guard let _self = self else { return }
            _self.cardsModels.removeAll()
            _self.cardsModels.append(contentsOf: _self.firCardsModels)
            _self.cardsModels.append(contentsOf: _self.backendCardsModels)

            self?.tableView.reloadData()
            // for api manager
            self?.firstCardsCount = _self.cardsModels.count

            SVProgressHUD.dismiss()
        }
    }


    fileprivate func secondRequest(_ index: Int) {
        // the second request
        debugPrint("swipe index", index, "firstCardsCount", firstCardsCount)

        // This is for how much to the end of the deck, we ask for more cards.
        let muchMoreIndex = 15
        let checkNumber = firstCardsCount-1 - index - muchMoreIndex
        debugPrint("checkNumber", checkNumber)

        if checkNumber == 0 || checkNumber < 0 {
            guard !isSecondTypeRequestLaunch else { return }
            isSecondTypeRequestLaunch = true
            apiManager.requestCards(0, secondRequestLimit: 50, isFirstRequest: false, success: { [weak self] (backendCards) in
                DispatchQueue.main.async {
                    guard let _self = self else { return }
                    _self.cardsModels.append(contentsOf: backendCards)
                    _self.firstCardsCount = _self.cardsModels.count
                    _self.isSecondTypeRequestLaunch = false

                    _self.tableView.reloadData()
                }

                }, fail: { [weak self] (error) in
                    self?.isSecondTypeRequestLaunch = false
            })
        }
    }

}

import UIKit
import Kingfisher

class CardTestTableViewCell: UITableViewCell {

    @IBOutlet private weak var titleLabel: UILabel!
    @IBOutlet private weak var cardImageView: UIImageView!
    @IBOutlet private weak var profileImageView: UIImageView!

    override func prepareForReuse() {
        cardImageView.image = nil
        profileImageView.image = nil
    }

    func setupData(_ card: CardModel) {
        downloadImages(card)
        setupLabelsData(card)
    }

    private func downloadImages(_ card: CardModel) {
        if let placeAvatarURLString = card.photoURLsProperties.placePhotoURLs.first {
            if let placeAvatarURL = URL(string: placeAvatarURLString) {
                cardImageView.kf.indicatorType = .activity
                cardImageView.kf.setImage(with: placeAvatarURL)
            } else {
                cardImageView.image = UIImage(named: "CardDefaultImage")
            }
        } else if let eventLogoURLPath = card.photoURLsProperties.placeLogoURLs.first {
            if let url = URL(string: eventLogoURLPath) {
                cardImageView.kf.indicatorType = .activity
                cardImageView.kf.setImage(with: url)
            } else {
                cardImageView.image = UIImage(named: "CardDefaultImage")
            }
        } else {
            cardImageView.image = UIImage(named: "CardDefaultImage")
        }

        guard card.boolProperties.isAutoGeneratedCard != true && card.boolProperties.isAdminCreatedCard != true else {
            profileImageView.image = #imageLiteral(resourceName: "ProfileDefaultIcon")
            return
        }

        let firImageDatabaseManager = FIRImageDatabaseManager()
        firImageDatabaseManager.downloadCardUserProfileImageBy(card.ownerID) { [weak self] (url, error) in
            DispatchQueue.main.async {
                guard error == nil else {
                    self?.profileImageView.image = #imageLiteral(resourceName: "ProfileDefaultIcon")
                    return
                }
                guard let _url = url else {
                    self?.profileImageView.image = #imageLiteral(resourceName: "ProfileDefaultIcon")
                    return
                }
                self?.profileImageView.kf.indicatorType = .activity
                self?.profileImageView.kf.setImage(with: _url)
            }
        }
    }

    private func setupLabelsData(_ card: CardModel) {
        titleLabel.text = card.title
    }

}

更新 2。当我注释掉与 Kingfisher 框架相关的代码时,没有内存泄漏和应用程序崩溃。

【问题讨论】:

  • 使用 SDWebImage 进行图像缓存。
  • 嗨@KKRocks 请告诉我,翠鸟不适合什么?同一个库只用 swift 编写。接下来我想问一下,你认为这不是大数据集的问题(目前最多可以包含1000个模型)。
  • 能否分享您的新闻提要数据的示例单个对象。
  • 嗨@MuhammadAliYousaf 是的,我可以。我以json的形式这样做,以免复制模型类。这么方便?
  • 如果你的dataSource中不存储UIImage应该没有问题,如果你这样做了,删除它,还建议删除kingfisher并尝试SDWebImage看看它是否修复,任何lib都可能有问题,如果两者都有,那是你的代码错误

标签: ios swift uitableview kingfisher


【解决方案1】:

我解决了我的问题,事实上 Kingfisher 最初将所有图像存储在 RAM 中,它具有如果应用程序收到内存警告,那么它应该释放内存的属性,但在我的情况下不是这样。所以我为 Kingfisher 设置了只能使用 1 兆 RAM 内存的限制。

我把这个函数放在AppDelegate中,并调用函数didFinishLaunchingWithOptions

fileprivate func setupKingfisherSettings() {
        ImageCache.default.maxMemoryCost = 1
    }

【讨论】:

  • 奇怪的是这么著名的库需要设置来处理这个简单的问题,所有图像都应该缓存在磁盘上而不是 RAM 上
  • 没有改成这样:ImageCache.default.memoryStorage.config.totalCostLimit
猜你喜欢
  • 2016-12-15
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 2020-03-30
  • 2017-10-18
  • 2017-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多