3

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

更新:这是新闻 JSON 模型。

[“peopleProperties”:[“numberOfPeopleDescription”:“这里没有人”,“numberOfPeople”:0,“availableSeats”:0],“isPrivateStatus”:false,“additionalInfo”:[“note”:“”],“ownerID” :“”,“ticketsInfo”:[“tickets”:[]],“isTest”:false,“isNewPendingRequest”:false,“dateProperties”:[“isEditable”:true,“iso8601”:“”,“day” :“”,“endTimeStamp”:0.0,“isFlexDate”:真,“isFlexTime”:真,“timeStamp”:0.0],“boolProperties”:[“isPartnerGeneratedCard”:假,“isAutoGeneratedCard”:真,“isUserCreatedCard”:假,“isAdminCreatedCard”:假],“location": ["formattedAddress": "692 N Robertson Blvd (at Santa Monica Blvd), West Hollywood, CA 90069,United States", "fullLocationName": "692 N Robertson Blvd", "coordinate": ["longitude": -118.38528500025966,“纬度”:34.083373986214625]],“id”:“”,“photoURLsProperties”:[“placePhotoURLs”:[“example”],“placeLogoURLs”:[]],“服务”:[“serviceURL”: "", "serviceID": "41cf5080f964a520a61e1fe3", "index": 1], "version": 1, "title": "The Abbey Food & Bar", "ownerName": "", "phones": [:] ]CA 90069,美国”,“fullLocationName”:“692 N Robertson Blvd”,“坐标”:[“经度”:-118.38528500025966,“纬度”:34.083373986214625]],“id”:“”,“photoURLsProperties”:[ “placePhotoURLs”:[“example”],“placeLogoURLs”:[]],“services”:[“serviceURL”:“”,“serviceID”:“41cf5080f964a520a61e1fe3”,“index”:1],“version”:1 , "title": "The Abbey Food & Bar", "ownerName": "", "phones": [:]]CA 90069,美国”,“fullLocationName”:“692 N Robertson Blvd”,“坐标”:[“经度”:-118.38528500025966,“纬度”:34.083373986214625]],“id”:“”,“photoURLsProperties”:[ “placePhotoURLs”:[“example”],“placeLogoURLs”:[]],“services”:[“serviceURL”:“”,“serviceID”:“41cf5080f964a520a61e1fe3”,“index”:1],“version”:1 , "title": "The Abbey Food & Bar", "ownerName": "", "phones": [:]]id”:“”,“photoURLsProperties”:[“placePhotoURLs”:[“example”],“placeLogoURLs”:[]],“服务”:[“serviceURL”:“”,“serviceID”:“41cf5080f964a520a61e1fe3”,“ index”:1],“version”:1,“title”:“The Abbey Food & Bar”,“ownerName”:“”,“phones”:[:]]id”:“”,“photoURLsProperties”:[“placePhotoURLs”:[“example”],“placeLogoURLs”:[]],“服务”:[“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 框架相关的代码时,没有内存泄漏和应用程序崩溃。

4

1 回答 1

4

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

我将此函数放入AppDelegate并调用函数didFinishLaunchingWithOptions

fileprivate func setupKingfisherSettings() {
        ImageCache.default.maxMemoryCost = 1
    }
于 2017-06-02T11:35:59.693 回答