我有一个KMM项目,需要访问用户的位置.我的LocationClient是用Kotlin 编写的.

一切正常,但在将Kotlin从1.8.20更新到1.9.20之后,我看到了奇怪的行为.

我像预期的那样得到了最初的几个位置,但随后代理就停止将位置发回.我通常在它停止之前得到5到6个位置,从来不会超过10个.我已经在模拟器和真实设备上测试过了,得到了同样的结果.

在SWIFT端,开始发送位置的触发器在一个"onspecar"块中,来回导航会触发另几个位置通过,但随后它们会再次停止.

当位置停止时,手机仍然认为应用程序正在监听位置,并继续显示适当的隐私通知.当我导航离开并停止监听位置时,这将被正确删除.

位置客户端:

actual class LocationProviderImpl : LocationProvider {
     private val locationManager = CLLocationManager()

    private class LocationDelegate : NSObject(), CLLocationManagerDelegateProtocol {
        var onLocationUpdate: ((Location?) -> Unit)? = null
        override fun locationManager(manager: CLLocationManager, didUpdateLocations: List<*>) {
//This is correctly called for the first few locations then stops
            didUpdateLocations.firstOrNull()?.let {
                val location = it as CLLocation
                location.coordinate.useContents {
                    onLocationUpdate?.invoke(Location(latitude, longitude, it.altitude))
                }
            }
        }

        override fun locationManager(manager: CLLocationManager, didFailWithError: NSError) {
//This never gets called
            onLocationUpdate?.invoke(null)
        }
    }
override fun getCurrentLocationAsFlow(): Flow<Location?> = callbackFlow {
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = kCLDistanceFilterNone
        val locationDelegate = LocationDelegate()
        locationDelegate.onLocationUpdate = { location ->
            trySend(
                location
            ).isSuccess
        }
        locationManager.delegate = locationDelegate
         locationManager.startUpdatingLocation()
       awaitClose {
//This is never called
            locationManager.stopUpdatingLocation()
        }
    }.flowOn(Dispatchers.IO)

    fun requestBackground() {
        locationManager.allowsBackgroundLocationUpdates = true
    }

    fun stopBackground() {
        locationManager.allowsBackgroundLocationUpdates = false
    }
    fun stopLocationUpdates() {
        locationManager.stopUpdatingLocation()
    }
}

它的用法如下:

object GpsService {

    private lateinit var locationClient: LocationProviderImpl
    private var initialized = false
    private var serviceScope = CoroutineScope(Dispatchers.IO)
 
 fun initialize(appModule: AppModule) {
        if (!initialized) {
            locationClient = LocationProviderImpl()
}
}
    

  fun startLocationListener() {
  locationClient.getCurrentLocationAsFlow().cancellable()
            .catch { e ->
                e.printStackTrace()
            }
            .mapNotNull { it }
            .onEach { location ->
                // send location to view
                _state.update {
                    it.copy(
                        currentLocation = location,
                        formattedAltitude = getFormattedAltitude(location.altitude)
                    )
                }
     }.launchIn(serviceScope)
    }

我没有收到任何错误或警告(并且到处都设置了断点/println()S),也没有命中任何错误或CATCH块,我只是在前几个位置之后停止获取任何位置.这一切都在Kotlin 1.8.20上运行得很好!

推荐答案

Kotlin有它自己的内存模型,但当您甚至从Kotlin代码创建Objc对象时,它们遵循Objc内存模型.

在iOS世界中,名为delegate的字段通常(总是与系统框架一起)存储对对象的弱引用-这意味着该对象不负责对象的生命周期,因此当该对象被销毁时,该属性自动设置为空.这样做是为了防止保留循环.有关更多详细信息,请参见this question.如果你计划从Kotlin实现更多的iOS功能,请查阅一些关于自动引用计数(ARC)的文章.

在你的代码中,一旦awaitClose暂停了函数的执行,locationDelegate就会被释放.

所以你需要把它储存在某个地方.例如,在类变量中.

对于您当前的代码,这可能很棘手,因为可以从不同的位置多次调用getCurrentLocationAsFlow,因此旧的委托将被新的调用覆盖-您可以考虑在init期间创建一个委托并存储为let,并向该对象添加onLocationUpdate个处理程序.

Ios相关问答推荐

缺少预期的键:';NSPrival yCollectedDataTypes';

使用CGAffineTransform旋转视图只起作用一次吗?

如何使用 SwiftData 从列表中删除子项目?

SwiftUI:.toolbar 不适用于 UITextView

在 React Native iOS 中加载多个 JS 包

为什么 Swift 不在启动的同一线程上恢复异步函数?

如何在 SwiftUI 中创建两个大小相同的正方形占据整个屏幕宽度?

弹出视图后使所有背景 UI 模糊

Xcode 不支持 iOS 15.6

SwiftUI - ScrollView 不显示底部文本

SwiftUI DatePicker 格式在每个设备上都不一样,如何让它总是显示相同的 Select 版本?

按钮在 SwiftUI 中没有采用给定的高度和宽度

检测 iPhone 应用程序中是否存在摄像头?

NSURLConnection 的 Xcode 4 警告未使用表达式结果

导航控制器自定义过渡动画

以编程方式创建按钮并设置背景图像

iOS UITextView 或 UILabel 带有可点击的动作链接

如何计算 Swift 数组中元素的出现次数?

在 SKScene 中设置按钮

UIGestureRecognizer 阻止子视图处理touch 事件