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

在我之前关于苹果在 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 二进制文件是微不足道的:

#---------------------------------------------------------------------
# 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 脚本中实现,例如:

isSwiftUI=$( echo "${otoolOutput}" | grep -o "/System/Library/Frameworks/SwiftUI\.framework/SwiftUI" | wc -l )

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

检测 Swift

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

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 检测所有应用程序:

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++ 编写的。

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,我最终得到了这些行:

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++。

最终脚本

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

/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 类的文件,例如:

@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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容