Skip to content

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.kts

Gradle 配置

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次)5ms12ms2.4x
带捕获变量(1M次)5ms18ms3.6x
异步Block(1k次)45ms52ms1.16x
GCD dispatch(1k次)28ms30ms1.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 可构建流畅、响应迅速的用户体验。