Swift踩坑——对“属性”的理解

本文深入探讨Swift中的属性概念,包括存储属性、计算属性、延迟存储属性及属性观察者,通过实例详细解释每种属性的特点与应用场景。

本文原载于我的博客:https://www.seekingmini.top/archives/swift踩坑对属性的理解

写在前面

在参考了官方英文文档以后,对于属性这个概念有了一些自己的思考。

属性

官方没有给出一个明确的定义,只是告诉我们属性是干什么的:

Properties associate values with a particular class, structure, or enumeration.

属性将值和特定的类、结构体或者枚举关联。这话说了跟白说一样。我们先暂且不给属性下定义,先来看看属性的分类。属性有两种——存储属性计算属性

存储属性

In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure.

提取关键句——存储属性是一个常量或者变量,这个变量或者常量是特定的类、结构体或者枚举的实例的一部分。这里我们至少知道了存储属性是一个常量或者变量。我们继续看:

Stored properties can be either variable stored properties (introduced by the var keyword) or constant stored properties (introduced by the let keyword).

存储属性分为变量存储属性常量存储属性。来看个例子:

struct Person {
    let name: String
    var age: Int
}

var person = Person(name: "Bob", age: 22)

上面的例子中,name是一个常量存储属性,而age是一个变量存储属性。从字面上来看,存储属性是用来存值的,我们可以对这个属性读或写(取决于它是常量或者变量)。

延迟存储属性

A lazy stored property is a property whose initial value is not calculated until the first time it is used.

当一个存储属性加了修饰符lazy时,只有当我们第一次使用这个属性时,它的值才会被计算。看个例子:

class DataImporter {
    var filename = "data.txt"
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
}

var manager = DataManager()
manager.data.append("data one")
manager.data.append("data two")
print(manager.importer.filename)  // 第一次使用

当我们初始化变量manager时,变量importer事实上还没有被初始化,或者说不存在,只有第一次使用它时(即运行代码print(manager.importer.filename))它才会被初始化。这么做可以一定程度上减少计算资源的消耗。

计算属性

官方的定义如下:

In addition to stored properties, classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.

可见计算属性是不存值的,只有在我们getset这个变量的时候才会采取一些行动。首先来看看get的用法:

struct Person {
    let name: String
    var age: Int
    var type: String {
        get {
            if self.age > 18 {
                return "adult"
            } else {
                return "teenager"
            }
        }
    }
}

var person = Person(name: "Bob", age: 14)
print(person.type)

在变量person初始化时,type是没有初始化的,只有当运行代码print(person.type)时,才会根据get语句里的代码进行计算。这与下面的代码有根本性区别:

struct Person {
    let name: String
    var age: Int
    var type: String { self.age > 18 ? "adult" : "teenager" }
}

var person = Person(name: "Bob", age: 14)
print(person.type)

这段代码里的type在变量person初始化的时候就已经计算出值了。接下来看看set的用法:

struct Person {
    let name: String
    var age: Int
    var type: String {
        get {
            if self.age > 18 {
                return "adult"
            } else {
                return "teenager"
            }
        }
        set (t) {
            if t == "old" {
                self.age = 65
            }
        }
    }
}

var person = Person(name: "Bob", age: 14)
print(person.type)
person.type = "old"
print(person.age)
print(person.type)

以上代码的运行结果是:

teenager
65
adult

我们来分析一下过程。首先第一个print(person.type)就不用分析了,直接来看person.type = "old"这段代码。当运行这段代码时,触发了set语句,把age重新赋值为65,但是第二个print(person.type)的结果仍然是"adult"而不是"old",是因为get语句的作用。

属性观察者

Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.

当一个属性值被set的时候,属性观察者会采取一些行动。属性观察者分为willSetdidSet两种:

  • willSet is called just before the value is stored.
  • didSet is called immediately after the new value is stored.

来看个例子:

