普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-Swift社区周报

SwiftUI 新容器视图 API 深度解析:轻松构建自定义布局

作者 展菲
2024年9月30日 11:13

前言

自 SwiftUI 的第一个版本发布以来,它就拥有了几种容器视图。最常用的有 HStack、VStack、List 等。今年,Apple 引入了新的 API,使我们能够以全新的方式构建自定义容器视图。本周,我们将学习 SwiftUI 新的分解 API 的优势。

容器视图

容器视图就是一个可以包含其他视图的视图。我们可以使用 @ViewBuilder 闭包轻松定义一个容器视图。以下是一个示例:

struct Card<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        VStack {
            content
        }
        .padding()
        .background(Material.regular, in: .rect(cornerRadius: 8))
        .shadow(radius: 4)
    }
}

如上面的例子所示,我们创建了 Card 视图,它是一个用于容纳任何 SwiftUI 视图的容器视图。它使用 @ViewBuilder 闭包包裹了内容,并添加了一个圆角背景和阴影。

struct ContentView: View {
    var body: some View {
        Card {
            Text("Hello, World!")
            Text("My name is Majid Jabrayilov")
        }
    }
}

这个 Card 类型使用起来非常简单。你只需创建一个 Card,并使用闭包提供内容。通过在 Card 容器视图内嵌入不同的视图,你可以在应用的多个屏幕中复用它。

这是使用容器视图的主要优势之一:你可以通过将共享的功能封装在容器视图中,在应用的不同地方重复使用它们。

想了解更多关于 @ViewBuilder 闭包的内容,可以查看我关于 “SwiftUI 中 @ViewBuilder 的强大功能” 的文章。

使用 ViewBuilder

@ViewBuilder 闭包让我们可以轻松地组合多个视图,并将一个视图嵌入到另一个视图中。但是如何从 @ViewBuilder 闭包中提取子视图呢?SwiftUI 引入了新的 API,允许我们重新组合视图。例如,我们可以从通过 @ViewBuilder 闭包构建的内容视图中提取子视图,并根据需要将它们放置。

struct Carousel<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView(.horizontal) {
            LazyHStack {
                ForEach(subviews: content) { subview in
                    subview
                        .containerRelativeFrame(.horizontal)
                }
            }
            .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
        .contentMargins(16)
    }
}

如上面的示例所示,我们使用了带有 subviews 参数的 ForEach 视图,这使我们能够提取内容视图的子视图并对它们进行迭代。

struct ContentView: View {
    var body: some View {
        Carousel {
            Color.yellow
            Color.orange
            Color.red
            Color.blue
            Color.green
        }
    }
}

SwiftUI 使用特定的 Subview 类型来公开提取视图的实例。它符合 View 协议,因此我们仍然可以附加额外的 SwiftUI 视图修饰符。它还为我们提供了 id 属性,这是一个唯一标识符,以及与特定视图关联的容器值。我们将在接下来的文章中更多讨论容器值。

访问子视图

另一种新的 API 允许我们通过索引访问子视图,而不是使用 ForEach 视图进行迭代。

struct Magazine<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView {
            Group(subviews: content) { subviews in
                if !subviews.isEmpty {
                    subviews[0]
                        .padding(.horizontal)
                        .containerRelativeFrame(.vertical) { length, _ in
                            return length / 3
                        }
                }
                
                if subviews.count > 1 {
                    ScrollView(.horizontal) {
                        LazyHStack {
                            ForEach(subviews[1...], id: \.id) { subview in
                                subview
                                    .containerRelativeFrame([.horizontal, .vertical])
                            }
                        }
                        .scrollTargetLayout()
                    }
                    .scrollTargetBehavior(.viewAligned)
                    .contentMargins(16)
                }
            }
        }
    }
}

在上面的示例中,我们使用了带有 subviews 参数的 Group 视图,它允许我们将子视图提取到一个名为 SubviewsCollection 的集合类型中。SubviewsCollection 类型符合 RandomAccessCollection 协议,并为我们提供了通过索引访问的功能。

组合子视图

如你所见,我们使用 Group 视图来分解内容视图,然后以另一种方式组合子视图。我们还利用了 id 参数的功能,允许我们使用 ForEach 视图与普通数据一起工作。

struct ContentView: View {
    var body: some View {
        Magazine {
            Color.yellow
            Color.orange
            Color.red
            Color.blue
            Color.green
        }
    }
}

可运行的 Demo

根据文章内容,我将提供一个可以展示如何使用 SwiftUI 新的容器视图 API 构建自定义视图的简单示例,包含 CardCarouselMagazine 容器视图。

import SwiftUI

// 定义 Card 视图,作为一个基本的容器视图
struct Card<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        VStack {
            content
        }
        .padding()
        .background(Material.regular, in: RoundedRectangle(cornerRadius: 8))
        .shadow(radius: 4)
    }
}

// 定义 Carousel 视图,横向滚动的自定义容器视图
struct Carousel<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView(.horizontal) {
            LazyHStack {
                ForEach(subviews: content) { subview in
                    subview
                        .containerRelativeFrame(.horizontal)
                }
            }
            .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
        .contentMargins(16)
    }
}

// 定义 Magazine 视图,具有垂直和水平组合布局的自定义容器视图
struct Magazine<Content: View>: View {
    @ViewBuilder var content: Content
    
    var body: some View {
        ScrollView {
            Group(subviews: content) { subviews in
                // 第一个子视图为大图
                if !subviews.isEmpty {
                    subviews[0]
                        .padding(.horizontal)
                        .containerRelativeFrame(.vertical) { length, _ in
                            return length / 3
                        }
                }
                
                // 其余子视图为横向滚动小图
                if subviews.count > 1 {
                    ScrollView(.horizontal) {
                        LazyHStack {
                            ForEach(subviews[1...], id: \.id) { subview in
                                subview
                                    .containerRelativeFrame([.horizontal, .vertical])
                            }
                        }
                        .scrollTargetLayout()
                    }
                    .scrollTargetBehavior(.viewAligned)
                    .contentMargins(16)
                }
            }
        }
    }
}

// 主视图,使用自定义容器视图
struct ContentView: View {
    var body: some View {
        VStack {
            // 使用 Card 视图
            Card {
                Text("SwiftUI 容器视图示例")
                    .font(.headline)
                Text("使用 Card 容器轻松复用视图")
            }
            .padding()
            
            // 使用 Carousel 视图
            Carousel {
                Color.yellow
                Color.orange
                Color.red
                Color.blue
                Color.green
            }
            .frame(height: 100)
            .padding()
            
            // 使用 Magazine 视图
            Magazine {
                Color.pink
                Color.purple
                Color.teal
                Color.mint
            }
            .frame(height: 300)
        }
        .padding()
    }
}

