阅读视图

发现新文章,点击刷新页面。

Swift 单元测试突破口:如何优雅测试私有方法?

这里每天分享一个 iOS 的新知识,快来关注我吧

image.png

前言

在编写单元测试时,我们通常希望测试代码的业务逻辑。常用的测试编写格式是 GWT(Given-When-Then),其中在 "When" 步骤中调用我们想要测试的代码。然而,如果你曾写过测试用例,你可能遇到过无法测试私有方法的问题。

这是因为私有属性或方法的作用域仅限于其所在的类,因此我们无法在测试类中访问这些属性或方法。

例如,如果我们有一个类,其中包含一个私有方法,如下所示:

class SomeClass {
    private func somePrivateMethod() {
        // 一些逻辑代码
    }
}

尝试在单元测试中调用 somePrivateMethod,会发现无法访问,并会产生编译错误:“'somePrivateMethod' 由于 'private' 保护级别而无法访问”,这很容易理解。

class SomeClassTests: XCTestCase {
    func testSomePrivateMethod() {
        let testTarget = SomeClass()
        testTarget.somePrivateMethod() // 错误:无法访问私有方法
    }
}

那么我们该如何解决这个问题呢?如果 somePrivateMethod 包含一些业务逻辑,单元测试一定要能够覆盖,那么我们就必须找到其他可行的方法。

方法一:改变访问级别

一种方法是将这些方法的访问级别改为非私有,但这会将这些方法暴露给其他代码,显然这不是一个理想的方案。

public class SomeClass {
    public func somePrivateMethod() {
        // 一些逻辑代码
    }
}

方法二:使用 TestHooks

TestHooks 可以派上用场了。我们可以创建测试钩子并利用它们来访问私有方法和属性,以进行单元测试。TestHooks 只是我们的类持有的一个钩子集,通过这些钩子可以提供对私有方法和属性的访问。

创建 TestHooks 时需要注意的几点:

  1. TestHooks 是在我们希望访问其私有方法或属性的类的扩展中创建的(在我们的例子中是 SomeClass),因为只有这样钩子才能访问那些属性或方法。

  2. 我们想访问的每个属性或方法都需要一个钩子。

  3. 建议将钩子扩展放在 DEBUG 宏中,以避免误用。

实现 TestHook:

以下是 SomeClass 的 TestHook 实现示例:

#if DEBUG    // 在 debug 宏下添加以避免误用,并避免在发布环境中暴露私有方法
extension SomeClass {    // 在类的扩展中编写,我们希望访问其私有方法
    var testHooks: TestHooks {      // testHooks 的实例,通过它我们将在单元测试中访问私有方法
        TestHooks(target: self)      // 使用 self 初始化以访问 self 的私有方法
    }
    struct TestHooks {    // TestHooks 结构体,其中包含我们希望访问的所有属性和方法的钩子
        var target: SomeClass    // 需要访问其私有方法的目标
        func somePrivateMethod() {    // 暴露方法的钩子
            target.somePrivateMethod()    // 暴露该方法
        }
    }
}
#endif

这样一来,我们可以在单元测试文件中通过 testHooks 访问 somePrivateMethod

class SomeClassTests: XCTestCase {
    func testSomePrivateMethod() {
        let testTarget = SomeClass()
        testTarget.testHooks.somePrivateMethod() // 通过 testHooks 访问私有方法
    }
}

结尾

通过这种方式,我们可以在不改变代码结构的情况下,合理地测试私有方法。

TestHooks 提供了一种在测试环境中访问私有方法的途径,同时在发布环境中保持代码的封装性。这是一种在不破坏类封装原则的情况下进行单元测试的有效方法。

希望这能帮助到大家更好地进行 Swift 的单元测试,你对这种方式有什么看法呢?是否还有更好的方案分享,欢迎在评论区留言讨论。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

掌握生死时速:苹果应用加急审核全攻略!

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

image.png

在应用开发的过程中,总会遇到一些突发情况,比如应用版本在上线后出现重大 bug 或崩溃,严重影响了大部分用户的使用体验。这种情况下,及时响应和修复问题至关重要,不仅可以减少用户的不良体验,还能防止应用评分的下降。

