ios 来自调试器的消息:由于内存问题而终止

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40398746/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 10:43:01  来源:igfitidea点击:

Message from debugger: Terminated due to memory issue

iosswiftmapboxmapbox-glmapbox-marker

提问by lee

My app working with Geojsonfile. I use MapBox SDKto add MGLPolylineto map. But the problem is my file too large, so that the app crash and got the error: Message from debugger: Terminated due to memory issue. I faced with 66234objects at first loop. I tried to chunk the array to new array but not success. Please help me to solve the prolem. Here is my code for draw on map and here is my test project on github use Xcode 8.1If have any different 3rd party which can solve my prolems is welcome too:

我的应用程序处理Geojson文件。我使用MapBox SDK添加MGLPolyline到地图。但问题是我的文件太大,以至于应用程序崩溃并出现错误:Message from debugger: Terminated due to memory issue. 我在第一个循环中遇到了66234 个对象。我试图将数组分块到新数组,但没有成功。请帮我解决问题。这是我在地图上绘制的代码,这是我在 github 上的测试项目,使用 Xcode 8.1如果有任何不同的第 3 方可以解决我的问题,也欢迎

func drawPolyline() {

    // Parsing GeoJSON can be CPU intensive, do it on a background thread
    DispatchQueue.global(qos: .background).async {
        // Get the path for example.geojson in the app's bundle
        let jsonPath = Bundle.main.path(forResource: "KMLMAPNew", ofType: "json")
        let jsonData = NSData(contentsOfFile: jsonPath!)

        do {
            // Load and serialize the GeoJSON into a dictionary filled with properly-typed objects
            guard let jsonDict = try JSONSerialization.jsonObject(with: jsonData! as Data, options: []) as? Dictionary<String, AnyObject>, let features = jsonDict["features"] as? Array<AnyObject> else{return}

            for feature in features {
                guard let feature = feature as? Dictionary<String, AnyObject>, let geometry = feature["geometry"] as? Dictionary<String, AnyObject> else{ continue }

                if geometry["type"] as? String == "LineString" {
                    // Create an array to hold the formatted coordinates for our line
                    var coordinates: [CLLocationCoordinate2D] = []

                    if let locations = geometry["coordinates"] as? Array<AnyObject> {
                        // Iterate over line coordinates, stored in GeoJSON as many lng, lat arrays
                        for location in locations {
                            // Make a CLLocationCoordinate2D with the lat, lng
                            if let location = location as? Array<AnyObject>{
                                let coordinate = CLLocationCoordinate2DMake(location[1].doubleValue, location[0].doubleValue)

                                // Add coordinate to coordinates array
                                coordinates.append(coordinate)
                            }
                        }
                    }

                    let line = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count))

                    // Optionally set the title of the polyline, which can be used for:
                    //  - Callout view
                    //  - Object identification
                    line.title = "Crema to Council Crest"

                    // Add the annotation on the main thread
                    DispatchQueue.main.async {
                        // Unowned reference to self to prevent retain cycle
                        [unowned self] in
                        self.mapboxView.addAnnotation(line)
                    }
                }
            }
        }
        catch
        {
            print("GeoJSON parsing failed")
        }
    }
}

EDIT::@Alessandro Ornano and @fragilecat thanks so much. But those solutions still cannot solve the terminate of the app on iPad. I think it so hard to change the current code to get it to work properly, because the data is so large. I think I will need another solution that works with big data. Like chunking the array into the small arrays then loading them by queue. But I don't know how to start :(