// 主应用入口
@main
struct ContainerViewDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

功能概述

  1. Card:一个简单的容器视图,可以包裹任何内容并添加背景和阴影。你可以在应用中的多个地方使用该容器来保持一致的样式。
  2. Carousel:一个横向滚动的容器视图,可以自动排列并展示内容,适合展示横向滑动的图像或视图。
  3. Magazine:一个自定义的容器视图,允许你将第一个子视图设置为大图,其他子视图横向排列展示。类似于杂志布局。

运行这个Demo

此代码展示了如何在 SwiftUI 中构建自定义的容器视图,灵活地将不同的布局封装在容器中,以便在应用中多次复用这些布局模式。

总结

通过使用 SwiftUI 新引入的 API 以及容器视图,你可以轻松构建具有良好复用性的自定义布局,提升应用的开发效率和代码可维护性。

苹果、华为“撞档”上新 | Swift 周报 issue 62

作者 展菲
2024年9月23日 19:16

前言

本期是 Swift 编辑组自主整理周报的第六十二期,每个模块已初步成型。各位读者如果有好的提议,欢迎在文末留言。

Swift 周报在 GitHub 开源,欢迎提交 issue,投稿或推荐内容。目前计划每两周周一发布,欢迎志同道合的朋友一起加入周报整理。

人生两条路,一条在心中,唤作梦想,一条于脚下,叫做现实。Swift社区不扰繁华,不论悲欢,点头于心,踏步向前!👊👊👊

周报精选

新闻和社区:苹果、华为“撞档”上新引海外关注

提案:成员导入可见性提案正在审查。

Swift 论坛:讨论并行计算 DAG / 共享未来

推荐博文:在 Swift 中引入对 Oblivious HTTP 的支持

话题讨论:

有人说智能手机的霸主地位要换主了,你觉得的呢?

上期话题结果

如果真的不支持微信,会不会出现另外一个拥有类似功能的 App?

新闻和社区

苹果、华为“撞档”上新引海外关注

2024 年 9 月 13 日

本周,苹果公司、华为公司相继举行新品发布会,推出各自最新创新产品。中美两家高科技企业“撞档”上新,备受科技界及国际舆论关注,中国科技企业的创新能力引热议。 苹果公司推出了 iPhone 16 系列智能手机和苹果手表等新品。华为公司发布了全球首款三折叠屏手机,在铰链系统、屏幕弯折等方面实现多项技术突破。一些媒体在报道中对两家公司的发展状况及新产品进行对比。

美国“石英”财经网站撰文称,华为自去年 8 月发布 Mate 60 Pro 系列智能手机以来,在中国市场的表现优于苹果。今年 4 月,华为发布的报告称其利润连续第四个季度实现增长,这凸显了华为在美方制裁和打压下展示出的韧性。

“反观苹果,其 iPhone 销量同期下降了 19%,这是自 2020 年新冠疫情以来苹果在中国市场表现最糟糕的一次。今年一季度,苹果在中国智能手机市场的份额从 19.7% 跌至 15.7%。面对华为和其他中国本土智能手机制造商的激烈竞争,苹果不得不下调部分机型在中国市场的价格。”报道称。 美国消费者新闻与商业频道(CNBC)报道称,华为在 2019 年美国对中国科技企业实施制裁打压后遭受重创,如今在智能手机领域强势回归。华为及其他中国智能手机企业已在销售折叠屏手机,而苹果尚未进军该领域。

知名技术市场分析公司卡纳利斯咨询公司研究经理安伯·刘对媒体表示,华为和苹果新品发布时间相近,标志着中国高端市场新一轮竞争浪潮的开始。关键竞争领域将包括高端产品、软件功能和人工智能部署。华为的快速复苏“直接挑战到”苹果在中国市场的表现。中国是苹果的全球第二大市场,占其全球出货量的 20% 以上。

卡纳利斯咨询公司发布的数据显示,今年二季度,苹果在中国市场的出货量被挤出前五,排名退至第六。前五位首次被中国国内手机品牌包揽。

美国网络公司美国在线(AOL)以“苹果公司中国竞争对手华为抢了 iPhone 16 风头”为题撰文称,每年秋季苹果发布新款 iPhone 时,都是智能手机领域的绝对霸主。然而今年,其在中国最大的竞争对手之一华为在努力“改写剧本”。 美国有线电视新闻网报道称,美国此前的限制措施当时让华为的智能手机发展受到沉重打击,但如今华为再次“重返巅峰”,同时还在进军新业务。去年华为推出了一款与特斯拉 Model S 竞争的电动车。华为在人工智能发展方面也有远大抱负。

两家中美企业发布的最新产品也引来一些媒体和业内人士的评测。印度新闻网站“今日商业”撰文称,苹果的 iPhone 16 系列虽然有一些改进,但与上一代相比并没有引入任何重大变化。相比之下,华为的新款折叠屏手机提供了突破性的设计和许多高科技功能。华为一直高度重视折叠屏手机市场,这次发布的新产品进一步巩固了其地位。

路透社报道称,苹果最新发布的 iPhone 16 未能让投资者兴奋,因为大家期待已久的新产品中的人工智能功能仍处于测试模式。而华为推出业界首款三折叠屏手机,在争夺全球智能手机市场主导地位的斗争中继续加码。

科技新闻网站 technology.org 撰文指出,折叠屏手机的兴起反映了消费者对智能手机屏显更加灵活、外形更加新颖有更高的期待。华为推出的三折叠屏手机 Mate XT 等设备展示了功能和美学的融合,这是智能手机设计的未来发展方向。同时,人工智能技术的深度融合也是未来智能手机技术革新的一大重要趋势。Mate XT 体现的领先人工智能技术不仅能提升用户体验,同时能变革使用者与人工智能技术的互动。(来源:新华社)

苹果公司发布新品

2024 年 9 月 10 日

image.png

9月9日,在美国加利福尼亚州丘珀蒂诺市举行的苹果新品发布会后,人们体验新品。 当日,苹果公司举行新品发布会,推出iPhone 16系列智能手机和苹果手表等新品。(来源:光明网)

