繁体   English   中英

使用 Swift 获取核心数据请求中的内存泄漏

[英]Memory leak in Core Data fetch request with Swift

当我使用 Swift 发出核心数据获取请求时,我遇到了内存泄漏。 但是,我在应用程序的不同部分发出了几乎相同的 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:添加了调用获取请求函数的代码。

在尝试了很多事情之后,我很确定这是在获取我的Day实体时 Core Data 如何将 NSArray 转换为 Swift Arrays 的错误。 也许它与Day实体的关系或属性有关。 我会继续研究它。

现在,我找到了解决方法。 Instruments 一直指向 libswiftFoundation 方法将 NSArray 转换为 Array,而 Cycles & Roots 一直显示一个no ivar的 NSArray。 根据我的研究,这与 fetch 请求创建的 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数组泄漏,但这似乎是问题的根源。

刚刚经历了这个完全相同的问题。 我四处添加弱自我到每个捕获列表,但无济于事。 结果是命名空间冲突,系统无法推断出我正在谈论的数组(或可能是它的类型)(因为它们共享相同的名称)。 我将下面的“foundCategories”从“categories”重命名,这是我的视图控制器的一个属性。

func fetchCategoriesAndNotes() {

categories = []

   fetchEntity("Category", predicates: nil) { [weak self] (found) -> Void in

       guard let foundCategories = found as? [Category] else { return }

        for category in foundCategories {} ... } }

内存泄漏消失了。

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

我建议简化您的代码。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM