2017-06-02 161 views
1

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

更新:这是新闻JSON模型。

[ “peopleProperties”: “numberOfPeopleDescription”: “没有人在这里”, “numberOfPeople”:0, “availableSeats”:0], “isPrivateStatus”:假的, “additionalInfo”: “注意”: “” ],“ownerID”:“”,“ticketsInfo”:[“tickets”:[]],“isTest”:false,“isNewPendingRequest”:false,“dateProperties”:[“isEditable”:true,“iso8601” “,”day“:”“,”endTimeStamp“:0.0,”isFlexDate“:true,”isFlexTime“:true,”timeStamp“:0.0],”boolProperties“:[”isPartnerGeneratedCard“:false,”isAutoGeneratedCard“:true ,“isUserCreatedCard”:false,“isAdminCreatedCard”:false],“location”:[“formattedAddress”:“692 N Robertson Blvd(位于Santa Monica Blvd),美国加利福尼亚州西好莱坞90069”,“fullLocationName” 692 N Robertson Blvd“,”coordinate“:[”longitude“:-118.38528500025966,”latitude“:34.083373986214625]],”id“:”“,”photoURLsProperties“:[”placePhotoURLs“:[例如“],”placeLogoURLs“:[]],”services“:[”serviceURL“:”“,”serviceID“:”41cf5080f964a520a61e1fe3“,”index“:1],”version“:1,修道院食品&酒吧”, “OWNERNAME”: “”, “手机”:[:]

更新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框架相关的代码时,就没有内存泄漏和应用程序崩溃。

+0

使用SDWebImage。 – KKRocks

+0

嗨@KKRocks请告诉我,在什么翠鸟不适合?这个相同的库只能快速写入。接下来我想问一下,你认为这不是一个大型数据集(目前最多可容纳1000个模型)的问题。 – Alexander

+0

您能否分享您的新闻提要数据的示例单个对象。 –

回答

1

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

AppDelegate放在这个函数和函数调用图像缓存didFinishLaunchingWithOptions

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

奇怪的是,看到这样一个着名的库需要设置来处理这个简单的问题,所有的图像应该缓存在磁盘上而不是RAM – Tj3n

+0

@ Tj3n请看这里https://github.com/onevcat/Kingfisher/issues/697 – Alexander