image.png

现已推出针对自动续期订阅的赢回优惠

2024 年 9 月 10 日

现在,你可以在 App Store Connect 中配置回头客优惠,这是一种针对自动续期订阅的新优惠。借助回头客优惠,你将能触达之前的订阅者,鼓励他们重新订阅你的 App 或游戏。例如,你可以创建提前支付优惠,对于标准续订价格为每年 39.99 美元的订阅项目,前六个月享受 9.99 美元的优惠价。Apple 会根据你的优惠配置,在不同位置向符合条件的顾客显示此类优惠,这些位置包括:

App Store 上的多个位置,包括你的产品页面,以及“Today”、“游戏”和 “App”标签页上的个性化推荐和编辑精选。 你 App 或游戏内的适当位置。

你通过自己的营销渠道分享的直接链接。

“订阅”设置。

在 App Store Connect 中创建回头客优惠时,你需要确定顾客资格,选择地区提供情况,并选取折扣类型。从今年秋季开始, 回头客优惠将向符合条件的顾客显示。

提案

通过的提案

SE-0443 精确控制编译器警告的标志 提案通过审查。该提案已在 第六十一期周报 正在审查的提案模块做了详细介绍。

正在审查的提案

SE-0444 成员导入可见性 提案正在审查。

在 Swift 中,有一些规则决定了是否会将另一个模块中的声明名称视为当前作用域的一部分。例如,如果你使用了 swift-algorithms 包,并且想要使用全局函数 chain(),那么你必须在引用该函数的文件中写上 import Algorithms,否则编译器会认为它超出了作用域:

// 缺少 'import Algorithms'
let chained = chain([1], [2]) // 错误:找不到 'chain' 的作用域

不过,对于成员声明(例如在结构体中声明的方法),其可见性规则却有所不同。当解析成员声明时,即使引入该成员的模块只是通过传递方式导入,成员也会处于作用域内。传递导入的模块可以是在另一个源文件中直接导入的模块,也可以是程序某个直接依赖项的依赖。这种不一致性可以理解为一个微妙的漏洞,而不是有意的设计决策,在很多 Swift 代码中它可能不会引起注意。

然而,当涉及到扩展的成员时,导入规则变得更令人惊讶,因为扩展和其名义类型(nominal type)可以在不同的模块中声明。

该提案通过更改规则,统一了名称查找的行为,使顶级声明和成员使用相同的标准进入作用域。

SE-0445 改进 String.Index 的打印描述 提案正在审查。

此提案符合 String.IndexCustomStringConvertible

Swift论坛

  1. 讨论SwiftIfConfig 库正在取代编译器的 #if 处理

内容大概:

Swift 编译器正在经历一项重大更新,新的 SwiftIfConfig 库将取代编译器中对 #if 指令的处理。这个库是 swift-syntax 包的一部分,目前已完成多个关键改进:

  1. 配置区域的实现:为 IDE 中的 #if 折叠功能提供支持,增强代码覆盖率分析。
  2. 替换 C++ 解析器中的 #if 条件逻辑:使大部分旧的 ParseIfConfig.cpp 代码不再需要,优化了对 #if 条件的解析。
  3. 在 ASTGen 中支持 #if 指令:新解析器通过支持 #if,提升了处理复杂语法结构的能力。
  4. 基于 #if 条件输出语法错误:根据 #if 指令的配置情况来决定哪些语法错误需要打印。
  5. 删除遗留的“内联文本”提取逻辑:进一步简化了编译器中的代码。

接下来的工作是从 C++ 语义 AST 中彻底移除 IfConfigDecl,这一改变不仅能使编译器代码更简洁,还能提高 #if 在不同语法规则中的扩展性。此外,这次更新还会保留一些重要的编译器行为,例如在不活跃的 #if 块中抑制变量未使用的警告,以及抑制 try 和 throw 相关的警告。

在 SourceKit 方面,多个查询已被 swift-syntax 和 swift-format 工具取代,更新会废弃旧的查询并在下一个 Swift 版本中完全移除这些处理#if的查询。与此类似,Swift 编译器前端的 swift-indent 模式也将被移除,因其功能有限,且已有更现代的工具(如 swift-format)可供使用。

总体而言,这次更新将 Swift 编译器中的 #if 处理逻辑迁移到 swift-syntax 库中,极大简化了主编译器的代码基础,标志着一大进步。

  1. 提议重新审视允许更多非标识符字符的反引号分隔标识符

内容大概:

本文提出再次允许在反引号(backticks)中使用包含空格和其他非标识符字符的标识符。过去曾提出过类似的提案(SE-0275),但被拒绝。本文试图基于新的信息和使用经验重新审视该提案。

  1. 描述性测试命名:

之前的拒绝理由之一是希望测试框架能够提供不同的方法来为测试用例附加字符串。虽然新的 swift-testing 框架已经实现了这一点,但当前的方法要求用户为测试命名两次,这不仅冗余,还引入了不一致。例如:

@Test("tapping pushes the nav stack")
func tappingPushesTheNavStack() {
    // 测试代码...
}

这种方法导致测试报告和测试 UI 中使用描述性名称,但调试器、回溯以及代码导航工具仍使用函数声明名。为了避免这种不一致,本文建议允许函数名直接使用描述性名称,例如:

@Test func `tapping pushes the nav stack`() {
    // 测试代码...
}
  1. 模块命名:

在大型代码库中,模块命名是一个挑战。当前的做法是将模块构建目标路径转换为有效的标识符名,例如:

import my_cool_project_ui_navigation

但这种做法增加了自动化工具的复杂性,特别是在处理依赖关系和导入管理时。本文建议允许直接使用包含非标识符字符的路径作为模块名,例如:

import `my/cool/project/ui/navigation`

这将简化导入语句并减少自动化工具的复杂性。

  1. 其他注意事项:

提案还讨论了与工具链的边缘情况和潜在的未来方向。例如,建议通过定义一个字符集来限制反引号中的允许字符,以增强对未来 Unicode 扩展的适应性。

总结:该提案旨在简化代码中的测试命名和模块命名,同时减少不必要的复杂性,并提高代码的可读性和一致性。

  1. 讨论如果没有办法拦截“fatalError”,则会对服务器造成危害

内容大概:

