首页 文章

使用Swift在Core Data获取请求中发生内存泄漏

提问于
浏览
2

当我使用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&Roots时,它在根部显示 NSArray+16 list: (null)+24 [no ivar]: (null) 分支关闭 .

我是如何存储我的获取请求结果的?或者这是Swift如何与Core Data交互的错误?

编辑:根据Mundi的建议整理代码 . 编辑2:添加了调用获取请求函数的代码 .

3 回答

  • 1

    在尝试了很多东西之后,我知道了在获取我的 Day 实体时Core Data如何将NSArray转换为Swift Arrays的错误 . 也许它与 Day 实体的关系或属性有关 . 我会继续研究它 .

    现在,我找到了一个解决方案 . 仪器一直指向用于将NSArray转换为Array的libswiftFoundation方法,并且Cycles&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 数组是漏的,但这似乎是问题的根源 .

  • 0

    刚刚遇到了同样的问题 . 我为每个捕获列表添加了弱自我,无济于事 . 原来是一个名称空间冲突,系统无法推断我正在谈论的是哪个数组(或者它的类型)(因为它们共享相同的名称) . 我从'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 {} ... } }
    

    内存泄漏消失了 .

  • 1

    我认为你的获取代码过于冗长 . 特别是,我认为将获取结果分配给另一个变量会导致从Objective-C类 NSArray (这是获取请求的结果类型)的某种转换,这在某种程度上会导致泄漏 . (我也不完全理解为什么,但我认为这也与这是一个定义变量的类函数有关 . )

    我建议简化你的代码 .

    let request = NSFetchRequest(entityName: "Day")
    do    { return try context.executeFetchRequest(request) as! [Day]) } 
    catch { return [Day]() }
    

相关问题