struct Town {
    var population: Int = 0{
        willSet(newPopulation) {
            print("小镇的人口马上要变成\(newPopulation)了!")
        }
        didSet {
            if (population > oldValue) {
                print("小镇的人口从原来的\(oldValue)增长了\(population - oldValue)!")
            } else if population == oldValue {
                print("小镇的人口没有增长,还是\(oldValue)。")
            } else {
                print("小镇的人口从原来的\(oldValue)减少了\(oldValue - population)!")
            }
        }
    }
}

var town = Town()
town.population = 100
town.population = 500
town.population = 50

以上代码的运行结果是:

小镇的人口马上要变成100了!
小镇的人口从原来的0增长了100!
小镇的人口马上要变成500了!
小镇的人口从原来的100增长了400!
小镇的人口马上要变成50了!
小镇的人口从原来的500减少了450!

我们来逐行分析一下。

  1. 对于town.population = 100这段代码,首先运行willSet中的代码,即打印小镇的人口马上要变成100了!,由于100大于初始值0,所以运行didSet中的代码,即打印小镇的人口从原来的0增长了100!
  2. 对于town.population = 500这段代码,首先运行willSet中的代码,即打印小镇的人口马上要变成500了!,由于500大于100,所以运行didSet中的代码并且选择if (population > oldValue)这个分支,即打印小镇的人口从原来的100增长了400!
  3. 对于town.population = 50这段代码,首先运行willSet中的代码,即打印小镇的人口马上要变成50了!,由于50大于500,所以运行didSet中的代码并且选择else这个分支,即打印小镇的人口从原来的100减少了450!

(未完待续)

同步定位与地图构建(SLAM)技术为移动机器人或自主载具在未知空间中的导航提供了核心支撑。借助该技术,机器人能够在探索过程中实时构建环境地图并确定自身位置。典型的SLAM流程涵盖传感器数据采集、数据处理、状态估计及地图生成等环节,其核心挑战在于有效处理定位与环境建模中的各类不确定性。 Matlab作为工程计算与数据可视化领域广泛应用的数学软件,具备丰富的内置函数与专用工具箱,尤其适用于算法开发与仿真验证。在SLAM研究方面,Matlab可用于模拟传感器输出、实现定位建图算法,并进行系统性能评估。其仿真环境能显著降低实验成本,加速算法开发与验证周期。 本次“SLAM-基于Matlab的同步定位与建图仿真实践项目”通过Matlab平台完整再现了SLAM的关键流程,包括数据采集、滤波估计、特征提取、数据关联与地图更新等核心模块。该项目不仅呈现了SLAM技术的实际应用场景,更为机器人导航与自主移动领域的研究人员提供了系统的实践参考。 项目涉及的核心技术要点主要包括:传感器模型(如激光雷达与视觉传感器)的建立与应用、特征匹配与数据关联方法、滤波器设计(如扩展卡尔曼滤波与粒子滤波)、图优化框架(如GTSAM与Ceres Solver)以及路径规划与避障策略。通过项目实践,参与者可深入掌握SLAM算法的实现原理,并提升相关算法的设计与调试能力。 该项目同时注重理论向工程实践的转化,为机器人技术领域的学习者提供了宝贵的实操经验。Matlab仿真环境将复杂的技术问题可视化与可操作化,显著降低了学习门槛,提升了学习效率与质量。 实践过程中,学习者将直面SLAM技术在实际应用中遇到的典型问题,包括传感器误差补偿、动态环境下的建图定位挑战以及计算资源优化等。这些问题的解决对推动SLAM技术的产业化应用具有重要价值。 SLAM技术在工业自动化、服务机器人、自动驾驶及无人机等领域的应用前景广阔。掌握该项技术不仅有助于提升个人专业能力,也为相关行业的技术发展提供了重要支撑。随着技术进步与应用场景的持续拓展,SLAM技术的重要性将日益凸显。 本实践项目作为综合性学习资源,为机器人技术领域的专业人员提供了深入研习SLAM技术的实践平台。通过Matlab这一高效工具,参与者能够直观理解SLAM的实现过程,掌握关键算法,并将理论知识系统应用于实际工程问题的解决之中。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值