在服务器端使用Swift时,fatalError 无法被拦截是有害的。虽然通过将功能隔离到Docker容器中,并自动重启失败的容器,可以在一定程度上缓解问题,但在生产环境中,代码无论多么理想化,都会因各种原因导致崩溃。Swift早期编译器版本中的无效代码、缺失库调用导致的致命错误、内存泄漏等问题,都可能导致容器崩溃。

有两种应对严重错误的思路:一种是类似Erlang的“快速失败”方法,失败时生成新的轻量级进程再次执行代码;另一种是更常见的方法,假设大多数异常不会破坏内存,程序可以继续运行。fatalError() 的设计假设这些错误不符合程序模型,并且假设大多数代码在隔离的进程中运行。这种方法是否适用于服务器端的Swift,尤其是在使用结构化并发时,仍然有待验证。

总的来说,Swift需要根据不同的应用场景来调整对严重错误的处理策略,特别是在服务器环境下。

  1. 讨论并行计算 DAG/共享未来?

内容大概:

本文讨论了如何在 Swift 中并行化计算任务。具体任务可以描述为以下函数:

func compute(_ input: [Key]) -> [Key: Value]

该函数的结果包含所有输入中的键,还会生成一些在计算过程中发现的额外键。存在一个依赖有向无环图(DAG),描述所有值之间的依赖关系。

通过一个简单的例子展示了计算的形式,例如计算整数n的阶乘并将其映射到字典中:

func compute(_ input: [Int]) -> [Int: Int] {
    var r: [Int: Int] = [:]

    func fact(_ x: Int) -> Int {
        if let y = r[x] { return y; }
        let y = x == 0 ? 1 : x * fact(x - 1)
        r[x] = y
        return y
    }

    for z in input { _ = fact(z) }
    return r
}

在这个讨论中提出了一种可能的并行化方案:在最终的for循环中为每个输入元素启动并行任务。同时,考虑到DAG的结构,一个键的值的计算可能依赖于另一个键的值,如果该键的值已经在另一个线程中开始计算,那么该计算可能会暂停等待。这种情况类似于共享Future系统。

此外,作者希望在每个线程中批量处理计算结果,并且只在批量足够大时将结果合并到最终结果中,以减少同步的开销。同时,允许某些键值对被重复计算,以避免使用共享的Future系统。

本次的讨论希望通过讨论找到合适的并行计算解决方案。

  1. 讨论关于“间接枚举”语义的澄清

内容大概:

最近作者在工作项目中建议将一些枚举标记为 indirect,以减少它们占用的栈内存。然而,在解释 indirect 实际上做了什么时,我发现很难清楚地说明,除了引用编译器内部机制之外。

官方文档中关于 indirect enum 的唯一提及是“递归枚举”,这是最常见的使用场景。然而,还有其他有效的使用场景,论坛上也经常讨论这些,但从官方文档来看,这是否是预期用途并不明确。

作者认为值得更明确地记录 indirect 在底层实际做了什么,并提到它在内存优化场景中的作用。虽然语言本身从未“官方”保证某些内容的内存分配方式,但了解其在不同场景下的行为会有所帮助。

例如,一个常见的场景是链表类型使用 indirect enum 实现。编译器可能足够智能,可以证明某个链表在函数内不逃逸,从而将堆分配转化为动态栈分配。

另一个例子是全局常量的值是 indirect enum,目前生成的代码会执行堆分配,但理论上可以将这些间接情况放入静态空间。

此外,由于 indirect 的情况是不可变的,因此不能对这些盒子的引用标识做出任何保证,除非使用不安全的技巧来观察其标识。

推荐博文

在 Swift 中引入对 Oblivious HTTP 的支持

摘要: 这篇官方文章介绍了 Swift 中对 Oblivious HTTP(OHTTP)的支持,并发布了新的 SwiftNIO 包 SwiftNIO Oblivious HTTP。Oblivious HTTP 通过加密 HTTP 请求并结合第三方中继服务,保护客户端的身份信息,增强隐私性,避免暴露诸如 IP 地址等数据。

SwiftNIO Oblivious HTTP 包提供两个主要库:

ObliviousHTTP:实现了 RFC 9292 中定义的二进制 HTTP 编码方案和 Oblivious HTTP。 ObliviousX:提供加密功能的 API,支持 Oblivious HTTP 及其他数据的加密。 文章通过代码示例演示了如何使用这些库进行 HTTP 消息的序列化、反序列化和加密解密。还提到了未来的开发计划,包括与 SwiftNIO 更好的集成、对其他 Swift 类型的支持,以及对分块 OHTTP 的支持。

SwiftNIO Oblivious HTTP 仍处于早期开发阶段,期待社区的反馈和贡献。。

Swift 中的任务和任务组

摘要: 这篇博客介绍了 Swift 中的任务(Task)和任务组(Task Group),并讲解了它们的使用方法及相关高级技巧。

任务(Task) 是 Swift 并发编程的一部分,允许在非并发环境中创建并发任务,任务在创建后立即运行。文章展示了如何创建任务、处理任务的错误和取消任务。Swift 提供了 Task.checkCancellation() 主动抛出错误终止任务,或通过 Task.isCancelled 检查任务是否被取消。还介绍了如何设置任务优先级和任务的生命周期状态(运行中、暂停、取消和完成)。

任务组(Task Group) 用于组合并发执行的多个任务,并等待所有任务完成后返回结果。通过 withTaskGroupwithThrowingTaskGroup 可以创建任务组,并发执行任务。文章提供了如何处理任务组中的错误、如何避免数据竞争,并展示了取消任务组的用法。

最后,作者强调了避免并发修改任务组的操作,推荐使用 cancelAll() 来取消任务组中的所有子任务,以及 addTaskUnlessCancelled() 来确保任务组未被取消时才添加新任务。。

递归枚举在 Swift 中的妙用

摘要: 这篇博客介绍了 Swift 中递归枚举的使用及其优势。递归枚举允许枚举的某些情况包含自身实例,适用于建模层次化或递归结构的数据,如文件系统。通过 indirect 关键字,Swift 可以安全地处理递归引用,避免内存问题。

文章首先展示了如何用递归枚举实现文件系统模型,并引入了文件、文件夹和别名的概念。然后通过代码示例,展示了如何使用递归枚举创建嵌套文件结构,并递归计算文件夹中的总项目数。

此外,文章还解释了在引用自身时如何正确使用 indirect 关键字,并指出当引用通过集合类型(如数组)实现时,不需要 indirect 标记。

话题讨论