编辑::@Alessandro Ornano 和@fragilecat 非常感谢。但是这些解决方案仍然无法解决 iPad 上应用程序的终止问题。我认为更改当前代码以使其正常工作非常困难,因为数据太大了。我想我需要另一个适用于大数据的解决方案。就像将数组分块成小数组然后按队列加载它们。但我不知道如何开始:(

I send an email to the support team at MapBox, asking for suggestions.

我向 MapBox 的支持团队发送了一封电子邮件,征求建议。

回答by DuckDucking

One thing I have learnt from creating memory intensive apps is that you have to use autoreleasepoolevery time you create variables inside loops, if these loops are long

我从创建内存密集型应用程序中学到的一件事是autoreleasepool,如果这些循环很长,则每次在循环内创建变量时都必须使用

Review all your code and transform things like

查看所有代码并转换诸如

func loopALot() {
    for _ in 0 ..< 5000 {
        let image = NSImage(contentsOfFile: filename)
    }
}

into

进入

func loopALot() {
    for _ in 0 ..< 5000 {
      autoreleasepool {
        let image = NSImage(contentsOfFile: filename)
      }
    }
}

Review all kinds of loops for, while, etc.

检查各类循环forwhile等等。

This will force of iOS to release the variable and its correspondent memory usage at the end of every turn of the loop, instead of holding the variable and its memory usage until the function ends. That will reduce dramatically your memory usage.

这将强制 iOS 在循环的每一轮结束时释放变量及其对应的内存使用量,而不是保持变量及其内存使用量直到函数结束。这将大大减少您的内存使用量。

回答by Pete Hornsby

The problem here is related to effective memory management. You are loading a lot of data via your json file. You realized that you needed to do the majority of the work on a background queue (thread) however the issue is how you are updating the UI via DispatchQueue.main.asyncfunction. In the current version of the drawPolyline()method you are switching between the background queue and the main queue 66234 times, given the number of objects in your first loop. Also you were creating the same number of CLLocationCoordinate2Darrays.

这里的问题与有效的内存管理有关。您正在通过 json 文件加载大量数据。您意识到您需要在后台队列(线程)上完成大部分工作,但问题是您如何通过DispatchQueue.main.async函数更新 UI 。在该drawPolyline()方法的当前版本中,您将在后台队列和主队列之间切换 66234 次,前提是第一个循环中的对象数量。您还创建了相同数量的CLLocationCoordinate2D数组。

This leads to a huge memory footprint. You do not mention any requirements in regards to how you render the lines. So if we restructure your drawPolyline()method to use a instance variable for the CLLocationCoordinate2Darray so we only use one and then we process all of the json file before we update the UI. The memory usage dropped down to a some what more manageable 664.6 MB.

这会导致巨大的内存占用。您没有提及有关如何渲染线条的任何要求。因此,如果我们重构您的drawPolyline()方法以使用CLLocationCoordinate2D数组的实例变量,那么我们只使用一个,然后在更新 UI 之前处理所有 json 文件。内存使用量下降到更易于管理的 664.6 MB。

enter image description here

在此处输入图片说明

Of course the rendering may not be exactly what you are looking for and if that's the case you might want to restructure your CLLocationCoordinate2Darray into a more suitable data structure.

当然,渲染可能并不完全符合您的要求,如果是这种情况,您可能希望将CLLocationCoordinate2D数组重组为更合适的数据结构。

Below is your ViewControllerclass with the rewritten drawPolyline()as drawPolyline2()

下面是你的ViewController类,重写drawPolyline()drawPolyline2()

import UIKit
import Mapbox

class ViewController: UIViewController, MGLMapViewDelegate {

@IBOutlet var mapboxView: MGLMapView!


fileprivate var coordinates = [[CLLocationCoordinate2D]]()
fileprivate var jsonData: NSData?

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    mapboxView = MGLMapView(frame: view.bounds)
    mapboxView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    // mapboxView.setCenter(CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
    //                             zoomLevel: 11, animated: false)

    mapboxView.setCenter(CLLocationCoordinate2D(latitude: 1.290270, longitude: 103.851959),
                         zoomLevel: 11, animated: false)


    view.addSubview(self.mapboxView)


    mapboxView.delegate = self
    mapboxView.allowsZooming = true

    drawPolyline2()
    //newWay()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}



func drawPolyline2() {

    DispatchQueue.global(qos: .background).async {

        if let path = Bundle.main.path(forResource: "KMLMAPNew", ofType: "json") {
            let fileURL = URL(fileURLWithPath: path)
            if let data = try? Data(contentsOf: fileURL) {

                do {

                    let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: []) as? Dictionary<String, AnyObject>

                    if let features = dictionary?["features"] as? Array<AnyObject> {

                        print("** START **")

                        for feature in features {
                            guard let feature = feature as? Dictionary<String, AnyObject>, let geometry = feature["geometry"] as? Dictionary<String, AnyObject> else { continue }

                            if geometry["type"] as? String == "LineString" {
                                // Create an array to hold the formatted coordinates for our line

                                if let locations = geometry["coordinates"] as? Array<AnyObject> {
                                    // Iterate over line coordinates, stored in GeoJSON as many lng, lat arrays

                                    var featureCoordinates = [CLLocationCoordinate2D]()

                                    for location in locations {
                                        // Make a CLLocationCoordinate2D with the lat, lng
                                        if let location = location as? Array<AnyObject>{
                                            let coordinate = CLLocationCoordinate2DMake(location[1].doubleValue, location[0].doubleValue)

                                            // Add coordinate to coordinates array
                                            featureCoordinates.append(coordinate)
                                        }
                                    }

                                    // Uncomment if you need to store for later use.
                                    //self.coordinates.append(featureCoordinates)

                                    DispatchQueue.main.async {
                                        let line = MGLPolyline(coordinates: &featureCoordinates, count: UInt(featureCoordinates.count))

                                        // Optionally set the title of the polyline, which can be used for:
                                        //  - Callout view
                                        //  - Object identification
                                        line.title = "Crema to Council Crest"
                                        self.mapboxView.addAnnotation(line)

                                    }


                                }

                            }

                        }

                        print("** FINISH **")

                    }

                } catch {
                    print("GeoJSON parsing failed")
                }
            }
        }
    }
}


func drawSmallListObj(list: [Dictionary<String, AnyObject>]){
    for obj in list{
        //            print(obj)
        if let feature = obj as? Dictionary<String, AnyObject> {
            if let geometry = feature["geometry"] as? Dictionary<String, AnyObject> {
                if geometry["type"] as? String == "LineString" {
                    // Create an array to hold the formatted coordinates for our line
                    var coordinates: [CLLocationCoordinate2D] = []

                    if let locations = geometry["coordinates"] as? Array<AnyObject> {
                        // Iterate over line coordinates, stored in GeoJSON as many lng, lat arrays
                        for location in locations {
                            // Make a CLLocationCoordinate2D with the lat, lng
                            if let location = location as? Array<AnyObject>{
                                let coordinate = CLLocationCoordinate2DMake(location[1].doubleValue, location[0].doubleValue)

                                // Add coordinate to coordinates array
                                coordinates.append(coordinate)
                            }
                        }
                    }

                    let line = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count))

                    // Optionally set the title of the polyline, which can be used for:
                    //  - Callout view
                    //  - Object identification
                    line.title = "Crema to Council Crest"

                    // Add the annotation on the main thread
                    DispatchQueue.main.async {
                        // Unowned reference to self to prevent retain cycle
                        [unowned self] in
                        self.mapboxView.addAnnotation(line)
                    }
                }
            }
        }
    }
}
func mapView(_ mapView: MGLMapView, alphaForShapeAnnotation annotation: MGLShape) -> CGFloat {
    // Set the alpha for all shape annotations to 1 (full opacity)
    return 1
}

