There are two kinds of attributes in Swift — those that apply to declarations and those that apply to types. An attribute provides additional information about the declaration or type.
简单来说,在 Swift 中,只有两种类型的属性:一种用于声明,另一种用于类型。属性提供关于声明或类型的附加信息。
Declarations Types #
在 Swift 中,@main
和 @available
都是 declarations attributes,用于修饰类、函数或其他代码元素,以提供特定的功能或条件。
@objc #
将 Swift 声明暴露给 Objective-C。
@main #
@main
用于标记 Swift 应用程序的入口点。用这个属性标记的类型会成为程序的启动点,相当于传统的 C 语言或者其他编程语言中的 main()
函数。在 Swift 5.3 中引入了 @main
属性,主要用于简化应用程序的启动配置。
一个典型的示例是 SwiftUI 应用程序的入口点:
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
在这个示例中,MyApp
结构体被标记为应用程序的入口点。Swift 程序在启动时会执行这个定义在 MyApp
结构体中的代码。
@available #
@available
用于声明代码的可用性,可以指定代码在不同操作系统、平台和版本中的可用性。从而使代码在编译和运行时提供相应的警告或错误提示,有助于保持代码的兼容性和稳定性。语法如下:
@available(platform version constraints, *)
其中:
platform
可以是iOS
,macOS
,watchOS
,tvOS
等。version constraints
指定代码在特定平台的版本要求。*
表示不指定其他平台。
常见的使用示例如下:
@available(iOS 14, macOS 11, *)
func newFeature() {
// 只在 iOS 14 和 macOS 11 及其之后的版本中可用的代码
}
// 或者用于类和结构体
@available(iOS 13.0, *)
class MyViewController: UIViewController {
// 类中的代码
}
property wrapper #
@propertyWrapper
#
@ViewBuilder
#
@ViewBuilder
是 SwiftUI 框架中的一个属性包装器,用于将包含多个视图的声明式代码块转换为单个聚合视图。这个功能使开发者可以方便地创建复杂的视图层次结构,同时保持代码的简洁和可读性。当您在 SwiftUI 中定义视图的初始化或布局时,常常会使用 @ViewBuilder
。
使用 @ViewBuilder
最直接的方法是定义一个自定义的 SwiftUI 视图。这里有一个简单的示例,展示如何在 SwiftUI 中使用 @ViewBuilder
:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello")
Text("World")
}
}
}
在这个示例中,VStack
使用了 @ViewBuilder
来接受多个视图。不过,@ViewBuilder
也可以被自定义视图使用。要理解和使用 @ViewBuilder
,我们可以创建一个自定义的容器视图,并在其中使用 @ViewBuilder
。以下是一个展示如何创建自定义视图并使用 @ViewBuilder
的详细示例:
import SwiftUI
struct CustomContainer<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
VStack {
content
}
}
}
struct ContentView: View {
var body: some View {
CustomContainer {
Text("Hello")
Text("World")
}
}
}
详细说明:
定义和初始化自定义视图:
CustomContainer
是一个通用的容器视图,接收一个内容视图Content
,该内容视图实现View
协议。- 在
init
方法中使用@ViewBuilder
属性包装器来定义参数content
,这个参数是一个返回Content
类型的闭包。
构建视图层次结构:
- 在
body
属性中使用VStack
作为容器,将内容闭包的返回视图包装起来。
- 在
使用自定义视图:
- 在
ContentView
中,可以看到我们使用CustomContainer
并传递两个文本视图:Text("Hello")
和Text("World")
。这些视图将会被VStack
包装,并显示在界面上。
- 在
@ViewBuilder
的优势
- 简洁和清晰:能够直观地组合多个视图,而看起来不会让代码变得冗长。
- 灵活性:适用于在不同层次结构中构建复杂的视图布局。
- 类型安全:通过 Swift 的类型系统,确保组合的视图都是有效的 SwiftUI 视图。
@ViewBuilder
是 SwiftUI 框架中一个强大的工具,帮助开发者以声明式的方式创建复杂的视图层次结构。借助 @ViewBuilder
,您可以轻松地定义自定义视图,并通过属性包装器将多个视图组合成一个整体,使您的 SwiftUI 代码更简洁、更优雅。
@Environment
#
https://developer.apple.com/documentation/swiftui/environment
@frozen @propertyWrapper
struct Environment<Value>
在 SwiftUI 中,@EnvironmentObject
和 @Environment
是用于在视图层次结构中共享和传递数据的属性包装器和环境变量。@EnvironmentObject
用于共享特定数据模型,而 @Environment
则用于读取系统级别的环境变量。下面是关于如何使用 @Environment
的简单示例:
1. 读取系统环境变量 #
@Environment
可以用于读取系统级别的环境变量,例如颜色方案、设备类型等。
示例代码
import SwiftUI
struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
Text("Color Scheme: \(colorScheme)")
}
}
}
在上面的示例中,通过 @Environment(\.colorScheme)
可以获取当前的颜色方案,然后在视图中显示当前的颜色方案。
2. 自定义环境变量 #
- 您还可以自定义环境变量,并在整个视图层次结构中共享和访问它们。
示例代码
import SwiftUI
struct CustomEnvironmentKey: EnvironmentKey {
static var defaultValue: String = "Default Value"
}
extension EnvironmentValues {
var customValue: String {
get { self[CustomEnvironmentKey.self] }
set { self[CustomEnvironmentKey.self] = newValue }
}
}
struct ContentView: View {
@Environment(\.customValue) var customValue
var body: some View {
Text("Custom Value: \(customValue)")
}
}
在上面的示例中,定义了一个自定义的环境键 CustomEnvironmentKey
和一个扩展 EnvironmentValues
,用于设置和访问自定义值。通过 @Environment(\.customValue)
可以在视图中访问自定义的环境变量。
结论 #
使用 @Environment
可以方便地读取系统级别的环境变量或自定义环境变量,并在整个视图层次结构中共享这些数据。
Type Attributes #
@escaping
的解释
#
在 Swift 中,@escaping
关键字并不是一个属性包装器(Property Wrapper),而是用于闭包的一个属性修饰符。理解 @escaping
以及它的用途,对于掌握 Swift 闭包的生命周期和使用场景至关重要。
当闭包作为函数参数传递时,默认情况下闭包是在函数体中执行的。换句话说,闭包的生命周期不会超出函数调用的生命周期。这种情况下,闭包被称为 “非逃逸”(non-escaping)。
但是,有些情况下,你可能需要在函数执行完毕之后,仍然保留并使用这个闭包。例如,当你将闭包作为回调存储并将在将来某个时间点调用时,你需要明确指出这个闭包是 “逃逸”(escaping)的,这样 Swift 编译器才能正确管理其生命周期。
语法 #
可以通过在闭包类型参数前面加上 @escaping
关键字,来标记这个闭包将会逃逸:
func someFunctionWithEscapingClosure(completion: @escaping () -> Void) {
// 存储闭包以便后续调用
someStoreClosure = completion
}
func someFunction(){
someFunctionWithEscapingClosure {
print("This closure is escaping")
}
}
使用场景 #
- 异步操作: 通常与异步操作如网络请求、延迟执行等场景相关联,这些操作会在函数返回之后才执行任务。
func performAsyncOperation(completion: @escaping () -> Void) {
DispatchQueue.global().async {
// 异步操作
completion()
}
}
- 存储属性: 将闭包存储为对象的属性,这些闭包会在函数返回之后才可能执行。
var completionHandler: (() -> Void)?
func someFunctionWithEscapingClosure(completion: @escaping () -> Void) {
completionHandler = completion
}
注意事项 #
- 在标记为
@escaping
的闭包中,如果访问self
的成员变量或者调用方法,需要使用self
来显示捕获以避免循环引用。这是因为逃逸闭包可能会在self
不再存在时仍然尝试访问它。
class SomeClass {
var someProperty: String = "Hello"
func doSomethingWithEscapingClosure(completion: @escaping () -> Void) {
DispatchQueue.global().async {
print(self.someProperty) // 需要使用 self
completion()
}
}
}
总之,@escaping
关键字用于标记将会在函数返回之后仍然存在和使用的闭包。理解并正确使用 @escaping
关键字有利于更好地进行异步操作处理和闭包的生命周期管理。