大师学SwiftUI第6章 - 声明式用户界面 Part 4

news2025/1/3 3:30:31

步进器视图

​Stepper​​视图创建一个带递增和递减按钮的控件。该结构体提供了多个初始化方法,包含不同的配置参数组合。以下是最常用的一部分。

  • Stepper(String, value: Binding, in: Range, step: Float, onEditingChanged: Closure):此初始化方法创建一个​​Stepper​​​视图。第一个参数定义标签,​​value​​​参数是希望用于存储当前值的绑定属性,​​in​​​参数是允许的最大最小值范围,​​step​​​参数是一个指定递增或递减值​​Float​​​或​​Double​​​(取决于绑定属性。),​​onEditingChanged​​参数是在用户开始及结束编辑该值时执行的闭包。
  • Stepper(String, onIncrement: Closure?, onDecrement: Closure?, onEditingChanged: Closure):该初始化方法创建一个​​Stepper​​​视图。第一个参数定义标签,​​onIncrement​​​参数是在用户点击+按钮时执行的闭包,​​onDecrement​​​参数是在用户点击-按钮时执行的闭包,​​onEditingChanged​​参数是在用户开始及结束编辑该值时执行的闭包。

要实现一个​​Stepper​​​视图,我们需要一个存储当前值的​​@State​​属性,并定义希望用户选取的范围值。

示例6-41:创建一个步进器

struct ContentView: View {
    @State private var currentValue: Float = 0
    
    var body: some View {
        VStack {
            Text("Current Value: \(currentValue.formatted(.number.precision(.fractionLength(0))))")
            Stepper("Counter", value: $currentValue, in: 0...100)
            Spacer()
        }.padding()
    }
}

​Stepper​​​视图使用​​Float​​​或​​Double​​类型的浮点值,因此我们将值格式化为显示整数。结果如下所示。

图6-27:步进器

图6-27:步进器

默认,值按1个单位递增或递减,但我们可以通过​​step​​​参数来进行修改。下例定义了一个按5个单位递增或递减的​​Stepper​​视图。

示例6-42:定义步进器的步长

struct ContentView: View {
    @State private var currentValue: Double = 0
    
    var body: some View {
        VStack {
            Text("Current Value: \(currentValue.formatted(.number.precision(.fractionLength(0))))")
            Stepper("Counter", value: $currentValue, in: 0...100, step: 5)
            Spacer()
        }.padding()
    }
}

类似​​Toggle​​​视图,​​Stepper​​​视图通过水平堆叠以及标签与控件之间的弹性空间实现。如果希望提供自己的标签并对控件定义自定义位置,我们需要应用​​labelsHidden()​​修饰符,如示例6-33。下例定义了一个自定义标签并通过​​onIncrement​​​和​​onDecrement​​参数创建一个视图在屏幕上显示箭头在告知用户最终是递增或递减。

示例6-43:在值递增或增减时修改界面

struct ContentView: View {
    @State private var currentValue: Float = 0
    @State private var goingUp: Bool = true
    
    var body: some View {
        VStack {
            HStack {
                Text("Current Value: \(currentValue.formatted(.number.precision(.fractionLength(0))))")
                Image(systemName: goingUp ? "arrow.up" : "arrow.down")
                    .foregroundColor(goingUp ? Color.green : Color.red)
                Stepper("", onIncrement: {
                    currentValue += 5
                    goingUp = true
                }, onDecrement: {
                    currentValue -= 5
                    goingUp = false
                }).labelsHidden()
            }
            Spacer()
        }.padding()
    }
}

本例中,我们定义了两个​​@State​​​属性:​​currentValue​​​用于存储步进器的当前值,布尔类型的属性​​goingUp​​​用于表示最终值是做了递增还是递减。各视图位于​​HStack​​​中并排显示。第一个是和之前一样的显示步进器当前值的​​Text​​​视图。在它之后,有一个​​Image​​​视图,检测​​goingUp​​​属性来根据属性值朝向、朝下的SF图标。同一个属性用于决定箭头的颜色。最后,我们定义了带​​onIncrement​​​和​​onDecrement​​​参数的​​Stepper​​​视图。在赋值给这些参数的闭包中,我们按5进行递增和递减,并修改​​goingUp​​属性的值来表示最终是做了递增还是递减。结果就是,用记点击+按钮时看到绿色的向上箭头,点击-按钮时为红色的向下箭头。

