如何在 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 documentationrequestLocation() returns 立即,对位置服务的实际请求发生在系统控制的后台线程中。

当定位服务确定设备位置时,将调用委托方法 didUpdateLocations,但这很容易花费几秒钟。

与此同时,您正在立即加载 ContentViewController,这会在位置服务有机会调用委托并将数据首次插入 Realm 之前触发 updateWeather() .

这是一个完全正常的逻辑模型。用某种加载微调器显示视图控制器,并在位置服务完成后随后更新它是有意义的。因此,你应该确保 updateWeather() 能够工作,即使 Realm 文件是空的,你也应该合并逻辑来更新 UI 一旦 Realm 文件确实有数据。

最简单的方法是检查 localitysubLocality 是否 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() 以重试。