UITableView 和 NSFetchedResultsController 内存泄漏
Memory leak with UITableView and NSFetchedResultsController
我有一个 UITableView,其内容由获取 CoreData 的 NSFetchedResultsController 管理。我有两种类型的 table 视图单元格,一种有图像,一种没有图像。图像由 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,您可能想知道为什么我要查询元数据来获取这些 images/documents。如果您知道在无处不在的容器中获取这些 images/documents 的可靠方法,我会洗耳恭听。它会在 table 视图单元格滚动期间导致此弹出效果。
这是我填充单元格的函数:
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 对于每个设备都是唯一的,我现在只用它来检测是否有这个客户端的图像。
这个问题困扰我好几个星期了,似乎无法确定。任何指导将不胜感激!
Workbench: Xcode 6.3 和 Swift 1.2.
解决了我自己的内存泄漏问题!借助此处的答案:[UIDocument never calling dealloc
我正在查询 UIDocuments 但从未关闭它们。这就是为什么 ARC 没有清理我未使用的 UIDocuments 的原因。上面的 link 有 Object C 版本,但是对于使用 Swift 的人来说,在使用它们之后尝试这些行来关闭您的文档:
document?.updateChangeCount(UIDocumentChangeKind.Done)
document?.closeWithCompletionHandler(nil)
哇!很高兴混乱结束了。希望这对某人有所帮助!
我有一个 UITableView,其内容由获取 CoreData 的 NSFetchedResultsController 管理。我有两种类型的 table 视图单元格,一种有图像,一种没有图像。图像由 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,您可能想知道为什么我要查询元数据来获取这些 images/documents。如果您知道在无处不在的容器中获取这些 images/documents 的可靠方法,我会洗耳恭听。它会在 table 视图单元格滚动期间导致此弹出效果。
这是我填充单元格的函数:
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 对于每个设备都是唯一的,我现在只用它来检测是否有这个客户端的图像。
这个问题困扰我好几个星期了,似乎无法确定。任何指导将不胜感激!
Workbench: Xcode 6.3 和 Swift 1.2.
解决了我自己的内存泄漏问题!借助此处的答案:[UIDocument never calling dealloc
我正在查询 UIDocuments 但从未关闭它们。这就是为什么 ARC 没有清理我未使用的 UIDocuments 的原因。上面的 link 有 Object C 版本,但是对于使用 Swift 的人来说,在使用它们之后尝试这些行来关闭您的文档:
document?.updateChangeCount(UIDocumentChangeKind.Done)
document?.closeWithCompletionHandler(nil)
哇!很高兴混乱结束了。希望这对某人有所帮助!