You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS Swift命令行应用执行Task语句时崩溃问题求助

Swift命令行项目中async/await的Task执行崩溃问题

我在练习Swift的async/await用法时,遇到应用执行Task语句就崩溃的问题,错误信息如下:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

捕获异常后调用栈没有提供有效排查信息,我怀疑是COMMAND LINE模板不支持这种简单形式的Task,相关代码如下:

//
//  main.swift
//

import Foundation

struct User {
    var username: String
    var movies = [Movie]()
}

struct Movie {
    var title: String
    var rating: Double?
}

class ManagerUsingAsync {
    func getUsers() async throws -> [User] {
        let allIsGood = true // Bool.random()

        if allIsGood {
            let users = ["mary", "casey", "theo", "dick"].map { User(username: $0) }
             return users
        } else {
            throw URLError(.cannotConnectToHost)
        }
    }

    func getUsersMovies(username: String) async throws -> [Movie] {
        let allIsGood = true // Bool.random()

        if allIsGood {
            let movies = ["breaking away", "diner", "the great escape"].map { Movie(title: $0) }
            return movies
        } else {
            throw URLError(.cannotCreateFile)
        }
    }

    func getMovieRating(title: String) async -> Double? {
        let allIsGood = true // Bool.random()

        if allIsGood {
            return Double.random(in: 1...10)
        }

        return nil
    }
}

func testAsync() async -> [User] {
    var _users = [User]()
    let manager = ManagerUsingAsync()

    do {
        _users = try await manager.getUsers()
        for var user in _users {
            user.movies = try await manager.getUsersMovies(username: user.username)
            for var movie in user.movies {
                movie.rating = await manager.getMovieRating(title: movie.title)
            }
        }

    } catch (let error) {
        print(error.localizedDescription)
    }

    return _users
}

print("TEST ASYNC/AWAIT...")

Task {
    let users = await testAsync()
    for user in users {
        print( "Username: \(user.username) Movies: \(user.movies)" )
    }
}

问题原因

根本原因不是命令行模板不支持Task,而是命令行程序的主函数执行完毕后会立刻退出进程,而Task是异步执行的,主进程结束时异步任务还在运行,导致内存被释放,触发EXC_BAD_ACCESS错误。

解决方法

1. 可靠等待异步任务完成

修改最后一段代码,通过Task的value属性等待任务执行完成:

print("TEST ASYNC/AWAIT...")

let task = Task {
    let users = await testAsync()
    for user in users {
        print( "Username: \(user.username) Movies: \(user.movies)" )
    }
}

// 等待异步任务完成,避免主进程提前退出
try? await task.value

2. 修复值类型修改无效的问题

代码里还有一个逻辑问题:User和Movie是值类型,遍历的时候修改的是副本,原数组里的对象不会被更新。可以改成两种方式:

  • 将User和Movie改为引用类型(class)
  • 用索引遍历修改原数组元素:
func testAsync() async -> [User] {
    var _users = [User]()
    let manager = ManagerUsingAsync()

    do {
        _users = try await manager.getUsers()
        // 用索引遍历修改原数组的User
        for i in 0..<_users.count {
            var user = _users[i]
            user.movies = try await manager.getUsersMovies(username: user.username)
            // 用索引遍历修改当前User的movies数组
            for j in 0..<user.movies.count {
                var movie = user.movies[j]
                movie.rating = await manager.getMovieRating(title: movie.title)
                user.movies[j] = movie
            }
            _users[i] = user
        }

    } catch (let error) {
        print(error.localizedDescription)
    }

    return _users
}

内容的提问来源于stack exchange,提问作者mobibob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:20:40