近期苹果公司推出了iPhone 16系列智能手机和苹果手表等新品。华为公司发布了全球首款三折叠屏手机,在铰链系统、屏幕弯折等方面实现多项技术突破,展示了功能和美学的融合。有人说智能手机的霸主地位要换主了,你觉得的呢?

1.华为也不过是一个高个子小兵罢了,想比肩苹果,可笑可笑。 2.苹果手机近年来创新科技不足,被华为超越是迟早的事。 3.于消费者而言,霸主更替并无坏处,品牌产商有足够的压力和动力才能创造更好的产品。

关于我们

Swift社区是由 Swift 爱好者共同维护的公益组织,我们在国内以微信公众号的运营为主,我们会分享以 Swift实战SwiftUlSwift基础为核心的技术内容,也整理收集优秀的学习资料。

特别感谢 Swift社区 编辑部的每一位编辑,感谢大家的辛苦付出,为 Swift社区 提供优质内容,为 Swift 语言的发展贡献自己的力量。

Apple 新品发布会亮点有哪些 | Swift 周报 issue 61

作者 展菲
2024年9月20日 19:21

前言

本期是 Swift 编辑组自主整理周报的第六十一期,每个模块已初步成型。各位读者如果有好的提议,欢迎在文末留言。

Swift 周报在 GitHub 开源,欢迎提交 issue,投稿或推荐内容。目前计划每两周周一发布,欢迎志同道合的朋友一起加入周报整理。

其实根本就没有什么假如,每个人的人生都不会重新设计。Swift社区也有梦想,也懂现实,生气不如争气,改变胜过抱怨。

周报精选

新闻和社区:苹果 9 月 10 日举行今年最重磅新品发布会,iPhone 16 亮相?都有哪些看点?

提案:精确控制编译器警告的标志的提案正在审查中

Swift 论坛:提议讨论整数泛型参数

推荐博文:Swift 中的 Typed Throws

话题讨论:

如果微信不支持 iPhone 16,你选微信还是 Apple

上期话题结果

根据投票结果可以看出,超过一半的朋友希望 Apple 能更好地将 AI 与 Swift 和 Xcode 集成。期待 iPhone 16 的发布带来亮眼的新功能。

新闻和社区

苹果据称正洽谈投资 OpenAI 英伟达也有意跟投

2024 年 8 月 30 日

据媒体援引消息人士报道,两大科技巨头苹果公司和英伟达均有意投资人工智能(AI)研究公司 OpenAI。

周三有消息称,OpenAI 正在洽谈新一轮融资,计划以超过 1000 亿美元的估值筹集数十亿美元资金,风投公司兴盛资本(Thrive Capital)将领投此轮融资,投资达到 10 亿美元。此外,作为 OpenAI 最大股东,微软也将参与这轮融资。

知情人士称,苹果正就投资 OpenAI 进行谈判,英伟达也已讨论过加入对 OpenAI 的最新融资。据悉,英伟达商谈在 OpenAI 新一轮融资中投入 1 亿美元。

据悉,OpenAI 首席财务官 Sarah Friar 周三一份备忘录中表示,该公司正寻求新的融资,但没有透露具体细节。Friar 称,OpenAI 将利用这笔融资获得更多的计算能力,并为其他运营费用提供资金。

目前尚不清楚苹果和微软计划投资多少金额。如果相关谈判取得成功,这意味着全球最有价值的三家科技公司都将成为 OpenAI 的股东。

OpenAI 成立之初是一家非营利组织,该公司在 2019 年转为一家营利性初创企业。自那以来,微软共向 OpenAI 投资了 130 亿美元,拥有后者 49% 的股份。

苹果在今年 6 月的全球开发者大会(WWDC)上宣布推出苹果智能(Apple Intelligence)系统,该公司还官宣了和 OpenAI 的合作关系。

苹果当时宣布,准备在今年晚些时候将 OpenAI 的聊天机器人 ChatGPT 集成到系统平台中,包括 iOS 18、iPadOS 18 和 macOS Sequoia,由 GPT-4o 提供支持。

苹果多年来大量投资了代工伙伴,部分原因是为了确保其设备零部件的供应,但该公司很少投资初创企业。

相比之下,作为 AI 龙头股的英伟达在投资领域更为活跃。自 2023 年以来,英伟达旗下投资部门 NVentures 已投资了十多家AI制药企业。(来源:科技板日报)

消息称苹果公司服务部门将裁员约百人,波及 Apple Books / Apple News 等业务

2024 年 8 月 28 日

北京时间8月28日上午,据彭博社援引知情人士消息称,苹果公司宣布在其服务部门裁员约 100 人。

当地时间周二,苹果公司通知了受影响的员工,这些员工分别在高级副总裁 Eddy Cue 的服务部门的几个不同团队工作。此次裁员包括一些工程师职位,受到影响最多的是负责苹果 Apple Books 图书应用和苹果书店的团队。而与此同时,其他服务团队也在裁员,包括运营 Apple News 的团队。

报道称,Apple Books 已不再是公司的重点工作,也不再被视为服务阵容的“重要组成部分”。据知情人士透露,随着时间的推移,Apple Books 应用程序仍有望获得新的功能,但 Apple News 的裁员不意味其“不再受到重视”。

裁员在苹果公司并不常见。据IT之家此前报道,今年 4 月,苹果向美国加利福尼亚州提交的必要文件显示公司永久裁员 600 多人。外界认为,可能是苹果放弃汽车项目之后,2000 多人的开发团队中部分转岗到苹果其它项目,而这 600 多人被苹果裁员。(来源:IT之家)

苹果 9 月 10 日举行今年最重磅新品发布会,iPhone 16 亮相?都有哪些看点?

2024 年 8 月 23 日

知名科技记者马克·古尔曼当地时间 8 月 23 日发文称,苹果公司计划在 9 月 10 日举行今年最大的产品发布会,届时该公司将推出最新款 iPhone、Watch 和 AirPods。

古尔曼援引知情人士消息表示,虽然具体时间尚未公布,但该公司正在为这一天做准备。发布会之后,新款手机预计将于 9 月 20 日正式上市销售,这与苹果近年来的做法一致。

马克·古尔曼此前曾表示,有知情人士向他透露,苹果的洛杉矶新店正在为 9 月 16 日那一周开业全力准备。众所周知,苹果公司喜欢在新产品上市之际开新店。

按照以往规律,苹果在 9 月 10 日开完发布会后,要等到周五( 9 月 13 日)开启预订,那么一周后新 iPhone 正式上市的日子( 9 月 20 日)正好赶上洛杉矶新店开业。

