[译]从 iPhone OS 1.0 到 iOS 14 编程语言的演变

2021-12-24 15:05:37 浏览数 (1)

在我之前关于苹果在 iOS 14 中使用了 Swift 和 SwiftUI 的文章中,我统计了 iOS 中使用 Swift 和 SwiftUI 的内置应用程序的数量。一些读者问我是否可以提供一个百分比而不是一个绝对数字。

在这篇新文章中,我将通过测量 iOS 中二进制文件的总数来回答这个问题。我将更进一步,并计算使用其他编程语言的二进制文件的数量:Objective-C、C 和 C。

最后,为了尽可能完整,我对所有主要的 iOS 版本进行了分析,从 iPhone OS 1.0 到 iOS 14。这将详细概述不同编程语言在十多年的 iOS 开发中的演变。

范围

在开始分析之前,让我澄清一下范围。首先我我只分析了通常意义上的 'iOS',即内核上方的所有内容。不包括 XNU 内核和低级固件(SecureROM、基带、...)。另一方面,我计算了位于 dyld 共享缓存中的框架,这是 iPhone OS 3.1 中引入的缓存机制,并且仍在 iOS 14 中使用。

另一点是您可能会注意到没有 Objective-C 。我使用的方法不能真正检测使用 Objective-C 的二进制文件。所以我把这些二进制文件算作同时使用了 Objective-C 和 C 。

最后,您应该对这种分析持保留态度。虽然我相信整体情况是准确的,但我的方法有一些局限性,因为我无法访问 iOS 源代码并且依赖于不精确的编译工件。

检测和统计二进制文件数量

要解决的第一个问题是计算 iOS 中二进制文件的数量。因为我在设备外运行分析,所以我不能依赖正确设置的权限。

相反,我使用了二进制文件必须是 Mach-O 文件才能执行的事实。通过file在 Bash 脚本中使用命令行工具来检查文件是否是 Mach-O 二进制文件是微不足道的:

代码语言:javascript复制
#---------------------------------------------------------------------
# Function to check if a file (passed as argument $1) is an executable
# It returns the number of occurrences of the string 'Mach-O' returned by
# the output of the file command. File would generally return a line like
# - Mach-O 64-bit executable arm64e (/usr/bin/sysdiagnose)
# - Mach-O 64-bit dynamically linked shared library arm64e (/usr/lib/libobjc-trampolines.dylib)
# - Mach-O 64-bit bundle arm64e (/usr/lib/xpc/support.bundle/support)
# - Mach-O 64-bit dynamic linker arm64e (/usr/lib/dyld)
# - Mach-O 64-bit kext bundle arm64e (/System/Library/Extensions/AppleIDV.kext/AppleIDV)
# - Mach-O 64-bit dynamically linked shared library stub arm64e (/Applications/FTMInternal-4.app/libMobileGestalt.dylib)
#---------------------------------------------------------------------
isExecutable ()
{
    file $1 2>/dev/null | grep -o "Mach-O" | wc -l
}

我在每个主要 iOS 版本的所有文件中运行此脚本,以计算二进制文件的数量并获得随时间的演变:

如您所见,iPhone OS 1.0 包含相当少量的二进制文件。但 iOS 变得越来越复杂,iOS 14.0 现在包含的二进制文件是 iPhone OS 1.0 的 20 倍。

检测不同的编程语言

在我之前关于苹果在 iOS 14 中使用 Swift 和 SwiftUI 的文章中,我统计了使用 Swift 和 SwiftUI 的内置应用程序。在本文中,我决定更进一步,并计算使用 Objective-C、C 和 C 的二进制文件的数量。

请记住,您可以混合不同的编程语言来创建二进制文件。一个示例是创建一个应用程序,该应用程序使用 C 作为低级引擎,使用 Objective-C 作为 UI。另一个很好的例子是Clatters,我使用了一些用 Swift、SwiftUI、Objective-C 和 C 编写的代码,这些代码是用来解决每个特定问题的最合适的编程语言。

检测 SwiftUI

在我之前的帖子中,我解释了如何通过检查二进制文件是否动态链接到/System/Library/Frameworks/SwiftUI.framework/Versions/A/SwiftUI. 这可以在 Bash 脚本中实现,例如:

代码语言:javascript复制
isSwiftUI=$( echo "${otoolOutput}" | grep -o "/System/Library/Frameworks/SwiftUI.framework/SwiftUI" | wc -l )

尽管 SwiftUI 并不是真正的编程语言,而是一种用户界面工具包,但我决定将其包含在本文中。

检测 Swift

检测 Swift 的使用可以使用类似的方法来完成,如2016 年的一篇旧帖子所述。然而,由于 Swift 库在 iOS 历史上已被移动到不同的位置,因此该脚本稍微复杂一些:

代码语言:javascript复制
isSwift=$( echo "${otoolOutput}" | grep -o "/usr/lib/swift/" | wc -l )
if [ ${isSwift} == 0 ]
then
    # On iOS 11, the Swift dylib were inside "/System/Library/PrivateFrameworks/Swift/"
    isSwift=$( echo "${otoolOutput}" | grep -o "/System/Library/PrivateFrameworks/Swift/" | wc -l )
    
    if [ ${isSwift} == 0 ] && [[ $1 != */libswiftUIKit.dylib ]]
    then
        # On iOS 9, the Swift dylib were built into the app
        isSwift=$( echo "${otoolOutput}" | grep -o "@rpath/libswiftUIKit.dylib" | wc -l )
    fi
fi

检测 Objective-C

检测二进制文件是否使用 Objective-C 非常简单,如Apple 文档中所述:

Objective-C 运行时是一个运行时库,它为 Objective-C 语言的动态属性提供支持,因此所有 Objective-C 应用程序都链接到它。Objective-C 运行时库支持函数在位于 /usr/lib/libobjc.A.dylib 的共享库中实现。

因此,一条简单的线就可以使用 Objective-C 检测所有应用程序:

代码语言:javascript复制
isObjectiveC=$( echo "${otoolOutput}" | grep -o "/usr/lib/libobjc." | wc -l )

检测 C

与 Objective-C 类似,我们可以依赖这样一个事实,即使用 C 的应用程序必须使用 C 标准库。在现代 iOS 版本中,C 标准库 libc 位于/usr/lib/libc .1.dylib. 在 iOS 7 及更早版本上,使用了 gcc 的 libstdc 并位于/usr/lib/libstdc .6.dylib.

如果您想知道为什么在下面的脚本中没有使用确切的路径,原因是标记libc abi.dylib为使用 C 。libc abi是对标准 C 库的低级支持,似乎是用 C 编写的。

代码语言:javascript复制
isCPP=$( echo "${otoolOutput}" | grep -o "/usr/lib/libc  " | wc -l )
if [ ${isCPP} == 0 ]
then
    isCPP=$( echo "${otoolOutput}" | grep -o "/usr/lib/libstdc  " | wc -l )
fi

检测 C

在不涉及复杂细节的情况下,Objective-C 和 C 是 C 的超集。这意味着 Objective-C 或 C 应用程序正在使用 C 代码。所以我们无法知道 Objective-C 或 C 应用程序是否使用了一些纯 C 代码。但是,有些应用程序完全是用 C 编写的。这些通常是低级命令行工具,例如/usr/bin/zprint.

如何检测这样的二进制文件?如果二进制文件既不链接到 Objective-C 运行时库也不链接到 C 标准库,而是使用libc位于的标准 C 库/usr/lib/libSystem.B.dylib,那么我们可以假设这个二进制文件完全用 C 编写。检测完全用 C 编写的应用程序C,我最终得到了这些行:

代码语言:javascript复制
isC=$( echo "${otoolOutput}" | grep -o "/usr/lib/libSystem." | wc -l )
if [ ${isC} == 0 ]
then
    isC=$( echo "${otoolOutput}" | grep -o "/usr/lib/system/" | wc -l )
fi

您还会注意到,我将位于内部的所有动态库硬编码/usr/lib/system/为用 C 编写。这些低级库没有链接到,/usr/lib/libSystem.B.dylib但很可能是用 C 编写的。

特别案例

