swift - 反向地理编码 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!)
}
})
}
}
解决方案
我不认为你的记忆问题来自这个例程。我运行了 144 次迭代(12 分钟,每 5 秒触发一次;相当于每 5 分钟运行 12 小时)并且没有看到明显的内存增长:
(这些“兴趣点”路标中的每一个都标记了一个反向地理编码请求。)
不过有一些观察:
我不会为每个请求实例化一个新请求,而是
CLGeocoder
实例化一个,并使用该实例执行重复reverseGeocodeLocation
调用。关于 的限制
CLGeocoder
,主要限制是您不能同时执行地理编码请求(如果您尝试这样做,它只会因错误而失败)。关于为什么您的记忆力会增长,我无法对此发表评论,因为我无法重现您描述的行为。也许您打开了一些调试选项(malloc 堆栈跟踪、僵尸等)。这将产生您描述的行为。关闭所有可选的内存诊断并执行发布构建。
如果内存在增长,有两个有用的工具:
“调试内存图”将向您显示已创建的对象,您可以查看在何处建立了哪些强引用。
如果您使用 Instruments 中的“Leaks”工具(也提供“Allocations”工具)来分析应用程序,您可以准确检查在哪里创建了哪些分配。
有关更多信息,请参阅Leaks 仪器未显示内存泄漏时如何调试内存泄漏?
一般来说,如果我们正在寻找用户位置的更新,访问服务和重大更改服务是更有效的方法。为什么每五分钟这样做一次会耗尽用户的电池电量,因为用户将在很长一段时间内不移动。让设备在用户移动时告诉您的应用程序,而不是不断地轮询。
这不是很相关,但这是我生成上图的代码:
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)
推荐阅读
- python - 是否可以将参数(数据)传递给散景应用程序?
- php - 填写表格时收到访客信息的电子邮件
- java - 我在华为 AppGallery 上发布我的应用后,Admob 广告没有显示
- javascript - 如何从返回语句中检索谷歌驱动文件的绝对链接?
- c++ - 在 C++ 中使用 {} 声明变量时出错
- javascript - 如何在 POST 请求后正确呈现新元素?
- rust - 使用 rust 从网站上抓取由混淆的 JS 代码生成的数据
- java - 为什么 ResponseBody 和 Jackson ObjectMapper 不返回相同的输出?
- c# - 使用 C# 更新 SQL 表
- kernel - 内核是否跟踪进程在内存中的启动位置或 CPU?