图6-28:自定义步进器

图6-28:自定义步进器

组合框视图

SwiftUi内置了一个​​GroupBox​​视图用于在一堆视图周边创建一个框。该视图定义有背景色以及视觉上对视图和控件分组的圆角。以下是该视图的一个初始化方法。

  • GroupBox(String, content: Closure):该初始化方法创建一个​​GroupBox​​​视图。第一个参数定义在框顶显示的标签,​​content​​参数是一个闭包,定义组中所包含的视图。

该视图默认样式带有背景色,因此我们增压机实现它并添加一个包含希望放到框内视图的闭包。

示例6-44:定义一个视图分组

struct ContentView: View {
    @State private var setting1: Bool = true
    @State private var setting2: Bool = true
    @State private var setting3: Bool = true
    
    var body: some View {
        GroupBox("Settings") {
            VStack(spacing: 10) {
                Toggle("Autocorrection", isOn: $setting1)
                Toggle("Capitalization", isOn: $setting2)
                Toggle("Editable", isOn: $setting3)
            }
        }.padding()
    }
}

图6-29:组合框

图6-29:组合框

模型

专业应用中包含有多个视图,分别表示可以导航的不同界面。这些视图要访问同样的数据并对应用中状态的改变进行响应。因此 ,这些应用必须可供访问的独立数据源并可由所有视图修改。这一数据源通常称之为模型。模型是应用基本结构的一个部分。在这一范式中,一组长结构体或对象定义该模型(应用的数据及状态),而连接数据的视图在屏幕上展示数据并根据用户的输入更新模型。

示例6-30:数据模型

示例6-30:数据模型

这种组织方式无法通过​​@State​​属性创建。前面示例中使用的​​@State​​属性封装器只能存储控制单一视图状态的值。我们需要的是一个可以传递给其它视图并对系统上报变更的对象。SwiftUI内置了如下的宏来定义这一对象。

  • @Observable:这个宏对类添加允许属性存储和管理应用状态所需的代码。

借助这个宏,我们可以将任意的类转换为可观测对象,也就是说我们可以使用该对象的属性存储和管理应用的状态。下面可以看到这种类定义的示例。我们称之为​​ApplicationData​​,但可以使用任意其它的名称。注意​​@Observable​​宏在Observation框架中定义,因此需要导入该框架才能使用这个宏。

示例6-45:在可观测对象中存储数据

import SwiftUI
import Observation

@Observable class ApplicationData {
    var title: String = "Default Title"
    var titleInput: String = ""
}

这一模型包含两个属性。名为​​title​​的属性用于存储书的标题,另一个名为​​titleInput​​的属性用于允许用户输入新标题。但因为我们是使用​​@Observable​​宏来修改该类,就无需再在视图中声明​​@State​​属性了。这个宏会处理该属性存储状态和上报变更到系统所需要代码的生成。

重要:定义用于存储数据的类型是应用的核心部分,可能会很大,因此将它们放到单独的Swift文件中比较合理。只需打开屏幕上方的File菜单,选择New/File,再在iOS面板中选择Swift File图标(见图5-107)。就会添加该文件,文件内部所定义的数据可在代码的任意位置访问。

有了模型之后,我们需要创建一个该类的实例并将其传递给视图。对于简单的应用,我们只需要创建实例并将其赋值给视图的属性即可,如下例所示。

示例6-46:初始化可观测对象

struct ContentView: View {
   var appData = ApplicationData()
    
    var body: some View {
        VStack(spacing: 0) {
            Text(appData.title)
                .padding(10)
            Button(action: {
                appData.title = "New Title"
            }, label: {
                Text("Save")
            })
            Spacer()
        }.padding()
    }
}

和之前极其相似,但不再读取和存储​​@State​​属性中的值,而是对​​ApplicationData​​对象的属性进行操作。视图中包含一个读取​​title​​属性的​​Text​​视图并在屏幕上显示其值,以及一个对属性赋值新字符串的按钮。在点击按钮时,新字符串New Title被赋值给了​​title​​属性,该属性向系统上报变更,系统更新视图来在界面中显示新的值。

