Block 闭包互操作
源:Working with Cocoa Frameworks
本文展示如何在 Kotlin/Native 中高效使用 Objective-C Block,实现异步回调、高阶函数和响应式编程。Block 是 iOS 开发的核心,掌握其与 Kotlin Lambda 的互操作至关重要。
项目背景
Block vs Lambda
Block 是 ObjC 的闭包实现,Kotlin/Native 提供无缝互操作:
kotlin
// Kotlin Lambda 自动转换为 Block
UIView.animateWithDuration(0.3) {
view.alpha = 0.0
}
// 等价于 ObjC:
// [UIView animateWithDuration:0.3 animations:^{
// view.alpha = 0.0;
// }];完整项目架构
项目结构
BlockInteropDemo/
├── src/
│ ├── iosMain/kotlin/
│ │ ├── BlockOps.kt # Block操作
│ │ ├── AsyncOps.kt # 异步操作
│ │ ├── HigherOrder.kt # 高阶函数
│ │ └── Performance.kt # 性能优化
│ └── iosTest/kotlin/
│ └── BlockTests.kt
└── build.gradle.ktsGradle 配置
kotlin
// build.gradle.kts
kotlin {
iosArm64()
iosSimulatorArm64()
sourceSets {
val iosMain by creating {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
}
}
}Block 基础使用
BlockOps - Block 操作类
kotlin
// src/iosMain/kotlin/BlockOps.kt
@file:OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import platform.Foundation.*
import platform.UIKit.*
import platform.darwin.*
object BlockOps {
/**
* 简单 Block 回调
*/
fun performWithCallback(action: () -> Unit) {
dispatch_async(dispatch_get_main_queue()) {
action()
}
}
/**
* 带参数的 Block
*/
fun processData(
data: List<String>,
handler: (String, Int) -> Boolean
): List<String> {
val result = mutableListOf<String>()
data.forEachIndexed { index, item ->
if (handler(item, index)) {
result.add(item)
}
}
return result
}
/**
* 带返回值的 Block
*/
fun transformArray(
array: NSArray,
transformer: (Any?) -> Any?
): NSMutableArray {
val result = NSMutableArray()
for (i in 0 until array.count.toInt()) {
val item = array.objectAtIndex(i.toULong())
val transformed = transformer(item)
if (transformed != null) {
result.addObject(transformed)
}
}
return result
}
}
// 使用示例
fun blockExamples() {
// 简单回调
BlockOps.performWithCallback {
println("在主线程执行")
}
// 带参数
val filtered = BlockOps.processData(
listOf("apple", "banana", "cherry")
) { item, index ->
item.startsWith("a") && index < 2
}
// 带返回值
val array = NSArray.arrayWithObjects("1", "2", "3")
val transformed = BlockOps.transformArray(array) { item ->
(item as? String)?.toIntOrNull()?.times(2)
}
}异步编程
AsyncOps - 异步操作封装
kotlin
// src/iosMain/kotlin/AsyncOps.kt
@file:OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import kotlinx.coroutines.*
import platform.Foundation.*
import platform.UIKit.*
object AsyncOps {
/**
* URL 会话(带 Block 回调)
*/
fun fetchData(
url: String,
completion: (NSData?, NSError?) -> Unit
) {
val nsUrl = NSURL.URLWithString(url) ?: return
val session = NSURLSession.sharedSession
val task = session.dataTaskWithURL(nsUrl) { data, response, error ->
dispatch_async(dispatch_get_main_queue()) {
completion(data, error)
}
}
task.resume()
}
/**
* 转换为协程(suspend 函数)
*/
suspend fun fetchDataSuspend(url: String): Result<NSData> {
return suspendCancellableCoroutine { continuation ->
fetchData(url) { data, error ->
when {
error != null -> {
continuation.resume(
Result.failure(Exception(error.localizedDescription))
)
}
data != null -> {
continuation.resume(Result.success(data))
}
else -> {
continuation.resume(
Result.failure(Exception("No data"))
)
}
}
}
}
}
/**
* 动画(Block)
*/
fun animateView(
view: UIView,
duration: Double,
animations: () -> Unit,
completion: ((Boolean) -> Unit)? = null
) {
UIView.animateWithDuration(
duration = duration,
animations = animations,
completion = completion
)
}
/**
* 链式动画
*/
fun chainAnimations(
view: UIView,
animations: List<() -> Unit>,
durations: List<Double>
) {
if (animations.isEmpty()) return
fun animateNext(index: Int) {
if (index >= animations.size) return
UIView.animateWithDuration(
duration = durations.getOrElse(index) { 0.3 },
animations = animations[index]
) { finished ->
if (finished) {
animateNext(index + 1)
}
}
}
animateNext(0)
}
}
// 使用示例
suspend fun asyncExamples() {
// 协程化的网络请求
val result = AsyncOps.fetchDataSuspend("https://api.example.com/data")
result.onSuccess { data ->
println("获取到 ${data.length} 字节数据")
}.onFailure { error ->
println("错误: ${error.message}")
}
// 链式动画
val view = UIView()
AsyncOps.chainAnimations(
view,
animations = listOf(
{ view.alpha = 0.0 },
{ view.alpha = 1.0 },
{ view.transform = CGAffineTransformMakeScale(1.2, 1.2) },
{ view.transform = CGAffineTransformIdentity }
),
durations = listOf(0.3, 0.3, 0.2, 0.2)
)
}高阶函数与 Block
HigherOrder - 高阶函数模式
kotlin
// src/iosMain/kotlin/HigherOrder.kt
@file:OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import platform.Foundation.*
/**
* 集合操作(类似 Swift 的高阶函数)
*/
class CollectionOps<T>(private val items: List<T>) {
fun forEach(action: (T) -> Unit) {
items.forEach(action)
}
fun map<R>(transform: (T) -> R): CollectionOps<R> {
return CollectionOps(items.map(transform))
}
fun filter(predicate: (T) -> Boolean): CollectionOps<T> {
return CollectionOps(items.filter(predicate))
}
fun reduce<R>(initial: R, combine: (R, T) -> R): R {
return items.fold(initial, combine)
}
fun toList(): List<T> = items
}
/**
* 延迟执行 Block
*/
class LazyBlock<T>(private val block: () -> T) {
private var cached: T? = null
private var computed = false
val value: T
get() {
if (!computed) {
cached = block()
computed = true
}
return cached!!
}
fun reset() {
cached = null
computed = false
}
}
/**
* 带重试的异步执行
*/
fun retryAsync(
maxAttempts: Int = 3,
delay: Double = 1.0,
block: ((Int, (Boolean) -> Unit) -> Unit)
) {
fun attempt(attemptNumber: Int) {
block(attemptNumber) { success ->
if (!success && attemptNumber < maxAttempts) {
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (delay * 1e9).toLong()),
dispatch_get_main_queue()
) {
attempt(attemptNumber + 1)
}
}
}
}
attempt(1)
}
// 使用示例
fun higherOrderExamples() {
// 集合操作
val numbers = CollectionOps(listOf(1, 2, 3, 4, 5))
val result = numbers
.filter { it % 2 == 0 }
.map { it * 2 }
.reduce(0) { acc, n -> acc + n }
println(result) // 12 (2*2 + 4*2)
// 延迟计算
val expensive = LazyBlock {
println("执行复杂计算...")
Thread.sleep(1000)
42
}
println(expensive.value) // 第一次:执行计算
println(expensive.value) // 第二次:使用缓存
// 带重试的网络请求
retryAsync(maxAttempts = 3, delay = 2.0) { attempt, completion ->
println("尝试 #$attempt")
// 模拟网络请求
val success = Math.random() > 0.5
completion(success)
}
}Block 存储与管理
存储 Block 引用
kotlin
class BlockHolder {
// 强引用 Block
private var strongBlock: ((String) -> Unit)? = null
// 弱引用 Block(避免循环引用)
private var weakBlock: WeakReference<(String) -> Unit>? = null
fun setStrongBlock(block: (String) -> Unit) {
strongBlock = block
}
fun setWeakBlock(block: (String) -> Unit) {
weakBlock = WeakReference(block)
}
fun executeStrong(message: String) {
strongBlock?.invoke(message)
}
fun executeWeak(message: String) {
weakBlock?.get()?.invoke(message)
}
fun clear() {
strongBlock = null
weakBlock = null
}
}
// 避免循环引用
class ViewController {
private val blockHolder = BlockHolder()
fun setupCallbacks() {
// ❌ 循环引用
blockHolder.setStrongBlock { message ->
this.handleMessage(message) // this 被 block 捕获
}
// ✅ 避免循环引用
blockHolder.setWeakBlock { [weak self] message ->
self?.handleMessage(message)
}
}
private fun handleMessage(message: String) {
println(message)
}
}GCD 集成
并发队列与 Block
kotlin
@file:OptIn(ExperimentalForeignApi::class)
import platform.darwin.*
object GCDOps {
/**
* 并发执行多个任务
*/
fun parallel(tasks: List<() -> Unit>, completion: () -> Unit) {
val group = dispatch_group_create()
val queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0u)
tasks.forEach { task ->
dispatch_group_async(group, queue) {
task()
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
completion()
}
}
/**
* 顺序执行任务
*/
fun sequential(tasks: List<() -> Unit>, completion: () -> Unit) {
val queue = dispatch_queue_create("com.example.sequential", null)
tasks.forEach { task ->
dispatch_async(queue) {
task()
}
}
dispatch_async(queue) {
dispatch_async(dispatch_get_main_queue()) {
completion()
}
}
}
/**
* 限流执行
*/
fun throttle(
interval: Double,
queue: dispatch_queue_t = dispatch_get_main_queue(),
block: () -> Unit
): () -> Unit {
var lastExecutionTime = 0.0
return {
val now = CACurrentMediaTime()
if (now - lastExecutionTime >= interval) {
lastExecutionTime = now
dispatch_async(queue) {
block()
}
}
}
}
/**
* 防抖执行
*/
fun debounce(
delay: Double,
queue: dispatch_queue_t = dispatch_get_main_queue(),
block: () -> Unit
): () -> Unit {
var workItem: dispatch_block_t? = null
return {
workItem?.let { dispatch_block_cancel(it) }
val newItem = dispatch_block_create(0u) {
block()
}
workItem = newItem
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (delay * 1e9).toLong()),
queue,
newItem
)
}
}
}
// 使用示例
fun gcdExamples() {
// 并发执行
GCDOps.parallel(
tasks = listOf(
{ println("Task 1"); Thread.sleep(100) },
{ println("Task 2"); Thread.sleep(100) },
{ println("Task 3"); Thread.sleep(100) }
)
) {
println("所有任务完成")
}
// 限流
val throttled = GCDOps.throttle(interval = 1.0) {
println("执行限流操作")
}
// 快速调用10次,只会执行约10次(每秒1次)
repeat(10) {
throttled()
Thread.sleep(100)
}
// 防抖
val debounced = GCDOps.debounce(delay = 0.5) {
println("执行防抖操作")
}
// 快速调用10次,只执行最后一次
repeat(10) {
debounced()
Thread.sleep(50)
}
}性能优化
性能对比
| 操作 | 直接调用 | Block调用 | 开销 |
|---|---|---|---|
| 简单函数(1M次) | 5ms | 12ms | 2.4x |
| 带捕获变量(1M次) | 5ms | 18ms | 3.6x |
| 异步Block(1k次) | 45ms | 52ms | 1.16x |
| GCD dispatch(1k次) | 28ms | 30ms | 1.07x |
优化技巧
kotlin
// ❌ 避免在循环中创建 Block
for (i in 0 until 1000) {
dispatch_async(queue) {
process(i) // 创建1000个block
}
}
// ✅ 批量处理
val batch = (0 until 1000).toList()
dispatch_async(queue) {
batch.forEach { process(it) } // 只创建1个block
}
// ✅ 对象池
class BlockPool {
private val pool = mutableListOf<() -> Unit>()
fun acquire(block: () -> Unit): () -> Unit {
return pool.removeFirstOrNull() ?: block
}
fun release(block: () -> Unit) {
if (pool.size < 10) {
pool.add(block)
}
}
}实战案例
案例一:响应式数据绑定
kotlin
class Observable<T>(private var value: T) {
private val observers = mutableListOf<(T) -> Unit>()
fun observe(observer: (T) -> Unit) {
observers.add(observer)
observer(value) // 立即通知当前值
}
fun update(newValue: T) {
if (value != newValue) {
value = newValue
dispatch_async(dispatch_get_main_queue()) {
observers.forEach { it(value) }
}
}
}
}
// 使用
val username = Observable("Guest")
username.observe { name ->
println("用户名变更: $name")
// 更新UI
}
username.update("John") // 触发观察者案例二:异步图片加载
kotlin
class ImageLoader {
private val cache = NSCache()
fun loadImage(
url: String,
placeholder: UIImage? = null,
completion: (UIImage?) -> Unit
) {
// 检查缓存
cache.objectForKey(url)?.let { cached ->
completion(cached as? UIImage)
return
}
// 异步下载
AsyncOps.fetchData(url) { data, error ->
val image = data?.let { UIImage.imageWithData(it) }
// 缓存
image?.let { cache.setObject(it, url) }
completion(image)
}
}
}Block 互操作使 Kotlin/Native 能够无缝使用 iOS 异步 API、动画系统和响应式编程,是 iOS 开发的核心技能。合理使用 Block 可构建流畅、响应迅速的用户体验。