对于新一代iPhone,有业内人士称,总体而言,iPhone 16 和 iPhone 15 不会有太大不同。上月苹果在 iPhone 15 的 Pro 以上机型“限量”上线了个人智能化系统 Apple Intelligence,iPhone 16 也将拥有这一先进人工智能(AI)技术加持的工具。此外,iPhone 16 的 Pro 机型屏幕会更大,会拥有新的照相功能,例如用于拍照的专用按钮。

iPhone 16 ‌机型将搭载配合 Apple Intelligence 使用的更强大芯片 A18,整个 iPhone 16 ‌系列都将支持新的 iOS 18‌。

iPhone 16 Pro 和 Pro Max 的显示屏会稍微大一些,尺寸分别为 6.3 英寸和 6.9 英寸。

整个 iPhone 16‌ 系列机型都将有操作按钮(Action Button),这将是基本款的 iPhone 首次采用该按钮。

整个 iPhone 16 ‌系列机型都将新增一个拍照按钮 Capture Button,位于右侧电源键下方。该按钮类似于数码相机上的快门按钮,轻按该按钮可自动对焦,让拍摄更精准,重按该按钮可直接完成拍照,用户在该按钮左右滑动可缩放画面,该按钮可切换拍照和录影功能。

iPhone 16 Pro 和 Pro Max 将配备 4800 万像素的超广角摄像头,像素较 iPhone 5 的 1200 万大幅提升。

相比 iPhone,苹果智能手表和耳机的变化可能更大。媒体称,Apple Watch Series 10 将比前代更薄,但屏幕更大。苹果将更新中低端的 AirPods 产品线,将首次在中端 AirPods 上提供降噪功能,将自 2019 年以来首次更新入门级的 Airpods。

对于苹果公司来说,这次产品发布非常关键,因为最近几个季度苹果的智能手机和可穿戴设备销量一直不太理想。

如果新的 iPhone 16 在 9 月 20 日上市,这意味着该设备的部分收入将计入第四财季财报,苹果预计这一财季营收将同比增长 5%。

而更大的销售份额则需要在下一个财季才能实现,分析师预测,在那个时期,恰逢假日季节,苹果营收将大幅增长 7%,达到 1284 亿美元。

在今年 6 月的年度开发者大会上,苹果公布了一项期待已久的人工智能战略,即将其最新人工智能技术整合到一系列应用程序中,并将 OpenAI 的聊天机器人 ChatGPT 引入其设备。

可穿戴设备也将迎来一些重大变化。据悉,Apple Watch Series 10 系列的型号将会更薄,但会配备更大的屏幕。预计新手表的起售价为 399 美元,并提供两种尺寸选择。

苹果还在更新其 AirPods 产品线,预计将推出新的中低端版本。据悉,这是苹果首次在中端 AirPods 上提供主动降噪 (ANC) 功能,而入门级型号则将进行 2019 年以来的首次更新。

该公司还计划今年晚些时候开始将其 Mac 电脑过渡到 M4 处理器,但 Mac 的更新通常要到 iPhone 发布会后一个月左右。

提案

正在审查的提案

SE-0443 精确控制编译器警告的标志 提案正在审查。

本提案引入了新的编译器选项,允许对编译器如何发出特定警告进行精细控制,使它们可以作为警告或错误来处理。

Swift论坛

  1. 提议整数泛型参数

内容大概

这篇讨论围绕通过引入整数泛型参数来增强 Swift 处理具有内联存储的固定容量数据结构的能力。对于像嵌入式 Swift 这样注重性能的代码库,这特别有用,在这些场景中,开发人员需要高效且类型安全的数据结构。目前,Swift 的局限性需要繁琐且容易出错的技术,例如手动创建具有特定元素数量的结构体,并使用不安全的操作进行索引。

动机:

这个提议的动机源于 Swift 缺乏对使用内联存储的固定大小或固定容量集合的原生支持。这些集合对于编译时容量固定的场景非常重要,例如固定大小的数组、具有固定桶数的哈希表或具有特定维度的矩阵。通过允许在泛型中使用整数参数,相同的实现可以在不同大小之间重用,从而提高代码的可重用性并确保更强的类型安全性。

提议的解决方案:

该提案引入了通过整数参数对泛型类型进行参数化的概念。这是通过在泛型参数列表中使用新的语法实现的,其中整数通过 let 关键字声明。例如:

struct Vector<let N: Int, T> { /* 实现待定 */ }

在这里,N 是一个字面整数参数,允许使用固定大小 N 定义 Vector 类型。这使得可以创建固定维度的矩阵或向量,例如:

struct Matrix4x4 {
    var matrix: Vector<4, Vector<4, Double>>
}

详细设计:

  1. 声明语法:
    • 整数泛型参数使用 let 关键字后跟参数名称和类型 Int 声明。
    • 例如: struct Matrix<let N: Int, let M: Int> { ... }
  2. 实例化:
    • 这些类型可以使用字面整数值实例化,也可以通过引用周围泛型上下文中的整数参数来实例化。
  3. 约束和算术:
    • 提案允许在整数参数之间添加约束,例如确保两个参数相等或某个参数等于特定值。
    • 未来的方向包括支持在这些泛型参数中进行算术运算,从而实现更复杂的关系,如组合向量或矩阵。
  4. 类型兼容性:
    • 该设计通过扩展现有的泛型语法保持源代码兼容性。为值参数引入 let 确保了清晰性,并防止在将来引入不同类型的值参数时产生歧义。
  5. ABI 兼容性:
    • 该提案不会影响现有 Swift 代码的 ABI,因为它引入了新功能,而不是改变现有行为。

未来方向:

该提案是 Swift 中迈向更高级功能的基础步骤:

  1. 固定大小和固定容量集合:
    • 未来的提案可能会引入充分利用此功能的标准库类型,例如固定大小的数组或缓冲区。
  2. 泛型参数中的算术:
    • 能够使用整数参数进行算术运算,例如将两个数组的维度相加以创建一个新数组。
  3. 非整数值泛型参数:
    • 扩展该功能以允许其他类型的值参数,可能会在类型级别支持更丰富的约束和操作。
  4. 参数包:
    • 未来的工作可能还会探索整数参数的可变参数包,这可能允许定义具有任意维度的多维数组或矩阵。
  1. 讨论[GSoC-2024] Visual Studio Code 中 Swift 宏的扩展(详细文章) - Lokesh.T.R(Alex Hoppen 和 Adam Fowler)