✍️跟我一起做:创建一个多平台荐。打开屏幕顶部的File菜单,点击New/File选项创建一个新的Swift文件(见图5-107)。将文件命名为​​ApplicationData.swift​​,使用示例6-45中的代码替换其中的内容。使用示例6-46中的代码更新​​ContentView​​视图。此时会在屏幕上看到一个标题和一个按钮。点击按钮,标题会发生改变。

在本例是中,我们显示了​​title​​属性并在点击按钮时修改了其值,但前面我们已经学到,有控件可以让用户输入新值。此时,我们需要创建一个双向绑定以便每次用户与控件进行交互时,新值存储到相应的属性中,界面得到更新。为此,我们需要使用如下的属性包装器将可观测对象声明为可绑定。

  • @Bindable:这一属性包装器在属性和可观测对象之间创建了一个双向绑定。

在下面的代码中,我们对前例添加了一个​​TextField​​视图来演示如何与可观测对象建立双向绑定。

示例6-47:与可观测对象建立双向绑定

struct ContentView: View {
   @Bindable var appData = ApplicationData()
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: $appData.titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = appData.titleInput
                appData.titleInput = ""
            }, label: {
                Text("Save")
            })
            Spacer()
        }.padding()
    }
}

和之前一样,我们需要在属性前添加一个美元符号来告知​​TextField​​视图要存储用户插入的值。因为使用​​@Bindable​​修改了​​appData​​属性,可观测对象接收到值并存入模型中。在点击按钮时,我们执行与之前一样的流程。用户插入的字符被赋值给了​​title​​属性,屏幕上的界面进行更新显示这些字符。

✍️跟我一起做:使用示例6-47中的代码更新​​ContentView​​视图。在界面中会看到标题、文本框以及一个按钮。在文本框中插入文本并按下按钮。输入的文本会替换掉原标题。

示例6-45的模型中,我们定义了两个属性,​​title​​用于存储实际信息,​​titleInput​​用于接收用户的输入。应用中稍后添加的其它视图可能需要访问​​title​​属性来向用户显示其值,但​​titleInput​​属性仅在包含​​TextField​​视图的视图中用到。也就意味着我们在应用的模型存储了视图的私有状态。虽然这种方法没什么错,但推荐做法是用模型存储应用数据,但在视图内管理视图的状态。可以实现不同的模式来组织应用。一种方法是为每个视图分别定义​​@State​​属性,类似前面那样,但另一种试是创建额外的可观测对象。例如,我们可以从模型中删除​​titleInput​​属性,并为视图定义一个可观测对象来管理用户的输入,如下所示。

示例6-48:定义一个视图级别的可观测对象

import SwiftUI
import Observation

@Observable class ViewData {
    var titleInput: String = ""
}

struct ContentView: View {
   @Bindable var viewData = ViewData()
    var appData = ApplicationData()
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: $viewData.titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = viewData.titleInput
                viewData.titleInput = ""
            }, label: {
                Text("Save")
            })
            Spacer()
        }.padding()
    }
}

基本和之前一样,只是定义了一个针对 ​​ContentView​​视图的可观测对象​​ViewData​​。现在用户插入的字符存储于这一对象的​​titleInput​​属性中,因此模型和视图状态做了隔离。在用户点击Save按钮时,我们将​​titleInput​​属性的值赋值给​​title​​属性,这样新标题就存储到了模型中。

✍️跟我一起做:删除​​ApplicationData​​类中的​​titleInput​​属性。使用示例6-48中的代码更新​​ContentView.swift​​文件。功能和此前一样,但现由视图自己取代模型来管理其状态。

使用可观测对象来代替​​@State​​属性管理视图状态的一个好处是,更易于动态初始化对象的属性。例如,我们可以在​​ContentView​​结构体初始化时将存储在模型中的当前标题赋值给​​titleInput​​属性,这样用户就可以在屏幕上看到之前的值。

示例6-49:初始化视图的可观测对象

init() {
        viewData.titleInput = appData.title
    }

这个初始化方法将模型的​​title​​属性值赋值给​​viewData​​对象的​​titleInput​​属性,因而​​TextField​​视图在整体视图出现时显示当前值。

图6-31:使用模型中的值初始化文本字段

图6-31:使用模型中的值初始化文本字段

✍️跟我一起做:将示例6-49中的初始化方法添加到​​ContentView​​结构体中(​​body​​属性定义的上方)。赋值给模型中​​title​​属性的字符串会如图6-31那样出现在文本框内。

