swift编号:_Swift的切入点:如何

swift编号:

In this article, we’ll see how Swift determines the entry point of an app, the different attributes used to alter this behavior in iOS, how they work internally, and what Swift 5.3’s new @main attribute brings to the table.

在本文中,我们将看到Swift如何确定应用程序的入口点,用于在iOS中更改此行为的不同属性,它们在内部的工作方式以及Swift 5.3的新@main属性带到表中。

Swift脚本如何工作 (How Swift Scripts Work)

When executing Swift code from the command line, by default, the code will be read from top to bottom just like in any other scripting language. While Swift scripts support everything you’d have in any other context, they have the additional advantage of being able to write expressions in a global scope:

从命令行执行Swift代码时,默认情况下,将像其他任何脚本语言一样从上至下读取代码。 尽管Swift脚本支持您在任何其他上下文中拥有的所有内容,但它们还有一个额外的优势,就是能够在全局范围内编写表达式:

print("Hello")struct MyStruct {
func foo() {}
}MyStruct().foo()

In an iOS app, by default, doing so would result in the Expressions are not allowed at the top level error, but in a scripted world where you want to execute something as possible, there's no point in preventing this behavior.

默认情况下,在iOS应用中,这样做会导致Expressions are not allowed at the top levelExpressions are not allowed at the top level出现错误,但是在脚本化的世界中,您想尽可能执行某些操作,没有必要阻止这种行为。

Deep down, the ability to call code globally is just cool syntax sugar. What is really happening is that Swift is abstracting your global code inside a fake main C function:

内心深处,全局调用代码的能力只是很酷的语法糖。 真正发生的是,Swift在一个伪造的main C函数中抽象了您的全局代码:

Swift uses clang internally to compile and provide interoperability with C/C++/Objective-C, so it’s natural that the starting point of a script/app mimics it as well. You can check this with swiftc (file) -emit-sil, which will print Swift's Intermediate Language representation of your code after optimizing and generating any additional code it needs. In this case, we'll see a definition for a main function and our script's contents inside of it:

Swift内部使用clang进行编译,并提供与C / C ++ / Objective-C的互操作性,因此,脚本/应用程序的起点也自然会模仿它。 您可以使用swiftc (file) -emit-sil ,在优化并生成所需的任何其他代码后,该代码将打印代码的Swift中间语言表示。 在这种情况下,我们将看到一个main函数的定义以及其中的脚本内容:

sil @main
// a bunch of stuff that defines your top-level code!

Interestingly enough, it’s not possible for you to “steal” and define your own main function in that context. The generated main function is literally symbolized as main, while anything you define will internally have its symbol mangled (a unique identifier for your function or class based on the context where it was defined). For example, s4MyApp4mainCACycfc.

有趣的是,在这种情况下,您不可能“窃取”并定义自己的主要功能。 生成的main函数从字面上被符号化为main ,而您定义的任何内容都将在内部对其符号进行修饰(基于定义上下文的函数或类的唯一标识符)。 例如, s4MyApp4mainCACycfc

This is what happens when your Swift script/binary only contains one file, but what if it contains multiple files?

当您的Swift脚本/二进制文件仅包含一个文件时,会发生这种情况,但是如果它包含多个文件,该怎么办?

In this case, as we can’t have multiple starting points, we must designate one of them as the main one. Like before, the main file will be the entry point of the app and gain access to global expressions, but now, as expected, any additional file will have to follow your usual Swift rules. In Swift, designating the main file is just a matter of naming it main.swift:

在这种情况下,由于我们不能有多个起点,因此必须指定其中一个为主要起点。 和以前一样,主文件将成为应用程序的入口点,并可以访问全局表达式,但是现在,正如预期的那样,任何其他文件都必须遵循您通常的Swift规则。 在Swift中,指定主文件只是将其命名为main.swift

swiftc main.swift anotherFile.swift

@UIApplicationMain —当需要控制入口点时 (@UIApplicationMain — When the Entry Point Needs to Be Controlled)

But not all kinds of programs fit into this top-bottom code execution format. For example, in iOS, the execution of the app relies on running and maintaining a UIApplication instance, a process that is the same for every single iOS app. This initial process of booting a UIApplication is abstracted by UIKit, and there's no reason for you as a user to have to worry about it — especially considering this is the very first thing your app should do. For this reason, you could argue that handing this responsibility to the user could even have negative consequences. Imagine accidentally shipping a version where your app doesn't boot at all!

但是并非所有类型的程序都适合这种最底层的代码执行格式。 例如,在iOS中,应用程序的执行依赖于运行和维护UIApplication实例,该过程对于每个单个iOS应用程序都是相同的。 启动UIApplication最初过程是UIKit提取的,作为用户,您没有理由担心它-特别是考虑到这是您的应用程序应该做的第一件事。 因此,您可能会争辩说,将这一责任移交给用户甚至会产生负面影响。 想象一下,不小心发布了一个根本无法启动您的应用程序的版本!

To prevent this issue from happening, Apple thought that if UIKit is responsible for providing the code necessary to boot an iOS app, then it should probably get its hands dirty and do it itself. The Swift compiler then started supporting two new special attributes: @UIApplicationMain and @NSApplicationMain (for macOS).