在最终脚本中,您将看到一些特殊情况:

  • /usr/lib/libobjc-trampolines.dylib: 这个库完全是用汇编写的,你可以在这个文件objc-blocktramps-arm64.s 中阅读它的 arm64 源代码
  • /usr/lib/dyld: dyld 是动态链接器,是一个非常特殊的二进制文件。由于它是开源的,我们可以很容易地确认它包含 C 代码。
  • /System/Library/Caches/com.apple.xpc/sdk.dylib/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib:这两个库似乎是自动生成的库和 dyld 的一部分。
  • /System/Library/Extensions/*:有几个(内核?)iOS 使用的扩展,比如/System/Library/Extensions/AppleIDV.kext. 所有这些二进制文件似乎都使用 C 。

最终脚本

您可以在此处下载循环遍历文件夹中所有文件的完整脚本。此脚本打印找到的所有二进制文件的路径,并告诉您使用的编程语言,例如:

代码语言:javascript复制
/Applications/AccountAuthenticationDialog.app/AccountAuthenticationDialog |Objective-C
/Applications/ActivityMessagesApp.app/ActivityMessagesApp |Objective-C
/Applications/ActivityMessagesApp.app/PlugIns/ActivityMessagesExtension.appex/ActivityMessagesExtension |Objective-C|Swift
/Applications/AnimojiStickers.app/AnimojiStickers |Objective-C
/Applications/AnimojiStickers.app/PlugIns/AnimojiStickersExtension.appex/AnimojiStickersExtension |Objective-C
/Applications/AppSSOUIService.app/AppSSOUIService |Objective-C
/Applications/AppStore.app/AppStore |Objective-C|Swift
/Applications/AppStore.app/PlugIns/ProductPageExtension.appex/ProductPageExtension |Objective-C|Swift
/Applications/AppStore.app/PlugIns/SubscribePageExtension.appex/SubscribePageExtension |Objective-C|Swift
[...]

原始结果

我在从 iPhone OS 1.0 到 iOS 14.0 的所有主要 iOS 版本上运行了这个脚本。如果您对原始数据感兴趣,可以在此处下载:

版本

设备

原始数据

iOS 14.0 (18A373)

iPhone X

iOS14.txt

iOS 13.1 (17A844)

iPhone X

iOS13.txt

iOS 12.0 (16A366)

iPhone X

iOS12.txt

iOS 11.1 (15B93)

iPhone X

iOS11.txt

iOS 10.1 (14B72)

iPhone 5S

iOS10.txt

iOS 9.0 (13A344)

iPhone 5S

iOS9.txt

iOS 8.0 (12A365)

iPhone 5S

iOS8.txt

iOS 7.0.1 (11A470a)

iPhone 5S

iOS7.txt

iOS 6.0 (10A403)

iPhone 3GS

iOS6.txt

iOS 5.0 (9A334)

iPhone 3GS

iOS5.txt

iOS 4.0 (8A293)

iPhone 3GS

iOS4.txt

iPhone 操作系统 3.0 (7A341)

iPhone 3GS

iOS3.txt

iPhone 操作系统 2.0 (5A347)

iPhone 2G

iOS2.txt

iPhone 操作系统 1.0 (1A543a)

iPhone 2G

iOS1.txt

iOS 14 中的编程语言分布

现在我们知道二进制文件的总数和它们的编程语言,我们可以回答关于在 iOS 14 中使用 Swift 和 SwiftUI 的二进制文件百分比的问题。请注意,一个二进制文件可以使用多种编程语言。因此,可以多次计算二进制文件,例如在 Swift 和 Objective-C 类别中。二进制文件的大小和重要性也没有考虑在内。

在 iOS 14 中的所有二进制文件中:

  • 88% 使用 Objective-C
  • 17% 使用 C
  • 8% 使用 Swift
  • 8% 完全用 C
  • 1% 使用 SwiftUI

一些有趣的点:

  • Objective-C 仍然是 iOS 14 中的关键组件。
  • C 和 C 也发挥着重要作用。这些语言通常由与音频、视频、电话、Web 和其他底层框架相关的二进制文件使用。
  • Swift 已经很快被采用,并且已经被 iOS 14 中 8% 的二进制文件使用。

编程语言的演变

通过在从 iPhone OS 1.0 到 iOS 14.0 的所有 iOS 主要版本上运行该脚本,我们可以看到 iOS 使用的编程语言的演变。再次请注意,单个二进制文件可以计算多次,因此二进制文件的总和大于二进制文件的总数:

二进制文件数量

它告诉我们什么?

  • 正如我们已经看到的,iOS 的每个版本都变得越来越复杂。
  • iPhone OS 1.0 包含的二进制文件少于使用 Swift 的 iOS 14.0 中的二进制文件数量。
  • Swift 的使用在 Apple 正在取得进展,现在使用 Swift 的二进制文件比完全用 C 编写的二进制文件多。但采用需要时间。
  • 随着每个 iOS 版本的发布,使用 Objective-C 的二进制文件的数量仍在增长。
  • 多年来,C 的使用也在不断增长。
  • 另一方面,完全用 C 编写的二进制文件的数量现在停滞不前。

从这张图中可以更容易地看出每种编程语言的演变:

使用各种编程语言的二进制文件数量

WidgetKit 小部件和 Objective-C

在查看数据以确保它们有意义时,我惊讶地发现所有使用 WidgetKit(第一个公共 SwiftUI 专用框架)构建的新 iOS 14 小部件都使用了 Objective-C。

事实证明,如果您使用 Configuration Intent 创建一个 iOS 14 小部件,Xcode 将自动生成一些包含 Objective-C 类的文件,例如:

代码语言:javascript复制
@objc(ConfigurationIntent)
public class ConfigurationIntent: INIntent {

}

因此,iOS 14 中的所有 SwiftUI 小部件都至少间接地使用了一些 Objective-C 代码。

结论

即使我们无法访问 iOS 源代码,我们也可以确定 iOS 中使用的编程语言。这种方法显然有一些限制,虽然我相信整体情况是准确的,但我不能保证结果是完全准确的。如果您认为我遗漏了什么,请在 Twitter 上联系我。

然而,我们可以得出几个结论。首先,iOS 变得越来越复杂,iOS 14.0 现在包含的二进制文件是 iPhone OS 1.0 的 20 倍。

有趣的一点是,iOS 14 中 88% 的二进制文件直接或间接依赖于 Objective-C。Objective-C 仍然是 iOS 中的关键编程语言。

还值得注意的是,C 的使用也在这些年来不断增长,iOS 14 中有 17% 的二进制文件使用 C 。

最后,Swift 的使用在 Apple 正在取得进展,但采用需要时间。在 iOS 14 中,大约 8% 的二进制文件使用 Swift。

译自 Evolution of the programming languages from iPhone OS 1.0 to iOS 14

0 人点赞