内容大概

Lokesh T.R 是来自印度 Chennai Vel Tech 大学的二年级学生,他参与了 2024 年的 Google Summer of Code (GSoC) 项目,专注于在 Visual Studio Code (VS Code) 中扩展 Swift 宏。在导师 Alex Hoppen 和 Adam Fowler 的指导下,Lokesh 的主要目标是实现一个代码操作,允许用户在 VS Code 编辑器中直接查看 Swift 宏的展开内容。

项目概述

该项目旨在通过引入一个新功能来增强 VS Code 中的 Swift 开发体验,即通过调用“Expand Macro”代码操作,将 Swift 宏展开并在“peeked”编辑器窗口中显示生成的内容。该功能将包含在与 Swift 6.1 捆绑的 SourceKit-LSP 中,并将在下一个 VS Code Swift 扩展版本中发布。

主要成就

  1. 引入新 LSP 扩展:
    • workspace/peekDocuments: 这个 LSP 扩展允许 SourceKit-LSP 服务器在 VS Code 中的一个 peek 窗口中显示存储在特定位置的内容。
    • workspace/getReferenceDocument: 这个扩展使得 VS Code Swift 扩展可以向 SourceKit-LSP 服务器请求并检索文档(例如 Swift 宏展开)的内容。
  2. 自定义 URL 方案:
    • Lokesh 引入了一个新的自定义 URL 方案 (sourcekit-lsp://),用于编码生成任何形式内容(特别是 Swift 宏展开)所需的数据。引入的第一个文档类型是 swift-macro-expansion。
  3. 在 Peeked 编辑器中的宏展开:
    • 当用户调用“Expand Macro”代码操作时,系统会使用 SourceKitD 为宏展开生成一个参考文档 URL,然后发出 workspace/peekDocuments 请求。这些内容随后将在 VS Code 的 peeked 编辑器中显示。
  4. 语义功能和嵌套宏展开:
    • Lokesh 扩展了项目范围,支持在宏展开预览中进行语义功能(如跳转到定义、悬停时的快速帮助和语法高亮显示)。这是通过重新使用源文件的构建参数来欺骗 SourceKitD,使其为参考文档提供这些功能。
    • 该系统还支持嵌套宏展开,其中在生成的宏展开中存在的宏本身也可以被展开。自定义参考文档 URL 通过允许每个宏展开引用其父级,从而促进了这种嵌套,使嵌套展开得以高效和无缝地进行。
  5. 支持其他基于 LSP 的编辑器:
    • 虽然新 LSP 扩展最初是为 VS Code 设计的,但 Lokesh 致力于为其他基于 LSP 的编辑器(如 Neovim)提供基本的宏展开支持,使用标准的 LSP 请求。这使得这些编辑器能够显示宏展开,尽管形式更为简单,方法是将展开内容存储在临时文件中然后显示出来。

实施细节

实施过程中涉及到几个关键组件的紧密互动:

  • VS Code-Swift 扩展: 作为 VS Code 和 SourceKit-LSP 之间的桥梁。
  • SourceKit-LSP: 提供必要的编辑器功能,并通过 LSP 进行通信。
  • SourceKitD: 嵌入在 Swift 编译器中的后台服务,提供宏展开所需的原始数据和操作。

为了实现项目目标,Lokesh 和他的导师引入了上述的自定义 LSP 扩展和 URL 方案,并确保这些功能平滑地集成到现有的 Swift 工具链和 VS Code 扩展中,注重用户体验和性能。

未来方向

  1. 测试用例和文档:
    • Lokesh 计划实施涵盖各种嵌套宏级别所有语义功能的全面测试用例。这包括在 VS Code 中的端到端测试,以确保“Expand Macro”代码操作在真实世界场景中的稳健
  1. 提议Non-Discardable throwing Tasks

内容大概

在 Swift 中,使用包含抛出函数的任务时,常见的问题是错误可能会被无意中忽略。这是因为当前的 Task 初始化器默认允许丢弃错误,这可能导致严重且难以发现的 bug。

当前行为:

当前的 Task 初始化器定义如下:

extension Task where Failure == any Error {
    @discardableResult
    public init(priority: TaskPriority? = nil, operation: sending @escaping @isolated(any) () async throws -> Success)
}

由于使用了 @discardableResult 属性,开发者可以忽略错误而不会收到任何警告,这可能导致关键错误被无意中忽略。

提议的更改:

提议中建议移除 Task 初始化器中的 @discardableResult 属性。这样一来,忽略错误将不再是默认行为,从而提高代码的安全性,确保开发者在必要时有意识地选择忽略错误。

例如,要有意忽略错误,开发者需要显式地写成:

_ = Task {
    try await dontCareIfThisThrowsOrNot()
}

这一更改旨在引入警告,帮助发现潜在的 bug,提高代码的安全性,并减少无意中的错误处理遗漏。

论据:

作者指出,当前默认允许丢弃错误的行为在原始的 Swift 结构化并发提案中并没有得到充分的理由支持。鉴于它带来的问题,重新审视这一设计决策可能会提升代码的可靠性。

一个相关的讨论主题也指出了同样的问题,这表明 Swift 社区中的其他开发者也遇到了类似的挑战。

  1. 讨论最小化结构体的 MemoryLayout 大小/步幅有哪些好处?

内容大概

在 Swift 中,结构体的内存布局由其存储属性的声明顺序决定。通过重新排列这些属性,可以减少结构体的 MemoryLayout.size 和 MemoryLayout.stride。这种优化看似有吸引力,但它的实际好处是什么呢?

主要好处:

  1. 存在类型优化:
  • Swift 可以优化存在类型(Any 或 any P),如果类型的大小为 3 个机器字或更小(在 64 位机器上为 24 字节),则可以将值内联存储在存在类型中。通过优化结构体的布局以符合这一限制,可以避免在频繁使用这些类型时进行不必要的内存分配。
  1. “大型”类型的阈值:
  • 如果类型超过 5 个机器字(在 64 位机器上为 40 字节),Swift 会将其视为“大型”并以不同方式传递,通常通过堆栈分配并传递指针。虽然这本身并不坏,但当复制该值时,这会导致增加的内存操作,从而影响性能。
  1. 汇编级别的考虑:
  • 一些开发者希望使用更少的寄存器来表示类型(例如,2 个寄存器而不是 4 个),以实现代码大小和性能上的微小提升。然而,对于大多数开发者来说,这些问题微不足道,不值得过多关注。
  1. 讨论并发 101

内容大概

  1. 挂起点和线程切换

在 Swift 中,当使用 async/await 处理异步函数时,挂起点是关键。这些是你代码中的特定点,由 await 标记,函数可以在这些点暂停或“挂起”。在挂起期间,执行此函数的线程可以切换到其他任务。这一过程是 Swift 并发能力的一部分。

如果你的代码没有这些挂起点(即没有 await 调用),函数将从头到尾执行而不暂停。在这种情况下,线程将被“阻塞”在当前任务上,无法进行其他工作。重要的是,Swift 不会引入超出 await 显式定义的附加挂起点。这一设计确保了开发者可以依赖同步函数的可预测行为,并清楚地区分同步和异步函数。

  1. 并发 vs. 并行
  • 并发 指的是任务可以独立进展的能力。然而,这并不一定意味着这些任务在同一时间运行。并发允许任务的执行交错进行,即多个任务正在进行中,但它们可能不会同时执行。
  • 并行 则是任务实际在同一时间运行,通常是在不同线程上。当系统有多个线程可用时,可以实现任务的同时执行。

使用 async/await 时,并不能保证并行执行。例如,如果你在一个任务中有多个 await 调用(如 await a; await b; await c),它们将在该任务中按顺序执行,而不是并行执行。并行执行只能在有多个任务在不同线程上运行时发生。

  1. 执行交错

交错是一种允许任务共享单线程时间的技术。在像 Swift 的并发模型这样的协作多任务环境中,这一点非常重要。当一个任务到达 await 点并挂起时,它会让出线程,允许其他任务运行。这种交错使得即使在单线程环境中,任务也能被视为并发执行,尽管它们并未并行执行。

因此,并发是并行的前提条件,但它们并不相同。一个系统可以有并发任务,但这些任务可能不会并行执行,取决于运行时如何调度它们。

  1. 函数执行和误解

一个常见的误解是“并发函数”的概念。实际上,函数本身并不并发;而是任务的执行可以是并发的。单个线程一次只能执行一段代码。这意味着虽然多个任务可以同时进行,但在给定线程上一次只能执行一个任务。

async/await 的好处在于它简化了异步代码的结构,使其比传统的回调或续传样式更易于阅读和推理。它还通过允许任务暂停和让出控制来实现更好的资源管理,而不是不必要地阻塞线程。

总之,async/await 在 Swift 中通过挂起点、执行交错和明确区分同步与异步函数来支持高效的并发。虽然它支持并行,但这取决于多个线程的可用性和运行时的调度决策。

推荐博文

SwiftUI 中 View 之间的通信 【macOS App】

摘要: 这篇博客探讨了 SwiftUI 中视图之间的通信方式,并与前端框架如 Vue 和 React 进行对比。文章首先介绍了父视图传递消息给子视图的方式,类似于 Vue 的 props,但使用方法有所不同。接着,文章讲述了子视图如何向父视图传递消息,分别介绍了使用 @Binding、闭包(Closure)回调函数、以及共享 Observable 数据的方式。最后,文章讨论了在多层级视图间使用环境变量传递消息的方式,类似于 Vue 的 Inject/Provide 和 React 的 context。作者总结认为,尽管 SwiftUI 提供了多种视图通信方式,但其灵活性和易用性与前端框架相比仍有所不足,尤其是在复杂场景下依然需要结合使用 Combine。

Swift 协议的进化之路:深入理解不透明类型与装箱类型

摘要: 这篇博客详细探讨了 Swift 协议中 some 和 any 两种关键字的使用及其背后的类型系统。文章首先解释了为什么使用带有关联类型的协议时会出现编译错误,并介绍了三种解决方案:使用 any 关键字创建装箱类型(Boxed Type)、使用泛型、以及使用 some 关键字创建不透明类型(Opaque Type)。

文章深入分析了不透明类型的作用,强调 some 关键字在编译时确定类型,但对外隐藏具体实现,适合简化 API 复杂性并提升性能。同时,文章也讨论了装箱类型的特性,指出 any 关键字在运行时确定具体类型,虽然灵活但有一定的性能开销。

作者强调了在不同场景下选择 some 或 any 的重要性,指出 some 适合编译时能确定类型的场景,而 any 则适合需要运行时确定类型的情况。了解这两者的区别对编写高效、简洁的 Swift 代码至关重要。

Swift 中的 Typed Throws

摘要: 这篇博客介绍了 Swift 6.0 中的新功能“Typed Throws”。过去,Swift 的错误抛出机制无法显式指定可能抛出的错误类型,开发者需要手动检查函数实现以了解错误类型。Swift 6.0 引入了“typed throws”,允许开发者在定义函数时明确指定抛出的错误类型,使代码更具可预测性和类型安全性。这一特性不强制要求使用,可以与旧的错误抛出方式兼容。博客还讨论了向后兼容性,解释了 Swift 编译器如何将未指定错误类型的抛出函数转换为使用 any Error 类型。通过这项改进,开发者能够更清晰地处理特定的错误类型,提高代码的可读性和安全性。

话题讨论

如果微信不支持 iPhone 16,你选微信还是 Apple

1、微信 2、Apple

关于我们

Swift社区是由 Swift 爱好者共同维护的公益组织,我们在国内以微信公众号的运营为主,我们会分享以 Swift实战SwiftUlSwift基础为核心的技术内容,也整理收集优秀的学习资料。

特别感谢 Swift社区 编辑部的每一位编辑,感谢大家的辛苦付出,为 Swift社区 提供优质内容,为 Swift 语言的发展贡献自己的力量。

提升代码调试技巧:从思维到实践

作者 展菲
2024年9月6日 12:14
调试是软件开发中的关键部分,它不仅帮助开发者找到代码中的错误,还能提高代码质量和开发效率。本文将从调试工具的使用、错误信息的解读、问题定位以及如何培养高效的调试思维等方面,系统地介绍提升调试技巧

如何在 CI/CD 过程中实施高效的自动化测试和部署

作者 展菲
2024年8月1日 20:41
在持续集成(CI)和持续交付(CD)过程中,自动化测试和部署是提高软件交付速度和质量的关键。本文将详细介绍如何选择适合的CI/CD工具,配置自动化构建和测试流程,制定全面的测试策略,并确保部署环
❌
❌