我已经写了几个月的应用程序,现在刚刚开始检查内存泄漏-原来我有很多- 25个独特的类型(紫色感叹号)和超过500个,如果我浏览我的应用程序足够。
Xcode中的内存图主要指向1.)UIDiffableDataSource的不同元素,2.)“CFString”/“CFString(Storage)",3.)“Malloc Blocks”。Leaks Instrument也给了我类似的反馈,我说大约90%的内存泄漏是这些“Malloc块”,还说负责帧是newJSONString或newJSONValue。在许多泄漏中,有一个“4节点-cycle”包含Swift Closure Context,My UIDiffableDataSource<Item,Section>对象和__UIDiffableDataSource。CFString只有一个对象- CFString,没有其他。我将尝试添加显示2个示例的图像,但StackO限制了我添加它们的能力。
这让我相信我在为UICollectionViewDiffableDataSource自定义的dataSource闭包中创建了某种类型的内存泄漏。我试着让DataSource成为一个弱var &我试着在每个闭包中使用弱self和unowned self--特别是创建闭包的闭包,但它没有任何影响。
- 当Memory Graph/Leaks Instrument指向这些通用的“CFString”或“Malloc Block”对象时,这意味着什么?
- 有没有人知道是什么导致了这些内存泄漏,以及如何解决它们?
- 内存图和泄漏工具是否会误报泄漏/产生不必要的噪音,或者这些是合法的?
任何额外的信息或额外的资源将是超级有帮助的。到目前为止,我发现iOS学院和Mark Moeykens YouTube视频有助于理解基础知识,但在将其应用于我的应用程序时遇到了麻烦。如果有帮助,我可以提供代码块,但有很多代码可能会导致它,并且不确定在这里倾倒什么。
overview of errors
4 node cycle (diffableDataSource)的
CFString (Storage)的
在发布这个之后,我能够找到一些额外的信息。基于this post,我能够使用回溯窗格,95%的内存泄漏都指向我的DataSource()和我的applySnapshotUsing(sectionIDs,itemsBySection)方法[下面删除了这些]。
我觉得我已经找到了漏洞的来源,但仍然在如何或是否应该修复漏洞上遇到了困难......我试过将闭包设置为“弱自我”,并将任何可能的变量设置为弱,但都无济于事。任何帮助都将不胜感激:)
Backtrace1Backtrace2
func applySnapshotUsing(sectionIDs: [SectionIdentifierType], itemsBySection: [SectionIdentifierType: [ItemIdentifierType]],animatingDifferences: Bool, sectionsRetainedIfEmpty: Set<SectionIdentifierType> = Set<SectionIdentifierType>()) {
var snapshot = NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType>()
for sectionID in sectionIDs {
guard let sectionItems = itemsBySection[sectionID], sectionItems.count > 0 || sectionsRetainedIfEmpty.contains(sectionID) else { continue }
snapshot.appendSections([sectionID])
snapshot.appendItems(sectionItems, toSection: sectionID)
}
self.apply(snapshot, animatingDifferences: animatingDifferences)
}
func createDataSource() -> DataSourceType {
//use DataSourceType closure provided by UICollectionViewDiffableDataSource class to setup each collectionViewCell
let dataSource = DataSourceType(collectionView: collectionView) { [weak self] (collectionView, indexPath, item) in
//loops through each 'Item' in itemsBySection (provided to the DataSource snapshot) and expects a UICollectionView cell to be returned
//unwrap self
guard let self = self else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SeeMore", for: indexPath)
return cell
}
//figure out what type of ItemIdentifier we're dealing with
switch item {
case .placeBox(let place):
//most common cell -> will display an image and Place name in a square box
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Restaurant", for: indexPath) as! RestaurantBoxCollectionViewCell
//Place object stored in each instance of ViewModel.Item and can be used to configure the cell
cell.placeID = place.ID
cell.segment = self.model.segment
//fetch image with cells fetchImage function
//activity indicator stopped once the image is returned
cell.imageRequestTask?.cancel()
cell.imageRequestTask = nil
cell.restaurantPic.image = nil
cell.fetchImage(imageURL: place.imageURL)
//image task will take time so animate an activity indicator to show activity in progress
cell.activityIndicator.isHidden = false
cell.activityIndicator.startAnimating()
cell.restaurantNameLabel.text = place.name
//setup long press gesture recognizer - currently contains 1 context menu item (<3 restaurant)
let lpgr : UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongPress))
lpgr.minimumPressDuration = 0.5
lpgr.delegate = cell as? UIGestureRecognizerDelegate
lpgr.delaysTouchesBegan = true
self.collectionView?.addGestureRecognizer(lpgr)
//return cell for each item
return cell
case .header:
//top cell displaying the city's header image
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Header", for: indexPath) as! CityHeaderCollectionViewCell
self.imageRequestTask = Task {
if let image = try? await ImageRequest(path: self.city.headerImageURL).send() {
cell.cityHeaderImage.image = image
}
self.imageRequestTask = nil
}
return cell
}
}
dataSource.supplementaryViewProvider = { (collectionView, kind, indexPath) in
//Setup Section headders
let header = collectionView.dequeueReusableSupplementaryView(ofKind: "SectionHeader", withReuseIdentifier: "HeaderView", for: indexPath) as! NamedSectionHeaderView
//get section from IndexPath
let section = dataSource.snapshot().sectionIdentifiers[indexPath.section]
//find the section we're currently in & set the section header label text according
switch section {
case .genreBox(let genre):
header.nameLabel.text = genre
case .neighborhoodBox(let neighborhood):
header.nameLabel.text = "The best of \(neighborhood)"
case .genreList(let genre):
header.nameLabel.text = genre
case .neighborhoodList(let neighborhood):
header.nameLabel.text = "The best of \(neighborhood)"
default:
print(indexPath)
header.nameLabel.text = "favorites"
}
return header
}
return dataSource
}
字符串
2条答案
按热度按时间oogrdqng1#
经过几个月的调试,我想我终于找到了问题所在。出于某种原因,我不完全理解我的集合视图(集合视图提供程序)中的节头导致我的许多对象被捕获在内存中,而不是被释放。
将这一行添加到我的CollectionViewController的deinit{}中,完全消除了我的应用程序中的内存泄漏(使用Memory Graph Trigger和Leaks Instrument验证):
第一个月
为了得出这个结论,我一点一点地重建了我的应用程序的相关部分,并在每次添加新代码时进行测试,以确定漏洞在哪里引入。
任何额外的澄清,为什么这导致泄漏将是伟大的太。
roejwanj2#
我知道它很旧,但我认为它可能有用。
当您访问dataSource以获取section enum值时,它可以引用在该函数中创建的dataSource,这是泄漏的。
你可以在这里有两个选择:
使用
[weak self]
并明确使用self.dataSource
字符串
或
您可以捕获dataSource
[weak dataSource]
的弱引用并正常使用它型
只是提醒一下,如果你正在使用来自self的任何东西,并且你想非常明确,你应该使用
[weak self, weak dataSource]
来只获取那些的弱引用。