【问题标题】:Reverse Geocode CLGeocoder Memory Leak反向地理编码 CLGeocoder 内存泄漏
【发布时间】:2020-07-02 06:07:09
【问题描述】:

我正在编写一个使用反向地理编码和 CLGeocoder 命令的应用程序。我正在使用 Xcode 11.3 和 Swift 5。

我使用的代码是标准的并且可以工作。我可以很好地执行反向地理编码。问题是我的应用程序每 5 分钟调用一次反向地理编码以报告当前地址 - 这绝对没问题....但是,大约 4 天后,我的应用程序不再在我的测试 iPhone 上运行。当我说不再工作时,我的意思是我单击图标打开应用程序并返回到 IOS。当我在模拟器上运行应用程序时,我可以看到大约每 5 分钟内存消耗增加 0.01MB,这表明可能在 4 天后容器内存已满并且应用程序停止正常工作。

因此,总而言之,我的问题是有没有其他人注意到,在一段时间内使用反向地理编码时,他们的应用程序无法正常运行。

我知道它是反向地理编码,就好像我不调用该函数一样,内存消耗保持不变。

我知道您可以调用反向地理定位的频率是有限制的 - 也许 Apple 知道它的漏洞,这就是限制存在的原因(以减少人们做我正在做的事情)???

我附上了整个股票标准代码。我已经稍微修改了它,因为我已经取出了频率元素,现在你可以点击屏幕上的一个按钮来触发反向地理定位……点击大约 10 次后,你会看到你的内存增加,如果你继续这样做你会继续看到内存增加...(最好每 15 - 20 秒尝试一次)...

我的代码:

import Foundation
import UIKit
import CoreLocation


class ViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager:CLLocationManager = CLLocationManager()

    var currentLatitude: Double = 37.33030778
    var currentLongitude: Double = -122.02896839


    @IBOutlet var myButton: UIButton!



    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        locationManager.delegate = self

        print("Running")

    }


    @IBAction func myButtonPressed(_ sender: Any) {
        // Print inside IBACTION
        print("Inside IBACTION")

        // incease latitude and longitue
        currentLatitude = currentLatitude + 0.10
        currentLongitude = currentLongitude + 0.10
        myReverseGeo()
    }



    func myReverseGeo() {

        let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)

        let geocoder: CLGeocoder = CLGeocoder()

        geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in

            if error != nil {
                let errorString = String(describing: error?.localizedDescription)

                print("Reverse Geocoding failed")
                print(errorString)

                return
            }

            let placemark = placemarks! as [CLPlacemark]

                if placemark.count > 0 {

                    let placemark = placemarks![0]

                    let eventCity = placemark.locality
                    let eventState = placemark.subAdministrativeArea
                    let eventCountry = placemark.country

                    print(eventCity!)
                    print(eventState!)
                    print(eventCountry!)

                }
        })

    }

}

关于如何阻止内存泄漏的任何想法(除了不使用反向地理编码)???

【问题讨论】:

  • 当前代码(但它缺少结尾?)不应该泄漏。如果我没记错的话,你会受到 n 次反向位置数量的限制。会不会是这样。当您说它在 4 天内不再工作时,您的意思是应用程序在启动时崩溃或无法启动?那么它可能链接到证书应用程序的“默认”生命周期),或者显示错误消息?
  • 我更新了代码以显示整个块,就像我说的那样是标准代码。当然它不应该泄漏,但确实如此。是的,我知道您可以多次执行反向定位 - 这不会导致内存泄漏。不知道“证书应用程序的默认生命周期”是什么 - 我不认为这是相关的,因为如果您监控消耗的内存,可以在几分钟内看到泄漏......令人费解......
  • 关于Apple故意泄漏以防止频繁请求的阴谋论:如果他们想限制您的速率,他们不会因为让它失败并报告错误而感到内疚(就像他们一样用于限制并发请求)。
  • 嗨 Rob 和 Larme,虽然我仍然遇到应用程序在终止后无法重新启动的问题,但自从我在 3 月份发布此项目以来,我已经阅读了大量内容。感谢你们的反馈/cmets - 我目前正在从各个角度考虑是否可以解决应用程序在 4 天后无法重新启动的问题。我现在也认为它与应用程序生命周期有关,但我不确定此时会发生什么。我会尽快发布更新(希望我渴望发布我的应用程序)。

