如何在 swift 中使用 NSOperation 中的 CLLocation 委托和 Realm?
how to use CLLocation delegates in NSOperation with Realm in swift?
我正在使用 CLLocation 来确定用户在 PageViewController 中的位置,并且需要使用从 locationManager(_:didUpdateLocations:)
返回的位置写入 Realm 数据库,然后在 PageViewController 的内容中的函数中使用 Realm page.The 内容页面在单独的源文件(视图控制器)中实现。所以:
首先,定位成功,
其次,将位置写入realm,
三、第二步成功后,调用内容页中的函数
并且我使用带有依赖关系的 NSOperationQueue 来控制上述步骤,但是委托函数 locationManager(_:didUpdateLocations:)
似乎从未被调用,并且当代码想要读取领域数据时导致应用程序崩溃:
请看下面我的代码:
PageViewController
class PageViewController: UIPageViewController {
static var isFirstLaunch: Bool = true
let locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = 3000
launchQueue = OperationQueue()
let locationOperation = BlockOperation {
OperationQueue.main.addOperation {
self.locationManager.requestLocation()
}
}
locationOperation.completionBlock = {
print("locationOperation finished, finished:\(locationOperation.isFinished)") //"locationOperation finished, finished:true" in console
}
let firstLaunchOperation = BlockOperation {
PageViewController.isFirstLaunch = false
}
firstLaunchOperation.completionBlock = {
print("firstLaunchOperation finished, finished:\(firstLaunchOperation.isFinished)") //"firstLaunchOperation finished, finished:true" in console
}
firstLaunchOperation.addDependency(locationOperation)
launchQueue.addOperation(locationOperation)
launchQueue.addOperation(firstLaunchOperation)
}
ContentViewController(in a separate source file)
class ContentViewController: UIViewController {
let defaultRealm = try! Realm()
let config = Realm.Configuration(fileURL: Bundle.main.url(forResource: "areaID", withExtension: "realm"), readOnly: true)
override func viewDidLoad() {
super.viewDidLoad()
UISetup()
autolayoutView()
if !PageViewController.isFirstLaunch {
upateWeather()
}
}
func upateWeather() {
let userArea = defaultRealm.objects(UserArea.self)
let place = userArea.first?.areas
let locality = place?[currentPage].locality
let subLocality = place?[currentPage].subLocality
let areaIDRealm = try! Realm(configuration: config)
let results = areaIDRealm.objects(RealmObject.self).filter("locality = '\(locality!)' AND subLocality = '\(subLocality!)'")
//Crashed here! fatal error: unexpectedly found nil while unwrapping an Optional value. I opened the realm, and no locality and subLocality write in the Realm.
}
}
CLLocationManagerDelegate
extension PageViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let currentLocation: CLLocation = locations[0]
let geocoder: CLGeocoder = CLGeocoder()
if currentLocation.horizontalAccuracy > 0 {
geocoder.reverseGeocodeLocation(currentLocation, completionHandler: {(placeMarks, error) in
if error == nil {
guard let placemark = placeMarks!.first else { return }
let userArea = self.defaultRealm.objects(UserArea.self)
func locationToRealm(place: String, subPlace: String) {
if let gpsLocation = userArea.first?.areas.first {
self.defaultRealm.beginWrite()
gpsLocation.locality = place
gpsLocation.subLocality = subPlace
try! self.defaultRealm.commitWrite()
} else {
try! self.defaultRealm.write {
self.defaultRealm.create(UserArea.self, value: [[["locality": place, "subLocality": subPlace]]])
}
}
}
if let place: String = placemark.locality {
if let subPlace: String = placemark.subLocality {
locationToRealm(place: place, subPlace: subPlace)
} else {
locationToRealm(place: place, subPlace: "---")
}
} else {
if let subPlace: String = placemark.subLocality {
locationToRealm(place: "---", subPlace: subPlace)
}
}
}
})
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {......}
}
如何使用NSOperationQueue来控制代码,一步一步来!非常感谢您的帮助!
好的,我检查了逻辑,我想我能明白发生了什么。
将 self.locationManager.requestLocation()
放入操作队列(尤其是主队列)毫无意义。 According to the documentation、requestLocation()
returns 立即,对位置服务的实际请求发生在系统控制的后台线程中。
当定位服务确定设备位置时,将调用委托方法 didUpdateLocations
,但这很容易花费几秒钟。
与此同时,您正在立即加载 ContentViewController
,这会在位置服务有机会调用委托并将数据首次插入 Realm 之前触发 updateWeather()
.
这是一个完全正常的逻辑模型。用某种加载微调器显示视图控制器,并在位置服务完成后随后更新它是有意义的。因此,你应该确保 updateWeather()
能够工作,即使 Realm 文件是空的,你也应该合并逻辑来更新 UI 一旦 Realm 文件确实有数据。
最简单的方法是检查 locality
和 subLocality
是否 nil
并且仅在它们不是时才执行查询。
func upateWeather() {
let userArea = defaultRealm.objects(UserArea.self)
let place = userArea.first?.areas
let locality = place?[currentPage].locality
let subLocality = place?[currentPage].subLocality
let areaIDRealm = try! Realm(configuration: config)
// Only proceed if the Realm query variables aren't nil
guard let locality = locality, subLocality = subLocality else {
return
}
let results = areaIDRealm.objects(RealmObject.self).filter("locality = '\(locality!)' AND subLocality = '\(subLocality!)'")
//Crashed here! fatal error: unexpectedly found nil while unwrapping an Optional value. I opened the realm, and no locality and subLocality write in the Realm.
}
您需要在委托完成写入 Realm 后再次调用 updateWeather()
以重试。
我正在使用 CLLocation 来确定用户在 PageViewController 中的位置,并且需要使用从 locationManager(_:didUpdateLocations:)
返回的位置写入 Realm 数据库,然后在 PageViewController 的内容中的函数中使用 Realm page.The 内容页面在单独的源文件(视图控制器)中实现。所以:
首先,定位成功,
其次,将位置写入realm,
三、第二步成功后,调用内容页中的函数
并且我使用带有依赖关系的 NSOperationQueue 来控制上述步骤,但是委托函数 locationManager(_:didUpdateLocations:)
似乎从未被调用,并且当代码想要读取领域数据时导致应用程序崩溃:
请看下面我的代码:
PageViewController
class PageViewController: UIPageViewController {
static var isFirstLaunch: Bool = true
let locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = 3000
launchQueue = OperationQueue()
let locationOperation = BlockOperation {
OperationQueue.main.addOperation {
self.locationManager.requestLocation()
}
}
locationOperation.completionBlock = {
print("locationOperation finished, finished:\(locationOperation.isFinished)") //"locationOperation finished, finished:true" in console
}
let firstLaunchOperation = BlockOperation {
PageViewController.isFirstLaunch = false
}
firstLaunchOperation.completionBlock = {
print("firstLaunchOperation finished, finished:\(firstLaunchOperation.isFinished)") //"firstLaunchOperation finished, finished:true" in console
}
firstLaunchOperation.addDependency(locationOperation)
launchQueue.addOperation(locationOperation)
launchQueue.addOperation(firstLaunchOperation)
}
ContentViewController(in a separate source file)
class ContentViewController: UIViewController {
let defaultRealm = try! Realm()
let config = Realm.Configuration(fileURL: Bundle.main.url(forResource: "areaID", withExtension: "realm"), readOnly: true)
override func viewDidLoad() {
super.viewDidLoad()
UISetup()
autolayoutView()
if !PageViewController.isFirstLaunch {
upateWeather()
}
}
func upateWeather() {
let userArea = defaultRealm.objects(UserArea.self)
let place = userArea.first?.areas
let locality = place?[currentPage].locality
let subLocality = place?[currentPage].subLocality
let areaIDRealm = try! Realm(configuration: config)
let results = areaIDRealm.objects(RealmObject.self).filter("locality = '\(locality!)' AND subLocality = '\(subLocality!)'")
//Crashed here! fatal error: unexpectedly found nil while unwrapping an Optional value. I opened the realm, and no locality and subLocality write in the Realm.
}
}
CLLocationManagerDelegate
extension PageViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let currentLocation: CLLocation = locations[0]
let geocoder: CLGeocoder = CLGeocoder()
if currentLocation.horizontalAccuracy > 0 {
geocoder.reverseGeocodeLocation(currentLocation, completionHandler: {(placeMarks, error) in
if error == nil {
guard let placemark = placeMarks!.first else { return }
let userArea = self.defaultRealm.objects(UserArea.self)
func locationToRealm(place: String, subPlace: String) {
if let gpsLocation = userArea.first?.areas.first {
self.defaultRealm.beginWrite()
gpsLocation.locality = place
gpsLocation.subLocality = subPlace
try! self.defaultRealm.commitWrite()
} else {
try! self.defaultRealm.write {
self.defaultRealm.create(UserArea.self, value: [[["locality": place, "subLocality": subPlace]]])
}
}
}
if let place: String = placemark.locality {
if let subPlace: String = placemark.subLocality {
locationToRealm(place: place, subPlace: subPlace)
} else {
locationToRealm(place: place, subPlace: "---")
}
} else {
if let subPlace: String = placemark.subLocality {
locationToRealm(place: "---", subPlace: subPlace)
}
}
}
})
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {......}
}
如何使用NSOperationQueue来控制代码,一步一步来!非常感谢您的帮助!
好的,我检查了逻辑,我想我能明白发生了什么。
将 self.locationManager.requestLocation()
放入操作队列(尤其是主队列)毫无意义。 According to the documentation、requestLocation()
returns 立即,对位置服务的实际请求发生在系统控制的后台线程中。
当定位服务确定设备位置时,将调用委托方法 didUpdateLocations
,但这很容易花费几秒钟。
与此同时,您正在立即加载 ContentViewController
,这会在位置服务有机会调用委托并将数据首次插入 Realm 之前触发 updateWeather()
.
这是一个完全正常的逻辑模型。用某种加载微调器显示视图控制器,并在位置服务完成后随后更新它是有意义的。因此,你应该确保 updateWeather()
能够工作,即使 Realm 文件是空的,你也应该合并逻辑来更新 UI 一旦 Realm 文件确实有数据。
最简单的方法是检查 locality
和 subLocality
是否 nil
并且仅在它们不是时才执行查询。
func upateWeather() {
let userArea = defaultRealm.objects(UserArea.self)
let place = userArea.first?.areas
let locality = place?[currentPage].locality
let subLocality = place?[currentPage].subLocality
let areaIDRealm = try! Realm(configuration: config)
// Only proceed if the Realm query variables aren't nil
guard let locality = locality, subLocality = subLocality else {
return
}
let results = areaIDRealm.objects(RealmObject.self).filter("locality = '\(locality!)' AND subLocality = '\(subLocality!)'")
//Crashed here! fatal error: unexpectedly found nil while unwrapping an Optional value. I opened the realm, and no locality and subLocality write in the Realm.
}
您需要在委托完成写入 Realm 后再次调用 updateWeather()
以重试。