UITableView 和 NSFetchedResultsController 的内存泄漏

Posted

技术标签:

【中文标题】UITableView 和 NSFetchedResultsController 的内存泄漏【英文标题】:Memory leak with UITableView and NSFetchedResultsController 【发布时间】:2015-04-11 20:27:38 【问题描述】:

我有一个 UITableView,其内容由获取 CoreData 的 NSFetchedResultsController 管理。我有两种类型的表格视图单元格,一种带有图像,一种没有图像。图像由 UIDocument 处理,并保存在 iCloud 通用文档容器中,并由客户端名称引用。

当屏幕上有许多用户生成的图像时,单元格会被生成和重用,我的程序的内存使用率越来越高。大约 110 mb,我收到内存不足警告。

我怀疑我的 tableView 单元格中的 prepareForReuse() 方法没有正常工作。

这是我的 UITableViewCell 现在的样子:

class ClientTableViewCell: UITableViewCell 

@IBOutlet weak var clientName: UILabel!

@IBOutlet weak var clientModifiedDate: UILabel!

@IBOutlet weak var profileImage: UIImageView!

let dateFormatter = NSDateFormatter()

var document: MyDocument?
var documentURL: NSURL?
var ubiquityURL: NSURL?

var metaDataQuery: NSMetadataQuery?

var myClient : Client?

    didSet
    
        updateClientInfo()
    


override func prepareForReuse() 
    super.prepareForReuse()

    clientName.text = nil
    clientModifiedDate.text = nil

    if let mc = myClient
    
        if mc.imageurl != ""
        
            if let p = profileImage
            
                p.image = nil
             else
            
                NSLog("nil document")
            
        
     else
    
         NSLog("nil client")
    



func updateClientInfo()

    if myClient != nil
    
        clientName.text = myClient!.name

        dateFormatter.dateStyle = .ShortStyle

        let dispDate = dateFormatter.stringFromDate(NSDate(timeIntervalSinceReferenceDate: myClient!.dateModified))

        clientModifiedDate.text = dispDate

        if let imageName = myClient?.imageurl  

            if myClient?.imageurl != "" 

                var myClientName : String!

                myClientName = myClient!.name

                metaDataQuery = NSMetadataQuery()

                metaDataQuery?.predicate = NSPredicate(format: "%K like '\(myClientName).png'", NSMetadataItemFSNameKey)

                metaDataQuery?.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]

                NSNotificationCenter.defaultCenter().addObserver(self,
                    selector: "metadataQueryDidFinishGathering:",
                    name: NSMetadataQueryDidFinishGatheringNotification,
                    object: metaDataQuery!)

                metaDataQuery!.startQuery()

            
        
    




func metadataQueryDidFinishGathering(notification: NSNotification) -> Void 

        let query: NSMetadataQuery = notification.object as! NSMetadataQuery

        query.disableUpdates()

        NSNotificationCenter.defaultCenter().removeObserver(self, name: NSMetadataQueryDidFinishGatheringNotification, object: query)

        query.stopQuery()

        let results = query.results

        if query.resultCount == 1 
            let resultURL = results[0].valueForAttribute(NSMetadataItemURLKey) as! NSURL
                document = MyDocument(fileURL: resultURL)
                document?.openWithCompletionHandler((success: Bool) -> Void in
            if success 

                if let pi = self.profileImage
                
                    pi.image = self.document?.image
                


                 else 
                    println("iCloud file open failed")
                
            )

          else 
            NSLog("Could not find profile image, creating blank document")
        

如果您不熟悉 iCloud 和 UIDocument,您可能想知道我为什么要查询元数据来获取这些图像/文档。如果您知道在无处不在的容器中获取这些图像/文档的可靠方法,我会全力以赴。它会在表格视图单元格滚动期间导致此弹出效果。

这是我填充单元格的函数:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

    //set the client
    let client = clients.fetchedObjects![indexPath.row] as! Client

    //set the correct identifier
    if let imageurl = client.imageurl as String?
    
        if imageurl == ""
        
            var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifierNoImage, forIndexPath: indexPath) as? ClientTableViewCell
            cell!.myClient = client
            return cell!

         else
        
            var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? ClientTableViewCell
            cell!.myClient = client
            return cell!
        
    
    var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifierNoImage, forIndexPath: indexPath) as? ClientTableViewCell
    cell!.myClient = client
    return cell!

我原本打算使用属性 imageurl 来存储客户端配置文件图像的名称和 url,但由于无处不在的容器 url 对于每个设备都是唯一的,所以我现在只使用它来检测是否存在带有此的图像客户与否。

我已经有这个问题好几个星期了,似乎无法确定。任何指导将不胜感激!

工作台:Xcode 6.3 和 Swift 1.2。

【问题讨论】:

我遇到了完全相同的问题,仪器在 uitableview 单元格上显示泄漏,我使用的唯一额外的东西是领域,并且在我的代码上我已经验证没有任何保留周期。 :( 【参考方案1】:

解决了我自己的内存泄漏问题!在此答案的帮助下:[UIDocument never calling dealloc

我正在查询 UIDocuments 并且从未关闭它们。这就是为什么 ARC 没有清理我未使用的 UIDocuments。上面的链接有 Object C 版本,但是对于使用 Swift 的人,在使用它们后尝试这些行来关闭您的文档:

document?.updateChangeCount(UIDocumentChangeKind.Done)
document?.closeWithCompletionHandler(nil)

哇!很高兴这场混乱结束了。希望这对某人有帮助!

【讨论】:

以上是关于UITableView 和 NSFetchedResultsController 的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

核心数据中的触发器

使用 xmpp 获取 XMPPGroupCoreDataStorageObject,但 NSManagingContextDidSaveChangesNotification 似乎不起作用

UITableView backgroundColor 和 UITableView.appearance

UIView 与 UITableview 和 UIImageview,使用 reloadData 访问 UITableview

UITableView 和 numberOfRowsInSection 崩溃

核心数据、UITableView 和 UISegmentedControl