然而,即便你已经找到了问题的根源,并准备好了解决方案,想要快速发布更新版本,也并非完全由你决定。

因为首先苹果是不允许热更新的,每个版本发布都必须经过苹果的审核,其次审核是否通过完全取决于苹果,因此加快审核流程就显得尤为重要。

但很多 iOS 开发者可能不知道,其实可以通过“加急审核”来告知 App Review 团队有紧急更新需要审核,从而加快审核速度。

本文将详细介绍如何通过 Apple 开发者门户请求加急审核。

如何请求加急审核

要请求加急审核,你需要前往 developer.apple.com 并登录到你的开发者账号。登录的账号必须具备管理需要请求加急审核的应用的权限。

image.png

  1. 滚动到主页底部,点击“联系我们”链接。

image.png 2. 从列表中选择“应用审核”类别。

Image

  1. 选择“请求加急应用审核”选项。

Image

  1. 点击“联系应用审核团队”。

Image

  1. 填写表单,告知审核团队你希望加快处理的应用信息。

Image

需要在表单中提供的信息包括:

  • 你希望向应用审核团队提出的请求类型:在我们这种情况下,选择“请求加急审核”选项。

  • 请求加急审核的人员姓名。

  • 请求加急审核的人员邮箱。

  • 拥有该应用的组织名称。

  • 应用名称。

  • 需要加急审核的版本平台。

完成以上步骤后,点击最后的“Send”按钮,将请求提交给应用审核团队。

正常情况下,几个小时后会收到苹果审核的结果。

注意事项

虽然加急审核是开发者工具箱中的一个强大工具,是在紧急情况下将应用快速交到用户手中的最佳方式,但它应仅在特殊情况下使用。正如 Apple 在审核表单中提到的:

如果你面临紧急情况,比如修复关键 bug 或发布应用以配合某个事件,可以通过填写此表单请求加急审核。

同时需要注意,太多次的加急审核请求可能会导致 Apple 对你的反感,然后可能会导致对你之后的加急请求不予理会,因此要谨慎使用。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

深度解析!Apple App Site Association 文件背后的秘密和配置攻略

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

image.png

在 iOS 生态系统中,Apple App Site Association(AASA)文件扮演着至关重要的角色。它通过在你的 iOS 应用和网络域之间建立安全且经过验证的链接,实现了诸如通用链接(Universal Links)、共享网络凭证、Handoff 和 App Clips 等功能。

不知道你有没有注意过,当你在手机浏览器上访问知乎、小红书或 YouTube 时,有些链接会让你继续留在浏览器中,而另一些则会直接跳转到对应的应用中?这背后的驱动力正是 AASA 文件。

AASA 文件的创建

Apple App Site Association 文件是一个配置文件,它定义了 URL 的处理方式,并指定它们是打开在浏览器中还是直接链接到应用程序内的内容。

以下是一个基本示例:

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "ABCDE12345.com.example.app",
        "paths": [
          "/",
          "/about-us/",
          "/products/*",
          "/services/detail/?id=*",
          "/news/article/*",
          "/promo/*",
          "NOT /private/",
          "NOT /settings/*",
          "*.html"
        ]
      }
    ]
  },
  "webcredentials": {
    "apps": ["ABCDE12345.com.example.app"]
  },
  "appclips": {
    "apps": ["ABCDE12345.com.example.app"]
  }
}

访问下边这些路径的请求都会将用户路由到应用程序中:

"/",
"/about-us/",
"/products/*",
"/services/detail/?id=*",
"/news/article/*",
...

而对于那些以 NOT 前缀的路径,系统将不会重新路由用户,允许体验继续在浏览器中进行:

"NOT /private/",
"NOT /settings/*",
...

部署 AASA 文件

当你准备好部署 AASA 文件时,需要将其托管在网站的根目录或 .well-known 目录下:

  • /apple-app-site-association

  • /.well-known/apple-app-site-association

注意:AASA 文件不应有文件扩展名。为了让内容分发网络(CDN)成功缓存此文件,它必须托管在可供所有 IP 地址和范围访问的域上,通过 HTTPS 提供文件,不重定向,并且不被访问策略阻止。

