当我使用Swift发出Core Data fetch请求时,我遇到了内存泄漏。但是,我在应用程序的不同部分进行了几乎相同的获取请求,但它不会导致泄漏。在这两种情况下,获取请求都在视图控制器的viewDidLoad中进行,并且获取请求的结果被分配给视图控制器的可选属性。
以下是不会导致任何泄漏的获取请求的方法:
class LocationFilter {
//Lots of other code...
class func getAllPlacesOfRegionType<T: Region>(regionType: RegionType) -> [T] {
let fetchRequest = NSFetchRequest(entityName: regionType.rawValue)
var places: [T]
do {
places = try CoreDataStack.sharedInstance.context.executeFetchRequest(
fetchRequest) as! [T]
} catch let error as NSError {
NSLog("Fetch request failed: %@", error.localizedDescription)
places = [T]()
}
places.sortInPlace({ (firstPlace, nextPlace) -> Bool in
//Ingenious sorting code...
})
return places
}
}
在viewController的viewDidLoad中调用此方法,并将结果分配给属性var allRegions: [Region]?
,而不会发生任何泄漏。这是代码:
class PlacesTableViewController: UITableViewController {
var allRegions: [Region]?
@IBOutlet weak var segmentedRegions: UISegmentedControl!
@IBAction func selectRegionSegment(sender: UISegmentedControl) {
// When the segmented control is tapped, the appropriate list will be loaded.
switch sender.selectedSegmentIndex {
case 0: //Country Segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Country)
case 1: //States segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Province)
case 2: //Cities segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.City)
case 3: //Addresses segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Address)
default:
break
}
// Then reload the cells with animations.
let index = NSIndexSet(index: 0)
tableView.reloadSections(index, withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func viewDidLoad() {
super.viewDidLoad()
selectRegionSegment(segmentedRegions)
}
}
在另一个viewController的viewDidLoad中调用以下方法来设置属性var allDays: [Day]!
。
class DateFilter {
//Lots of other code in the class...
class func getAllDays() -> [Day] {
let fetchRequest = NSFetchRequest(entityName: "Day")
let days: [Day]
do {
days = try CoreDataStack.sharedInstance.context.executeFetchRequest(
fetchRequest) as! [Day]
} catch let error as NSError {
NSLog("Fetch request failed: %@", error.localizedDescription)
days = [Day]()
}
return days
}
}
这就是所谓的地方:
class SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var allDays: [Day]!
override func viewDidLoad() {
super.viewDidLoad()
allDays = DateFilter.getAllDays()
let backgroundView = UIView(frame: CGRectZero)
tableView.tableFooterView = backgroundView
tableView.backgroundColor = UIColor.groupTableViewBackgroundColor()
}
}
Xcode仪器在调用此函数时会检测到内存泄漏。据推测,负责的库是libswiftFoundation.dylib
,负责的框架是static Array<A>._forceBridgeFromObjectiveC<A>(NSArray, result:inout [A]?) -> ()
。当我看到Cycles&amp;根,它在根部显示NSArray
,+16 list: (null)
和+24 [no ivar]: (null)
分支。
我是否存储了如何存储获取请求的结果?或者这是Swift如何与Core Data交互的错误?
编辑:根据Mundi的建议整理代码。 编辑2:添加了调用获取请求函数的代码。
答案 0 :(得分:1)
在尝试了很多东西之后,我很确定这是在获取我的Day
实体时Core Data如何将NSArray转换为Swift Arrays的错误。也许它与Day
实体的关系或属性有关。我会继续研究它。
现在,我找到了一个解决方法。仪器一直指向用于将NSArray转换为Array的libswiftFoundation方法,以及Cycles&amp; Roots一直在显示一个带有no ivar
的NSArray。根据我的研究,这与由获取请求创建的NSArray的初始化有关,后者在后台转换为Swift数组。由于我无法更改此内容,因此我从获取结果中创建了一个新数组:
override func viewDidLoad() {
super.viewDidLoad()
let fetchResults: [Day] = DateFilter.getAllDays()
allDays = fetchResults.map({$0})
let backgroundView = UIView(frame: CGRectZero)
tableView.tableFooterView = backgroundView
tableView.backgroundColor = UIColor.groupTableViewBackgroundColor()
}
奇迹般地,内存泄漏消失了!我不完全确定fetchResults
数组为何泄漏,但这似乎是问题的根源。
答案 1 :(得分:1)
刚刚遇到了同样的问题。我为每个捕获列表添加了弱自我,无济于事。原来是一个名称空间冲突,系统无法推断我正在谈论的是哪个数组(或者它的类型)(因为它们共享相同的名称)。我从'categories'重命名了下面的'foundCategories',这是我的视图控制器的属性。
func fetchCategoriesAndNotes() {
categories = []
fetchEntity("Category", predicates: nil) { [weak self] (found) -> Void in
guard let foundCategories = found as? [Category] else { return }
for category in foundCategories {} ... } }
内存泄漏消失了。
答案 2 :(得分:0)
我认为您的提取代码过于冗长。特别是,我认为将获取结果分配给另一个变量会导致从Objective-C类NSArray
(这是获取请求的结果类型)的某种转换,这在某种程度上会导致泄漏。 (我也不完全理解为什么,但我认为这也与这是一个定义变量的类函数有关。)
我建议简化你的代码。
let request = NSFetchRequest(entityName: "Day")
do { return try context.executeFetchRequest(request) as! [Day]) }
catch { return [Day]() }