另一种使用可观测对象的属性或​​@State​​属性初始化的方法是​​onAppear()​​修饰符。如前所见,这一修饰符在视图出现于屏幕上时执行一个闭包。例如,我们可以将其赋值给​​ContentView​​视图的​​VStack​​视图来在屏幕上显示视图时初始化​​titleInput​​属性。

示例6-50:在视图出现时初始化视图的可观测对象

.onAppear {
    viewData.titleInput = appData.title
}

✍️跟我一起做:从​​ContentView​​结构体中删除示例6-49中添加的初始化方法。对​​VStack​​视图添加示例6-50中的​​onAppear()​​修饰符(位于​​padding()​​修饰符下方)。结果和前面一样。

虽然可观测对象对于构建应用的模型以及管理视图状态很好,但视图并不一定需要在值发生改变时进行更新。如果可观测对象中存在属性,不需要在每次发生值改变时更新视图,就可以实现如下的装饰宏。

  • @ObservationIgnored:这个宏生成取消指定属性观测的代码。

例如,我们可以对可观测对象添加一个属性,用于对本例视图中Save按钮点击次数进行计数。

示例6-51:取消可观测对象中属性的观测

import SwiftUI
import Observation

@Observable class ViewData {
    var titleInput: String = ""
    @ObservationIgnored var counter: Int = 0
}

struct ContentView: View {
   @Bindable var viewData = ViewData()
    var appData = ApplicationData()
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: $viewData.titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = viewData.titleInput
                viewData.titleInput = ""
                viewData.counter += 1
                print("Current Counter: \(viewData.counter)")
            }, label: {
                Text("Save")
            })
            Spacer()
        }.padding()
    }
}

每次按下Save按钮时,我们都对可观测对象中的​​counter​​属性累加1,但因为这个属性通过​​ObservationIgnored​​宏进行修饰,视图不会在每次值发生改变时更新。

访问模型

我们的应用可能一个展示菜单的视图、一个展示内容列表的视图以及另一个显示用户所做选择信息的视图。所有这些视图都必须访问相同的数据,因此都要包含对模型的引用。将指针从一个视图传到另一个视图直到需要使用值的视图比较笨重且容易出错。更好的选择是将模型的指针传入环境,然后在需要时从环境中读取引用。

图6-32:通过环境访问模型

图6-32:通过环境访问模型

如前所述,环境是存储应用与视图相关信息的通用容易,但它也可以存储自定义数据,包含对可观测对象的引用。在图6-32中,可观测对象的一个实例添加到环境中,然后仅由需要使用它的视图访问。

可观测对象通过​​environment()​​修饰符添加,并通过​​@Environment​​属性所定义的属性访问。必须要考虑的是​​environment()​​修饰符将对象赋值给一个视图层级的对象,因此我们必须将其应用于界面上所有视图的初始视图才能访问它。下例展示了我们需要对​​App​​结构体所做的修改,创建了一个​​ApplicationData​​对象并将其添加到应用的初始视图的环境中(​​ContentView​​视图)。

示例 6-52:将可观测对象赋值给视图的环境

import SwiftUI

@main
struct TestApp: App {
    @State private var appData = ApplicationData()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(appData)
        }
    }
}

​ApplicationData​​类的实例必须存储于​​@State​​属性中。有了这个对象后,我们对​​ContentView​​应用​​environment()​​修饰符将对象注入到环境中。在视图中访问对象很简单。只需要通过​​@Environment​​属性包装器创建一个属性,如下所示。

示例6-53:从环境中获取可观测对象的引用

import SwiftUI
import Observation

@Observable class ViewData {
    var titleInput: String = ""
}

struct ContentView: View {
    @Bindable var viewData = ViewData()
    @Environment(ApplicationData.self) private var appData
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: $viewData.titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = viewData.titleInput
                viewData.titleInput = ""
            }, label: {
                Text("Save")
            })
            Spacer()
        }.padding()
    }
}

#Preview {
    ContentView()
        .environment(ApplicationData())
}

使用​​@Environment​​属性包装器,我们可以在界面中的任意视图中访问模型。但这并不适用于不同层级的视图,比如由预览所创建的​​ContentView​​视图。这一视图有其自身的层级和环境,因此我们需要再创建一个模型实例,将其注入到预览自己的环境中才能使用。

