首页 > 解决方案 > 反向地理编码 CLGeocoder 内存泄漏

问题描述

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

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

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

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

我知道你可以调用反向地理定位的频率是有限制的——也许苹果知道它的漏洞,这就是为什么存在限制(以减少人们做我正在做的事情)???

我附上了整个股票标准代码。我已经稍微修改了它,因为我取出了频率元素,现在你可以点击屏幕上的一个按钮来触发反向地理定位……点击大约 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!)

                }
        })

    }

}

进行反向地理定位之前的第一张图片

在多次调用反向地理定位后几分钟

14 分钟后利用率高达 12MB,从 10.7MB 开始 关于如何阻止内存泄漏的任何想法(除了不使用反向地理编码)???

标签: swiftmemory-leaksreverse-geocodingclgeocoder

解决方案


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

在此处输入图像描述

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

不过有一些观察:

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

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

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

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

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

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

    有关更多信息,请参阅Leaks 仪器未显示内存泄漏时如何调试内存泄漏?

  5. 一般来说,如果我们正在寻找用户位置的更新,访问服务重大更改服务是更有效的方法。为什么每五分钟这样做一次会耗尽用户的电池电量,因为用户将在很长一段时间内不移动。让设备在用户移动时告诉您的应用程序,而不是不断地轮询。


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

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)

推荐阅读