Kingfisher 将数据缓存在 RAM 中

Kingfisher caches the data in RAM

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

更新:这是新闻 JSON 型号。

["peopleProperties": ["numberOfPeopleDescription": "Nobody here", "numberOfPeople": 0, "availableSeats": 0], "isPrivateStatus": false, "additionalInfo": ["note": ""], "ownerID": "", "ticketsInfo": ["tickets": []], "isTest":错误,"isNewPendingRequest":错误,"dateProperties":["isEditable":正确,"iso8601":“”,"day":“”,"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 ,美国", "fullLocationName": "692 N Robertson Blvd", "coordinate": ["longitude": -118.38528500025966, "latitude": 34.083373986214625]], "id": "", "photoURLsProperties": ["placePhotoURLs": ["example"], "placeLogoURLs": []], "services": ["serviceURL": "", "serviceID": "41cf5080f964a520a61e1fe3", "index": 1], "version": 1, "title": "The 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 最初将所有图像存储在 RAM 中,它有 属性 如果应用程序收到内存警告,那么它应该释放内存,但在我的情况下这是不是。所以我给 Kingfisher 设置了限制,你只能使用 1 兆字节的 RAM 内存。

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

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