✍️跟我一起做:使用示例6-52中的代码更新​​App​​结构体,并用示例6-53中的代码更新​​ContentView.swift​​文件。应用和之前功能相同,这现在是通过环境获取可观测对象,因此它们在​​ContentView​​相同层级的所有视图中都可使用。我们会在第8章中学习如何对相同层级添加更多视图。

如果希望环境属性能够处理双向绑定属性,需要使用​​@Bindable​​属性包装器将其转化为可绑定属性。例如,我们像示例6-45中那样将用户输入存储到模型的属性中,还需要像如下那样创建单独的视图来处理其值。

示例6-54:将环境属性转变成可绑定属性

struct ContentView: View {
    @Environment(ApplicationData.self) private var appData
    
    var body: some View {
        MyInputView(appData: appData)
    }
}

struct MyInputView: View {
    @Bindable var appData: ApplicationData
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: $appData.titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = appData.titleInput
                appData.titleInput = ""
            }, label: { Text("Save") })
            Spacer()
        }.padding()
    }
}

上例中,我们像之前一样从环境中访问模型,但现在将值传递给另一个视图​​MyInputView​​。这个视图通过​​@Bindable​​属性包装器使得模型可绑定,此时就可以使用模型中的​​titleInput​​属性管理用户输入的值。

如果仅部分属性需要双向绑定,我们可以不使用属性包装器,而是通过​​Bindable​​结构体逐一转化属性。下例和之前功能一致,但我们通过仅在需要时将​​appData​​属性转化为可绑定属性简化了代码。

示例6-55:通过​​Bindable​​结构体创建一个可绑定属性

struct ContentView: View {
    @Environment(ApplicationData.self) private var appData
    
    var body: some View {
        VStack(spacing: 8) {
            Text(appData.title)
                .padding(10)
            TextField("Insert Title", text: Bindable(appData).titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                appData.title = appData.titleInput
                appData.titleInput = ""
            }, label: { Text("Save") })
            Spacer()
        }.padding()
    }
}

✍️跟我一起做:使用示例6-54中的代码更新​​ContentView.swift​​,再使用示例6-45中的​​ApplicationData.swift​​。应用功能和之前相同,但现在可以管理模型中的双向绑定属性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1394686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Prism框架的WPF前端框架开发《知产代理数字化解决方案》

最近新开发了一套WPF前端界面框架,叫《知产代理数字化解决方案》,采用了时下流行的Prism框架作为整个系统的基础架构,演示了Prism中的IRegionManager区域管理器、IDialogAware对话框、IDialogService对话框服务、IContainerExtension容器等用…

财务思维一张图

系统研读了《肖星的财务思维课》和得到《贾宁财务思维课》,尝试用一张最简单的图总结财务思维所学到精髓吧。 《肖星的财务思维课》 得到《贾宁财务思维课》 附: 【清华大学】最好的财务课《商业的财务逻辑》(全7课)肖星教授 【…

Multimodal Multitask Learning with a Unified Transformer

SNLI-VE dataset,natural language understanding tasks:MNLI,QNLI,QQP,SST-2 截止到发文时间的issue数,多吓人呐,不建议复现

猫头虎博主第9期赠书活动:《YOLO目标检测》计算机AI视觉实战YOLO人工智能目标检测与跟踪图像处理深度学习图像检测书籍

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通Golang》…

分析Unity崩溃日志(Android)

Unity版本: 这里Unity版本是Unity 2019.4.30f1 崩溃信息: 错误信息如下: Exception Appversion: versionName 1.0.1 versionCode 1 Process: sg.atla.f.c PID: 22716 UID: 10749 Flags: 0x3088be44 Package: sg.atla.f.c v1 (1.0.1) Fo…

.NET架构师:全网最全“权限系统”设计剖析

🏆作者:科技、互联网行业优质创作者 🏆专注领域:.Net技术、软件架构、人工智能、数字化转型、DeveloperSharp、微服务、工业互联网、智能制造 🏆欢迎关注我(Net数字智慧化基地),里面…

flutter3使用dio库发送FormData数据格式时候的坑,和get库冲突解决办法