为了防止发生此问题,Apple认为,如果UIKit负责提供启动iOS应用程序所需的代码,那么它可能应该动手做。 然后,Swift编译器开始支持两个新的特殊属性: @UIApplicationMain@NSApplicationMain (用于macOS)。

These attributes are magical, but they don’t deviate from what we already know. Their purpose is to automatically generate an entry point, and what happens internally is that the presence of these attributes will result in a fake main function being added to your binary:

这些属性是神奇的,但是它们并不偏离我们已经知道的内容。 它们的目的是自动生成一个入口点,内部发生的是这些属性的存在会导致将伪造的main函数添加到二进制文件中:

func main(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>) -> Int32 {
return UIApplicationMain(argc, argv, nil, ClassName)
}

The content of the function depends on the attribute you used, but as you can expect, it simply initializes your iOS app.

函数的内容取决于您使用的属性,但是正如您所期望的,它只是初始化您的iOS应用程序。

You might know UIApplicationMain() if you ever needed to use a subclass of a UIApplication in your app. This is the function that bootstraps an iOS app, and you can use the third and fourth arguments to change the classes you want to use as your UIApplication and UIApplicationDelegate. This means that there's nothing special about the @UIApplicationMain attribute, and you can reproduce what the compiler is doing by removing it and creating your own main.swift file. This is a legit technique to fine-tune the initialization of your app, which you can use to run code before your app launches (literally).

如果您需要在应用程序中使用UIApplication的子类,则可能知道UIApplicationMain() 。 这是引导iOS应用程序的功能,您可以使用第三个和第四个参数来更改要用作UIApplicationUIApplicationDelegate 。 这意味着@UIApplicationMain属性没有什么特别的,您可以通过删除它并创建自己的main.swift文件来重现编译器正在执行的操作。 这是微调应用程序初始化的一项合法技术,可用于在应用程序启动(字面意思)之前运行代码。

UIHooks.swizzleEverything()
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, "AppDelegate")

As a fake main function is emitted as a result of using the @UIApplicationMain attribute, you can't have both the attribute and a main.swift file. Trying to use both will result in a duplicated symbol error, and in some situations, the compiler will even give you a specific attribute cannot be used in a module that contains top-level code error.

由于使用@UIApplicationMain属性会发出伪造的main函数,因此不能同时拥有该属性和main.swift文件。 尝试同时使用两者将导致重复的符号错误,并且在某些情况下,编译器甚至会为您提供attribute cannot be used in a module that contains top-level code错误attribute cannot be used in a module that contains top-level code的特定attribute cannot be used in a module that contains top-level code

Swift 5.3和@main (Swift 5.3 and @main)

Many years later, it was realized that UIKit was not the only framework that benefited from controlling the entry point. Many frameworks for Swift CLI tools involve some sort of initial setup, and so it would be great if the language provided a standard way to replicate what was hardcoded as @UIApplicationMain. Finally, in Swift 5.3, the @main attribute was added to allow developers to control this behavior.

许多年后,人们意识到UIKit并不是唯一受益于控制入口点的框架。 Swift CLI工具的许多框架都涉及某种初始设置,因此如果该语言提供了一种标准方法来复制被硬编码为@UIApplicationMain内容,那将是很好的。 最后,在Swift 5.3中,添加了@main属性,以允许开发人员控制此行为。

@main struct MyScript {
static func main() throws {
print("SwiftRocks!")
}
}

@main works similarly to a protocol. When added, you must define a main() method that will serve as the entry point for your Swift code. Apart from that, @main works precisely like @UIApplicationMain. The fake main function is still emitted, but instead of returning the hardcoded UIKit behavior, it executes the function that you defined as a result of adding the attribute to a type. As seen at WWDC 2020, the new @main attribute is used by SwiftUI and the new Widgets extension to abstract the definition of their entry points.

@main工作原理类似于协议。 添加后,您必须定义一个main()方法作为您的Swift代码的入口点。 除此之外, @main工作方式与@UIApplicationMain 。 仍会发出伪造的main函数,但是它不会返回硬编码的UIKit行为,而是执行您将属性添加到类型后定义的函数。 如在WWDC 2020上所见,SwiftUI和新的Widgets扩展使用了新的@main属性来抽象其入口点的定义。

One particularly interesting use of it is in Apple’s swift-argument-parser library, which is a tool for creating commands and arguments for CLI tools. Before Swift 5.3, you had to manually initialize your tool by calling its main command’s main() method from the library, but now you can simply attach the new attribute to mark it as the starting point.

它的一个特别有趣的用途是在Apple的swift-argument-parser库中,该库是用于为CLI工具创建命令和参数的工具。 在Swift 5.3之前,您必须通过从库中调用工具的主命令的main()方法来手动初始化工具,但是现在您只需附加新属性即可将其标记为起点。

Before:

之前:

Post-Swift 5.3:

Swift 5.3后:

Because of this addition, using @UIApplicationMain is now officially deprecated, as Apple allows you to use the new @main attribute in your AppDelegates (except in the second Xcode 12 beta, where the feature was temporarily disabled).

由于添加了此功能,因此@UIApplicationMain现在已正式弃用,因为Apple允许您在AppDelegates中使用新的@main属性(第二个Xcode 12 beta中除外,该功能暂时被禁用)。

翻译自: https://medium.com/better-programming/entry-points-in-swift-how-main-and-uiapplicationmain-work-internally-1ebe0faae8d6

swift编号:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值