func mapView(_ mapView: MGLMapView, lineWidthForPolylineAnnotation annotation: MGLPolyline) -> CGFloat {
    // Set the line width for polyline annotations
    return 2.0
}

func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor {
    // Give our polyline a unique color by checking for its `title` property
    if (annotation.title == "Crema to Council Crest" && annotation is MGLPolyline) {
        // Mapbox cyan
        return UIColor(red: 59/255, green:178/255, blue:208/255, alpha:1)
    }
    else
    {
        return UIColor.red
    }
}


}

enter image description here

在此处输入图片说明

回答by Alessandro Ornano

I had some problems to test your project with pods, so I've directly remove pods and use Mapbox framework directly from here.

我在使用 pod 测试您的项目时遇到了一些问题,因此我直接从此处删除了 pod 并直接使用 Mapbox 框架。

I've no problems for the first launches both in simulator and real iPad (my iPad 4 gen.) but after a while I've your same error, so I've correct this code with:

我在模拟器和真正的 iPad(我的 iPad 4 代)中的第一次启动都没有问题,但过了一段时间我遇到了同样的错误,所以我用以下代码更正了这段代码:

DispatchQueue.main.async {
      // weaked reference to self to prevent retain cycle
      [weak self] in
      guard let strongSelf = self else { return } 
      strongSelf.mapboxView.addAnnotation(line)
}

because unownedit's not enough to prevent retain cycle. Now seems it works well.

因为unowned这不足以防止保留循环。现在看来效果很好。

Hope it helps.

希望能帮助到你。

P.S.(I've used the latest available Mapbox v3.3.6)

PS (我使用了最新的可用 Mapbox v3.3.6)



Update(after comments):

更新(评论后):

So, first of all I make all my test with Mapbox framework inserted as "embedded framework".

因此,首先,我使用 Mapbox 框架作为“嵌入式框架”插入所有测试。

I've make some corrections to your github project only to ViewController.swiftto avoid retain cycles. P.S. I remove comments lines to make easy reading:

我对你的 github 项目做了一些更正,只是ViewController.swift为了避免保留周期。 附注。我删除了注释行以方便阅读:

func drawPolyline() {
        DispatchQueue.global(qos: .background).async {
            [weak self] in
            guard let strongSelf = self else { return }
            let jsonPath = Bundle.main.path(forResource: "KMLMAPNew", ofType: "json")
            let jsonData = NSData(contentsOfFile: jsonPath!)
            do {
                guard let jsonDict = try JSONSerialization.jsonObject(with: jsonData! as Data, options: []) as? Dictionary<String, AnyObject>, let features = jsonDict["features"] as? Array<AnyObject> else{return}
                for feature in features {
                    guard let feature = feature as? Dictionary<String, AnyObject>, let geometry = feature["geometry"] as? Dictionary<String, AnyObject> else{ continue }
                    if geometry["type"] as? String == "LineString" {
                        var coordinates: [CLLocationCoordinate2D] = []
                        if let locations = geometry["coordinates"] as? Array<AnyObject> {
                            for location in locations {
                                if let location = location as? Array<AnyObject>{
                                    let coordinate = CLLocationCoordinate2DMake(location[1].doubleValue, location[0].doubleValue)
                                    coordinates.append(coordinate)
                                }
                            }
                        }
                        let line = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count))
                        line.title = "Crema to Council Crest"
                        print(feature) // Added this line just for debug to see the flow..
                        DispatchQueue.main.async {
                            strongSelf.mapboxView.addAnnotation(line)
                        }
                    }
                }
            }
            catch
            {
                print("GeoJSON parsing failed")
            }
        }
    }