问题描述 问题1:当你使用FormData.from(Flutter3直接不能用)的时候,可能会提示没有这个方法,或者使用FormData.fromMap(flutter3的dio支持)的时候也提示没有,这时候可能就是和get库里面的Formdata冲突了 问题1:The me…

Gateway网关限流

在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止恶意网络攻击 文章目录 一、常见限流场景1.1 限流的对象1.2 限流的处理1.3 限流的架构 二、常见的限流算法2…

HTML5+CSS3+JS小实例:实时给中文添加拼音

实例:实时给中文添加拼音 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"&…

GoZero微服务个人探究之路(零)个人对微服务产生原因的思考,对前三篇的补充

为什么产生了微服务架构--必要性 这里我觉得看GoZero作者写的博文就可以有很好的体会 具体的&#xff0c;他画了这一张图&#xff08;以电商后台系统为例子&#xff09; 所以&#xff0c;我个人产生了如下思考 1.业务逻辑越来越复杂&#xff0c;层层嵌套&#xff0c;分解成微…

力扣刷MySQL-第三弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…

CSS||Emmet语法

1、简介 ​ Emmet语法的前身是Zen coding,它使用缩写,来提高html/css的编写速度, Vscode内部已经集成该语法。 ​ 快速生成HTML结构语法 ​ 快速生成CSS样式语法 2、快速生成HTML结构语法 生成标签 直接输入标签名 按tab键即可 比如 div 然后tab 键&#xff0c; 就可以生成 <…

如何使用VNC实现Win系统远程桌面Ubuntu图形化界面【内网穿透】

文章目录 推荐前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 …

Go 中 slice 的 In 功能实现探索

文章目录 遍历二分查找map key性能总结 之前在知乎看到一个问题&#xff1a;为什么 Golang 没有像 Python 中 in 一样的功能&#xff1f;于是&#xff0c;搜了下这个问题&#xff0c;发现还是有不少人有这样的疑问。 补充&#xff1a;本文写于 2019 年。GO 现在已经支持泛型&am…

[Linux 进程(五)] 程序地址空间深度剖析

文章目录 1、前言2、什么是进程地址空间&#xff1f;3、进程地址空间的划分4、虚拟地址与物理地址的关系5、页表的作用扩展 6、为什么要有地址空间&#xff1f; 1、前言 Linux学习路线比较线性&#xff0c;也比较长&#xff0c;因此一个完整的知识点学习就会分布在两篇文章中&…

龙腾荆楚 | 软件供应链安全检测中心落地襄阳

1月16日&#xff0c;襄阳市东津新区“园区提质、企业满园”行动暨2024年东津云谷首月重大项目集中签约活动圆满完成&#xff0c;开源网安城市级项目再下一城&#xff0c;分别与襄阳市政府、高校、国投签订战略合作协议&#xff0c;推动荆楚地区数字政府、数字经济、数字社会、数…

首次公开发声,OpenAI CEO 奥特曼回忆“宫斗门”丨 RTE 开发者日报 Vol.129

开发者朋友们大家好&#xff1a; 这里是「RTE 开发者日报」&#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

风丘科技为您提供完整的ADAS测试方案

一 方案概述 随着5G通讯与互联网的快速发展&#xff0c;智能汽车和ADAS辅助系统的研究与发展在世界范围内也在如火如荼地进行。风丘科技紧跟时代脚步&#xff0c;经多年积累沉淀&#xff0c;携手整车厂与高校共同研发打造出了一套完整且适用于国内ADAS测试的系统方案。 | ADAS…

Python-基础篇-类与对象/面向对象程序设计-py脚本

面向对象基础 第一个面向对象 class Cat:def eat(self):print("小猫爱吃鱼")def drink(self):print("小猫要喝水")# 创建猫对象 tom Cat()tom.eat() tom.drink()print(tom)addr id(tom) print("%x" % addr)新建两个猫对象 class Cat:def ea…

【现代控制系统】LTI系统的反馈结构和状态估计器

LTI系统的反馈结构和状态估计器 2023年12月13日 #controlsys 文章目录 LTI系统的反馈结构和状态估计器1. 线性系统的反馈结构1.1 状态反馈/线性直接状态反馈1.2 反馈至状态微分的输出反馈1.3 反馈至参考输入的输出反馈 2. 状态反馈的极点配置算法2.1 状态反馈渐进跟踪问题——…