Skip to content

iOS 平台调试技巧

在 KMP 项目中调试 iOS 端代码是常见挑战。本文提供实用的调试技巧和工具使用方法。

Xcode 中调试 Kotlin 代码

设置断点

虽然 Kotlin 代码编译为 Native 代码,但仍可以在 Xcode 中设置断点:

  1. 在 Xcode 中打开 iOS 项目
  2. 找到调用 Kotlin 代码的 Swift 文件
  3. 在 Swift 调用 Kotlin 方法的行设置断点
  4. 运行应用,当断点命中时,查看变量值

使用 LLDB 调试

bash
# 在 Xcode Debugger Console 中

# 查看 Kotlin 对象
(lldb) po user
(lldb) po user.name

# 查看所有变量
(lldb) frame variable

# 继续执行
(lldb) continue

dSYM 符号化

确保 Framework 包含调试符号:

kotlin
// shared/build.gradle.kts
kotlin {
    iosTargets.forEach { target ->
        target.binaries.framework {
            isStatic = true
            
            // 保留调试信息
            freeCompilerArgs += listOf(
                "-Xadd-light-debug=enable"
            )
        }
    }
}

日志调试

使用 Kermit 日志

kotlin
// commonMain
import co.touchlab.kermit.Logger

class UserRepository {
    private val logger = Logger.withTag("UserRepository")
    
    suspend fun getUser(id: String): User {
        logger.d { "Fetching user: $id" }
        
        return try {
            val user = api.fetchUser(id)
            logger.i { "User fetched: ${user.name}" }
            user
        } catch (e: Exception) {
            logger.e("Failed to fetch user", e)
            throw e
        }
    }
}

在 iOS 端查看日志:

swift
// Xcode Console 输出
// [UserRepository] Fetching user: 123
// [UserRepository] User fetched: Alice

NSLog 输出

kotlin
// iosMain
import platform.Foundation.NSLog

fun debugLog(message: String) {
    NSLog("[KMP] %@", message)
}

崩溃调试

捕获 Kotlin 异常

swift
// Swift
do {
    let user = try await repository.getUser(id: "123")
    print(user.name)
} catch let error as NSError {
    print("Error domain: \(error.domain)")
    print("Error code: \(error.code)")
    print("Error message: \(error.localizedDescription)")
    print("Stack trace: \(error.userInfo)")
}

Crashlytics 集成

kotlin
// iosMain
import cocoapods.FirebaseCrashlytics.FIRCrashlytics

class IosCrashReporter {
    fun logException(exception: Throwable) {
        FIRCrashlytics.crashlytics().recordError(
            NSError(
                domain = "KMP",
                code = -1,
                userInfo = mapOf(
                    "message" to exception.message,
                    "stackTrace" to exception.stackTraceToString()
                )
            )
        )
    }
}

内存泄漏调试

使用 Instruments

  1. 在 Xcode 中选择 Product → Profile
  2. 选择 Leaks instrument
  3. 运行应用并观察内存泄漏

检查循环引用

kotlin
// 避免循环引用
class ViewModel {
    private var listener: (() -> Unit)? = null
    
    fun setListener(listener: @escaping () -> Unit) {
        self.listener = listener
    }
    
    fun cleanup() {
        listener = null  // 清理引用
    }
}
swift
// Swift - 使用 weak self
viewModel.setListener { [weak self] in
    self?.updateUI()
}

网络调试

Charles Proxy 抓包

  1. 配置 iOS 模拟器使用代理
  2. 安装 Charles 证书
  3. 查看 Kotlin/Native 发起的网络请求

Ktor Client 调试

kotlin
HttpClient {
    install(Logging) {
        logger = object : Logger {
            override fun log(message: String) {
                NSLog("[Ktor] %@", message)
            }
        }
        level = LogLevel.ALL
    }
}

性能分析

Time Profiler

使用 Xcode 的 Time Profiler 分析 Kotlin 代码性能:

  1. Product → Profile → Time Profiler
  2. 运行应用
  3. 查看 Kotlin 函数调用耗时

自定义性能监测

kotlin
inline fun <T> measureTime(tag: String, block: () -> T): T {
    val start = System.currentTimeMillis()
    return block().also {
        val duration = System.currentTimeMillis() - start
        Logger.d { "$tag took ${duration}ms" }
    }
}

// 使用
val users = measureTime("fetchUsers") {
    repository.getUsers()
}

常见问题排查

问题 1:Framework 未找到

dyld: Library not loaded: @rpath/shared.framework/shared

解决方案

  1. 检查 Xcode 项目中 Framework Search Paths
  2. 确认 Framework 已正确嵌入:Embed & Sign

问题 2:符号未解析

Undefined symbol: _OBJC_CLASS_$_SharedKt

解决方案

  1. 清理并重新构建 Kotlin Framework:
bash
./gradlew clean
./gradlew :shared:linkDebugFrameworkIosArm64
  1. 在 Xcode 中 Clean Build Folder (Cmd+Shift+K)

问题 3:方法未导出

Kotlin 函数在 Swift 中不可见。

解决方案

kotlin
// 确保类和函数是 public
class UserRepository {
    fun getUser(id: String): User { }  // ✅ public
}

// 或使用 @ObjCName
@ObjCName("KMPUserRepository")
class UserRepository { }

调试工具推荐

工具用途平台
Xcode Debugger断点调试iOS
Instruments性能分析、内存泄漏iOS
Charles Proxy网络抓包跨平台
Kermit日志管理KMP
Crashlytics崩溃报告跨平台

掌握这些调试技巧,可以快速定位和解决 iOS 平台的问题,提升开发效率。