func newWay(){
        DispatchQueue.global(qos: .background).async {
            [weak self] in
            guard let strongSelf = self else { return }
            let jsonPath = Bundle.main.path(forResource: "KMLMAPNew", ofType: "json")
            let jsonData = NSData(contentsOfFile: jsonPath!)
            do {
                if let jsonDict = try JSONSerialization.jsonObject(with: jsonData! as Data, options: []) as? Dictionary<String, AnyObject> {
                    if let features = jsonDict["features"] as? Array<AnyObject> {
                        let chunks = stride(from: 0, to: features.count, by: 2).map {
                            Array(features[
NSURLCache.sharedURLCache().removeAllCachedResponses()
NSURLCache.sharedURLCache().diskCapacity = 0
NSURLCache.sharedURLCache().memoryCapacity = 0
..<min(
let day_url = NSURL(string: "http://www.example.com")
let day_url_request = NSURLRequest(URL: day_url,
    cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData,
    timeoutInterval: 10.0)

let day_webView = UIWebView()
day_webView.loadRequest(day_url_request)
+ 2, features.count)]) } for obj in chunks{ strongSelf.drawSmallListObj(list: obj as! [Dictionary<String, AnyObject>]) } } } } catch { print("GeoJSON parsing failed") } } } func drawSmallListObj(list: [Dictionary<String, AnyObject>]){ for obj in list{ if let feature = obj as? Dictionary<String, AnyObject> { if let geometry = feature["geometry"] as? Dictionary<String, AnyObject> { if geometry["type"] as? String == "LineString" { var coordinates: [CLLocationCoordinate2D] = [] if let locations = geometry["coordinates"] as? Array<AnyObject> { for location in locations { if let location = location as? Array<AnyObject>{ let coordinate = CLLocationCoordinate2DMake(location[1].doubleValue, location[0].doubleValue) coordinates.append(coordinate) } } } let line = MGLPolyline(coordinates: &coordinates, count: UInt(coordinates.count)) line.title = "Crema to Council Crest" DispatchQueue.main.async { [weak self] in guard let strongSelf = self else { return } strongSelf.mapboxView.addAnnotation(line) } } } } } }

回答by User511

First Solution

第一个解决方案

Maybe your for loop is running infinitely and allocating memory to an array with nil value every time. It is using high amounts of memory, so it gives this error.

也许您的 for 循环无限运行,并且每次都将内存分配给一个值为 nil 的数组。它正在使用大量内存,因此会出现此错误。

Please check by print something in the for loop.

请通过在 for 循环中打印一些内容来检查。

Second Solution

第二种解决方案

Add this in didReceiveMemoryWarning:

将此添加到didReceiveMemoryWarning

##代码##

You can also change the cache policy of the NSURLRequest:

您还可以更改以下缓存策略NSURLRequest

##代码##

More information on cache policies here.

有关缓存策略的更多信息,请访问此处

回答by Naloiko Eugene

Will share my experience with this strange issue.

将分享我对这个奇怪问题的经验。

For me the app crashed with "Message from debugger: Terminated due to memory issue" and instruments didn't help a lot. As well the Memory - was within the green limits. So I was not sure what is causing that. And it was not possible to debug, and single-device specific issue.

对我来说,应用程序因“来自调试器的消息:由于内存问题而终止”而崩溃,并且仪器没有太大帮助。以及内存 - 在绿色范围内。所以我不确定是什么原因造成的。并且无法调试,以及单设备特定问题。

Just restarted the iPhone 6- and the issue disappeared for now.

刚刚重新启动 iPhone 6- 现在问题消失了。

回答by demopix

make you stuff on callout this means execute polyne only when click on the pin func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)

让你在标注上填充这意味着只有在点击 pin func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) 时才执行 polyne