Swift — @ 符号之属性

Attributes

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 可以是 iOSmacOSwatchOStvOS 等。
  • 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")
        }
    }
}

详细说明:

  1. 定义和初始化自定义视图

    • CustomContainer 是一个通用的容器视图,接收一个内容视图 Content,该内容视图实现 View 协议。
    • init 方法中使用 @ViewBuilder 属性包装器来定义参数 content,这个参数是一个返回 Content 类型的闭包。
  2. 构建视图层次结构

    • body 属性中使用 VStack 作为容器,将内容闭包的返回视图包装起来。
  3. 使用自定义视图

    • 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 #

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 关键字有利于更好地进行异步操作处理和闭包的生命周期管理。

本文共 2473 字,上次修改于 Dec 16, 2024