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