你还应该确保服务器以 Content-Type: application/json 的形式提供文件,并且文件大小不超过 128KB。

当你首次托管 AASA 文件时,苹果的 CDN 将在 24 小时内获取它,这意味着它们将在文件发布的第一天内从你的服务器请求并缓存一份副本。

根据苹果文档的说法,这个 24 小时的窗口仅与首次将文件放入苹果的 CDN 有关。随后的更新会在不同的时间间隔发生。

其实我们可以通过下边的链接看下其他公司的 AASA 文件是如何写的:

测试与验证

随着应用程序的迭代,你将需要修订 AASA 文件,以便为新的用户流程整合通用链接(Universal Link)。

AASA 文件中的错误很常见,因此验证此文件的行为和语法成为流程中不可或缺的一部分。一旦被苹果的 CDN 确认,此文件中的任何错误都可能影响所有用户的通用链接行为——无论是新用户还是老用户。

幸运的是,在更改到达苹果之前,有几种方法可以验证 AASA 文件的行为。

绕过 CDN

关联域支持一种备用模式,允许开发者通过绕过 CDN 直接从服务器获取文件,以验证修改后的 AASA 文件的行为。

要在 Xcode 项目中激活此模式:

  • 前往 Signing & Capabilities -> Associated Domains

  • 在域条目的末尾添加 ?mode=developer(例如:applinks:yourdomain.com?mode=developer

现在,当你构建并运行应用程序时,它将直接从你的服务器检索更新的 AASA 文件。

在开发期间只能使用备用模式,你必须在将应用程序提交到 App Store 之前从关联域中删除后面这段字符串。

验证 AASA 文件配置

你可以使用以下工具来验证 AASA 文件的语法和配置:

  • Universal Link & Apple App Site Association Testing Tool[1]

这是一个免费的工具,用于验证和测试 Apple App Site Association(AASA)文件。确保你的通用链接配置正确,通过简单的链接创建、实时测试和团队协作功能简化 AASA 文件的故障排除。

官方查询方式

你可以通过访问以下网址检查苹果的 CDN 是否已获取文件的最新版本:

  • https://app-site-association.cdn-apple.com/a/v1/{YOUR_DOMAIN_HERE}

如果 CDN 确实具有文件的最新版本,那么任何新应用安装也将获得此最新版本。但是对于老用户,他们的设备每周只会检查一次更新的副本。

重新安装应用程序将从 CDN 获取最新版本。

实际上,AASA 文件的任何更改的推出周期是 CDN 刷新其缓存所需的时间与现有用户每周检查的时间相结合。

缓存更新时间

为了更好地理解缓存版本更新前剩余的时间,我们可以查看来自 CDN 的响应头中的 Cache-Control 字段。

Cache-Control 头由网络服务器使用,以决定浏览器和中间缓存(如 CDN)应该如何以及在多长时间内缓存文件的提供版本。

在 Facebook 和 Yelp 上,我们可以通过以下链接查看:

  • https://app-site-association.cdn-apple.com/a/v1/facebook.com

  • https://app-site-association.cdn-apple.com/a/v1/yelp.com

然后我们可以通过从 max-age 中减去 age 来确定缓存何时更新:

image.png

image.png

  • max-age:6 小时,缓存刷新时间为 17,760 秒 / 约 5 小时。

  • max-age:1 小时,缓存刷新时间为 3,112 秒 / 约 50 分钟。

在实际情况中,最短的 max-age 是 3,600 秒,即 1 小时,最长的是 21,600 秒,即 6 小时。

值得注意的是,苹果的 CDN 会覆盖原始网站指定的 Cache-Control 设置。例如,直接从 Yelp 访问 AASA 文件的 max-age 为 1200,但从苹果的 CDN 检索时,其 max-age 为 3600。

通过更长时间的缓存文件,苹果能够最大限度地减少对原始服务器的请求频率,从而减少网络和原始服务器的流量和负载。

总结

AASA 文件是 iOS 生态系统中一个非常重要的组件,它通过在 iOS 应用和网络域之间建立安全且经过验证的链接,实现了诸如通用链接(Universal Links)、共享网络凭证、Handoff 和 App Clips 等功能。

通过了解 AASA 文件的创建、部署、测试与验证,开发者可以更好地优化其应用的链接配置,提升用户体验,并确保应用的稳定性和可靠性。

参考资料

[1]

Universal Link & Apple App Site Association Testing Tool: getuniversal.link/?ref=digita…

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

Swift 6 新特性(一):count(where:) 方法带来的从复杂到简洁变化

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

Swift 语言一直在不断演进,推出的新特性不仅提升了性能,还提高了代码的可读性。

其中一个值得关注的新功能就是在 SE-0220 中引入的 count(where:) 方法。这个方法让我们可以更高效、更具表现力地统计序列中满足特定条件的元素,避免了之前需要结合 filter()count 的复杂操作。

count(where:) 方法将过滤(filter)和计数(count)两个步骤合并为一个,省去了创建和丢弃中间数组的麻烦,从而提升了性能,同时代码也变得更加简洁。

示例1:统计高于冰点的温度

假设我们有一个以摄氏度为单位的温度数组,我们想统计其中有多少温度高于冰点(0°C):

在之前,我们可能需要这样写:

let temperatures = [-510, -22025, -1]
let aboveFreezingCount = temperatures.filter { $0 > 0 }.count

现在,我们可以使用 count(where:) 方法来简化代码:

let temperatures = [-510, -22025, -1]
let aboveFreezingCount = temperatures.count { $0 > 0 }

// 输出 `3`
print(aboveFreezingCount)

在这个例子中,aboveFreezingCount 的值为 3,因为有三个温度(10, 20, 25)符合条件。

示例2:统计具有特定前缀的元素

让我们再看一个示例,假如我们有一组产品名称,想统计以 "Apple" 开头的产品数量:

let products = [
    "Apple", 
    "Banana", 
    "Apple Pie",
    "Cherry", 
    "Apple Juice", 
    "Blueberry"
]
let appleCount = products.count { $0.hasPrefix("Apple") }

// 输出 `3`
print(appleCount)

在这个例子中,appleCount 的值为 3,因为 "Apple"、"Apple Pie" 和 "Apple Juice" 都以 "Apple" 开头。

示例3:根据长度统计元素

一个常见的应用场景是根据元素的长度进行统计。例如,我们可能想知道数组中有多少名字的长度少于六个字符:

let names = ["Natalia""Liam""Emma""Olivia""Noah""Ava"]
let shortNameCount = names.count { $0.count < 6 }

// 输出 `4`
print(shortNameCount)

在这个例子中,shortNameCount 的值为 4,因为 "Liam"、"Emma"、"Noah" 和 "Ava" 的长度都少于六个字符。

示例4:统计特定元素

如果需要统计序列中某个特定元素出现的次数,可以在闭包中使用等于运算符 (==)。例如:

let animals = ["cat""dog""cat""bird""cat""dog"]
let catCount = animals.count { $0 == "cat" }

// 输出 `3`
print(catCount)

在这个例子中,catCount 的值为 3,因为 "cat" 在数组中出现了三次。

适用范围和平台支持

count(where:) 方法适用于所有遵循 Sequence 协议的类型,这意味着我们不仅可以在数组中使用,还可以在集合、字典和其他序列类型中使用。

但需要注意的是,序列必须是有限的,以确保方法能够在合理的时间内完成。

count(where:) 方法在 Swift 6 中引入,因此需要 Xcode 16 才能使用这个特性。它支持多种平台和操作系统版本,包括 iOS 8.0+、macOS 10.10+、visionOS 1.0+ 等。

总结

count(where:) 方法是一个非常实用的功能,它不仅简化了代码,还提高了性能。如果你是一名经验丰富的 Swift 开发者,想要学习高级技巧,可以关注我的公众号,我会持续分享 Swift 相关的技巧和知识。

你对这个新特性有什么看法呢?欢迎在评论区与我们分享你的想法。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

Xcode 高效秘诀:这 11 个快捷键你必须知道!

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

作为一个 iOS 开发者,在使用 Xcode 时,掌握键盘快捷键是提高生产力和效率的关键。在这篇文章中,我将为大家介绍一些我最喜欢的 Xcode 快捷键。

其实之前也写过一些相关的文章,感兴趣的也可以去看看:

快捷键速查表

在开始之前,先来一个快捷键速查表:

  • ⌘ - Command

  • ⇧ - Shift

  • ⌥ - Option/Alt

  • ⌃ - Control

现在,让我们一起深入了解吧。

1. 产品菜单快捷键

首先,让我们从 Xcode 的产品菜单中一些基础快捷键开始:

  • 运行:⌘ R

  • 测试:⌘ U

  • 清理构建文件夹:⇧ ⌘ K

  • 清理 DerivedData 文件夹:⌘ ⇧ K

  • 停止:⌘ .

如果你已经是一个 iOS 开发者了,相信这些快捷键你已经很熟悉了,下面我们再介绍一些更高级的快捷键。

2. 快速导航

在处理不同文件,或者跟踪调用栈来解决问题时,快速导航是节省时间的法宝:

  • 前进:⌃ ⌘ →

  • 后退:⌃ ⌘ ←

3. 快速打开与跳转定义

另一个重要的快捷键是快速打开文件,使用 ⇧ ⌘ O,这个快捷键不仅可以搜索文件,还可以搜索类名和方法。

image.png

使用此快捷键后,跳转到定义,然后如果你想知道当前方法的所在的文件,可以使用 ⌃ ⌘ J 快捷键。

4. 查找功能

无论是在当前打开的文件中,还是在整个项目/工作区内,查找都是必不可少的操作:

  • 当前文件查找:⌘ F

  • 当前文件查找并替换:⌥ ⌘ F

  • 全局查找:⇧ ⌘ F

5. 视图管理

能够快速显示和隐藏不同的 Xcode 区域特别有用,尤其是在较小屏幕上工作时:

  • 显示/隐藏项目导航器:⌘ 0

  • 显示/隐藏调试控制台:⌘ ⇧ Y

  • 显示/隐藏检查器:⌘ ⌥ 0

6. 快速打开两个文件并排显示

我之前提到过的快速打开命令 ⌘ ⇧ O,在结合使用 ⌥ 时更加强大。

  • 使用 ⌘ ⇧ O 打开对话框

  • 输入你要查找的文件名

  • 按住 ⌥ 键然后用鼠标单击目标项目,或者使用 enter 键选择文件。新文件将会在一个单独的编辑器中打开,这样你可以继续在当前文件上工作,同时访问新打开的文件。

这个 ⌥ 技巧在项目导航器中选择文件时也同样适用。

7. 快速跳转到文件中的特定方法

对于大型文件中有众多方法的情况,这个快捷键非常有用,因为滚动很快会变得繁琐。

  • 使用 ⌃ 6 打开文档结构

  • 开始输入方法名

  • 使用 enter 键跳转到该方法

image.png

在输入时还可以模糊匹配,Xcode 会为你完成搜索。

8. 重复上一次测试

如果你经常写单元测试,快捷键 ⌃ ⌘ ⌥ G 在特别有用,可以重复运行我们上次进行的测试。

9. 重新缩进代码

我们可以通过按 ⌃ I 来重新缩进选定的代码,这在代码位置混乱时(例如在重构之后)特别有用。

我一般会配合全选快捷键 ⌘ A 一起使用,先全选再重新缩进,这样就可以将整个文件的代码进行重新缩进了。

10. 启用/禁用断点

在调试代码时,快捷键 ⌘ Y 可以帮助我们快速启用和禁用断点。

11. 不编译运行

这个快捷键非常有用,但可能很多人不知道。

在开发过程中,我们通常使用 ⌘ R 来运行代码,但这个命令其实是先编译再运行,但有时候我们并不需要编译,比如我刚执行完 ⌘ B,或者刚刚运行完没改代码的情况下想再运行一次。

这时候就可以使用 ⌃ ⌘ R 快捷键,直接运行,不用重新编译,非常节省时间。

总结

通过掌握这些快捷键,你可以大大提高在 Xcode 中的工作效率,节省宝贵的时间,让开发过程更加顺畅。希望这些快捷键能为你的开发旅程带来更多便利,你还有哪些喜欢的快捷键,欢迎在评论区留言分享。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

惊人发现!Swift 循环性能对比,你用对了吗?

这里每天分享一个 iOS 的新知识,快来关注我吧

image.png

前言

在 Swift 编程中,我们经常需要使用循环来处理数据,那么你有没有想过哪些循环函数的性能最好?

今天我们随便挑几个循环函数来测一下性能。

for 循环、while 循环和高阶函数 reduce 的性能表现如何呢?让我们通过一个简单的性能测试来一探究竟。

性能测试代码

我们使用以下代码来测量不同循环结构的执行时间,如果你想测试其他的循环方法,也可以参考这个代码:

func processTime(_ title: String, closure: () -> ()) {
    let startTime = CFAbsoluteTimeGetCurrent()
    closure()
    let duration = CFAbsoluteTimeGetCurrent() - startTime
    print(title, "Duration = \(String(format : "%.4f", duration))")
}

processTime("for-in") {
  var sum = 0
  for i in 0..<10000000 {
    sum += i
  }
}

processTime("while") {
  var sum = 0
  var i = 0
  while i<10000000 {
    sum += i
    i += 1
  }
}
        
processTime("reduce") {
  let sum = (0..<10000000).reduce(0, +)
}

测试结果

在 Debug 模式下运行上面的代码,结果如下:

  • for-in Duration = 1.5586 秒

  • while Duration = 0.0115 秒

  • reduce Duration = 1.5766 秒

看到这个结果,我不禁好奇,为什么 for 循环和 reducewhile 循环慢这么多呢?

深入分析

for-in 循环分析

通过使用 Profiler Instruments 工具,我们可以更深入地了解 for-in 循环在底层的运作:

Image

  • for 循环被转换为 IndexingIterator

  • 每次调用下一个索引时,都会触发 next() 函数(97% 的时间都花在调用 next() 函数上)。

  • 每次触发 next() 时,它会调用一系列协议方法(动态调度),这需要时间去查找协议表。

为什么 next() 函数使用动态调度呢?让我们看看 IndexingIterator 的实现细节:

public protocol CollectionSequence {
    ...
}

public struct IndexingIterator<ElementsCollection> { 
    let _elements: Elements
}

extension IndexingIteratorIteratorProtocolSequence {
    public mutating func next() -> Elements.Element? {
        if _position == _elements.endIndex { return nil }
        let element = _elements[_position]
        _elements.formIndex(after: &_position)
        return element
    }
}

next() 函数中,我们使用了 _elements 属性。由于 _elements 可以是任何 Collection 协议类型,因此在编译时我们并不知道 _elements[_position]_elements.formIndex 指向哪个成员。因此,我们必须在运行时查看虚表并使用协议表。

相比之下,while 循环只需管理一个参数,不需要执行任何表查找。因此,while 循环的工作量更少,性能自然比 for-inreduce 更好。

Swift 编译器探索

我们还可以使用 godbolt 工具来查看代码的编译方式:

在编译时,Swift 会将 for-in 转换为:

let range: Range<Int> = 0..<10000000
let iterator = range.makeIterator()
while iterator.next() {
  ...
}

Image

Release 模式下的表现

在 Release 模式下,开启编译器优化后,结果如下:

  • for-in Duration = 0.0063 秒

  • while Duration = 0.0053 秒

  • reduce Duration = 0.0075 秒

在编译器优化开启的情况下,Swift 不需要执行一堆动态调度。它只需管理一个计数器变量,增加它的值并执行求和操作,直到算出结果为止。🚀

学到什么?

  1. Profiler Instrument 是一个调试应用程序性能的有用工具。

  2. 测量应用程序性能时,应在 Release 模式下进行。

  3. 了解了一些关于 Swift 编译器的知识。

总的来说,while 循环在 Debug 模式下表现更好,而在 Release 模式下,开启编译器优化后,各种循环的性能差异变得不明显,表明编译器优化对性能的影响很大。

这提醒我们,在进行性能优化时,必须考虑编译器优化的影响。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

开发者必看,全面解析 iOS 架构,探索 iOS 背后的秘密!

这里每天分享一个 iOS 的新知识,快来关注我吧

image.png

前言

苹果的移动设备,包括 iPhone、iPad 和 iPod,都是运行在 iOS 平台上,虽然后来 iPad 独立拆分出去成为了 iPadOS,但 iOS 和 iPadOS 的底层架构和 iOS 是相同的。

iOS 是基于 Darwin 为基础开发的。iOS 不仅可以管理硬件功能,还提供了开发 iOS 应用所需的一切技术支持。

iPhone 设备的基础软件包中包含一些常用的系统应用,例如邮件、日历、计算器、电话和浏览器等。然而,出于安全和商业原因,不可能在 Apple 以外的任何硬件上运行 iOS 和 Mac OS X,并且禁止在 Apple 以外的任何其他移动设备上使用 iOS。。

但是你有没有想过,iOS 的架构是怎样的?今天我们就来简单了解一下 iOS 的架构,如果未来你有机会研发一个新的操作系统,那么了解 iOS 的架构将对你有很大的帮助。

iOS 架构概述

iOS 架构的分层设计主要分为以下几层,可以从图中看得比较清楚:

image.png

  1. Core OS:核心操作系统层,负责管理硬件设备,提供内存管理、文件系统处理和线程等基本操作系统的功能。

  2. Core Services:核心服务层,将核心操作系统层提供的服务抽象化,提供所有应用可用的基本服务。

  3. Media:媒体层,通过 Media 层,开发者可以使用 iPhone 上的多媒体服务。

  4. Cocoa Touch:Cocoa Touch 层,提供了一个抽象层,使 iPhone 和其他 iOS 设备编程的各种库可访问。

iOS 会讲代码封装到框架(Framework)中。框架通常包括头文件、图片和所有所需的动态共享库。

多数 iOS 开发者在开始开发时倾向于选择更高级别的框架,因为它们提供了面向对象的抽象、更少的代码编写以及封装其他特性的能力。接下来让我们来探讨这些不同的抽象层次。

Core OS

作为 iOS 栈的最底层,Core OS 位于设备硬件之上。它提供了所有 iOS 功能的基础低级特性。除了基本操作系统的标准功能,如内存管理、文件系统处理和线程外,还提供了一些服务,包括低级网络和对外部配件的访问。

核心 OS 层储存了大多数其他技术的底层特性,比如。

  • Core Bluetooth Framework:与传统和低能耗蓝牙设备进行交互。

  • External Accessories Framework:与通过蓝牙或 Apple Lightning 连接的设备配件通信。

  • Accelerate Framework:进行大规模数学和图像计算,优化高性能和低能耗。

  • Security Services Framework:控制对应用及其维护数据的访问。

  • Local Authorization Framework:通过用户熟悉的生物识别或密码进行身份验证。

Core Services

Core Services 层将 Core OS 层提供的服务抽象化,提供所有应用可用的基本服务。核心服务层和其他层一样,提供了一组框架:

  • Accounts framework:允许用户直接从应用访问和管理其外部帐户,而无需输入登录信息。

  • Address Book framework:访问用户的联系信息。

  • CFNetwork framework:管理网络配置更改和网络服务访问。

  • Core Data framework:管理 MVC 应用的数据模型。

  • Core Foundation framework:提供基本数据管理和服务功能的接口。

  • Core Location framework:支持提供应用的位置信息和方向数据。

  • Core Media framework:提供音频和视频的播放、录制和处理。

  • Core Motion framework:提供设备的运动数据。

  • Core Telephony framework:提供电话网络服务。

  • EventKit framework:提供访问日历和提醒数据的功能。

  • Foundation framework:它为应用程序和框架提供了基础功能层,包括数据存储和持久化、文本处理、日期和时间计算、排序和过滤以及网络。对于 iOS 开发来说,Foundation 框架是使用最频繁的框架之一。

  • Mobile Core Services framework:该框架提供对重要操作系统功能的访问和控制。

  • Social framework:这个框架利用通用系统接口,将用户分享内容发布到支持的社交网站。

  • StoreKit:这个框架支持应用内购买以及与 App Store 的交互。

  • System Configuration framework:该框架允许应用程序访问设备上的网络配置选项,检查设备的可达性,例如 Wi-Fi 或蜂窝连接是否处于活动状态等。

Media Layer

通过 Media 层,开发者可以使用 iPhone 上的多媒体服务。主要包括系统的图形、音频和视频技术。苹果特别注重多媒体体验的反馈,尤其是音频和视频质量。所以 iOS 的底层媒体层赋予 iOS 音频、视频、图形和 AirPlay(无线传输)能力。

  • Assets Library framework:这个框架提供对用户资产的媒体库的访问。

  • AV Foundation framework:处理视听资源、管理相机设置、编辑音频并建立系统音频交互。

  • Core Audio framework:与设备的音频硬件接口。

  • Core Graphics framework:iOS 应用的本地绘图引擎,支持自定义 2D 矢量和基于图像的渲染。

  • Core Image framework:为控制视频和照片提供高级无损支持。

  • Core MIDI framework:提供与 MIDI(乐器数字接口)设备通信的 API,包括硬件键盘和合成器。

  • Core Text framework:提供了用于布局文本和处理字体的低级编程接口。

  • Core Video framework:利用基于管道的 API,支持 Metal 和 OpenGL 来处理数字视频,包括逐帧编辑。

  • Image I/O framework:这个框架提供对图像元数据的访问以及读取和写入大多数图像文件类型。

  • GLKit framework:使用硬件加速 API 管理高级 2D 和 3D 渲染。

  • Media Player framework:这个框架可以从应用程序中查找并播放歌曲、音频播客、有声读物和其他媒体。

  • OpenAL framework:这个框架提供了一种行业标准音频传输技术。

  • OpenGL ES framework:该框架通过硬件加速接口控制强大的 2D 和 3D 渲染。

  • Quartz Core framework:该框架允许用户使用幻灯片和 Core Image 滤镜查看、更改和保存照片。

Cocoa Touch

Cocoa Touch 层提供了一个抽象层,使 iPhone 和其他 iOS 设备编程的各种库可访问。Cocoa Touch 层包含了一组使用 Mac OS X Cocoa API 创建的 Objective-C 框架。

任何 iOS 应用的 UI 设计开发都是使用 Cocoa Touch 框架创建的。这一层支持通知、多任务处理、触摸特定输入、所有高级系统服务和其他重要技术。

  • Address Book UI framework:这个框架可以获取用户的联系人并将其呈现在图形界面中。

  • Event Kit UI framework:这个框架是一个用视图控制器来显示和更改事件的通用系统界面。

  • Game Kit framework:该框架允许用户通过游戏中心在线共享游戏相关数据。

  • iAd framework:广告框架。

  • Map Kit framework:地图框架。

  • Message UI framework:消息框架。

  • Twitter framework:用于创建推文以及创建 URL 来访问 Twitter 服务的用户界面。

  • WebKit framework:网页框架。

  • UI Kit framework:为 iOS 图形化、事件驱动的应用开发提供关键基础。

可以说做 iOS 开发,使用最多的就是 Foundation 和 UIKit 两个框架。

另外这一层还提供其他开发者使用的能力,例如:

  • 多任务处理。

  • 基本的应用程序管理和基础设施。

  • 用户界面管理。

  • 处理触摸事件。

  • 剪切、复制和粘贴等。

结论

综上所述,iOS 的架构设计合理,分层清晰,为开发者提供了丰富的框架和接口,使得开发 iOS 应用变得更加高效和便捷。

无论是从底层的 Core OS 到最上层的 Cocoa Touch,每一层都扮演着重要的角色,为用户提供了稳定、高效、安全的移动应用体验。深入了解 iOS 的架构,将有助于开发者更好地理解和利用 iOS 平台的强大功能,为开发出更优秀的应用奠定基础。

iOS 的成功不仅在于其设计和功能,更在于其庞大而完善的生态系统,相信随着技术的不断发展,iOS 将继续引领移动应用开发的潮流,为用户带来更多惊喜与便利。

你对 iOS 的架构有什么看法?欢迎在评论区留言讨论。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 “iOS新知”,每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

❌