标签: swift memory-leaks reverse-geocoding clgeocoder


【解决方案1】:

我不认为你的记忆问题来自这个例程。我运行了 144 次迭代(持续 12 分钟,每 5 秒触发一次;相当于每 5 分钟运行 12 小时),但没有发现明显的内存增长:

(每个“兴趣点”路标都标记了反向地理编码请求。)

不过有一些观察:

  1. 我不会为每个请求都实例化一个新的CLGeocoder,而是实例化一个,然后对该实例执行重复的reverseGeocodeLocation 调用。

  2. 关于CLGeocoder 的限制,主要限制是您不能同时执行地理编码请求(如果您尝试这样做,它只会因错误而失败)。

  3. 关于为什么你的记忆力会增长,我无法对此发表评论,因为我无法重现你描述的行为。也许您打开了一些调试选项(malloc 堆栈跟踪、僵尸等)。这将产生您描述的行为。关闭所有可选的内存诊断并执行发布构建。

  4. 如果内存在增长,有两个有用的工具:

    • “调试内存图”将向您显示已创建的对象,您可以查看在何处建立了哪些强引用。

    • 如果您使用 Instruments 中的“Leaks”工具(也提供“Allocations”工具)分析应用程序,您可以准确检查在何处创建了哪些分配。
       

      李>

    更多信息请见How to debug memory leaks when Leaks instrument does not show them?

  5. 通常,如果我们要查找用户位置的更新,visits servicesignificant change service 是更有效的方法。为什么每五分钟这样做一次会耗尽用户的电池电量,因为用户将有很长的时间不会移动。让设备在用户移动时通知您的应用,而不是不断轮询。


这不是很相关,但这是我生成上图的代码:

var counter = 0
var simulatedTimeInterval: TimeInterval = 0
let maxCounter = 12 * 60 / 5 // how many geocode request in 12 hours, every five minutes

let formatter: DateComponentsFormatter = {
    let formatter = DateComponentsFormatter()
    formatter.unitsStyle = .full
    return formatter
}()

func startTimer() {
    Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { [weak self] timer in
        guard
            let self = self,
            self.counter < self.maxCounter
        else {
            timer.invalidate()
            return
        }

        self.counter += 1
        self.simulatedTimeInterval += 60 * 5 // add 5 minutes
        let simulatedTimeString = self.formatter.string(from: self.simulatedTimeInterval)!

        os_signpost(.event, log: pointsOfInterest, name: "Geocode", "Request #%d @ %{public}@", self.counter, simulatedTimeString)

        self.updateLocation()
        self.myReverseGeo()
    }
    print("Running")
}

func updateLocation() {
    currentLatitude += 0.010
    currentLongitude += 0.010
}

func myReverseGeo() {
    let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)

    let geocoder = CLGeocoder()

    geocoder.reverseGeocodeLocation(location) { placemarks, error in
        guard
            error == nil,
            let placemark = placemarks?.first,
            let city = placemark.locality,
            let state = placemark.administrativeArea,
            let country = placemark.country
        else {
            print("Reverse Geocoding failed")
            print(error ?? "No placemarks found")
            return
        }

        print(city, state, country)
    }
}

import os.signpost

let pointsOfInterest = OSLog(subsystem: "Geocode", category: .pointsOfInterest)

【讨论】:

  • 嗨 Rob,首先非常感谢您的回信,以及您所有宝贵的 cmets 和对代码的反馈。自从我发布这个项目以来,我一直在阅读更多关于应用程序生命周期的内容。 Whist问题仍然存在,即。应用程序在大约 4 天后不会重新启动(如果应用程序被向上滑动方法杀死) - 我觉得它与反向地理编码的东西无关。我还在调查。问题是我必须将代码加载到我的设备上,并在应用程序失败之前(通常是 4 天)每天通过终止或进入后台进行测试..
猜你喜欢
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
相关资源
最近更新 更多