Swift中的let和var背后的编程模式

Mac mini价格上涨背后:Swift开发实战与部署指南 在Apple生态开发中,硬件与工具链的匹配度往往决定开发效率。Swift作为iOS/macOS应用的核心语言,其编译性能与运行环境高度依赖Mac设备。而在网络层实现中,URLRequest的灵活运用与async/await并发模式能显著提升请求效率。同时,随着本地大模型部署需求增长,如何在Mac mini上高效运行模型推理成为开发者关注热点。本文从Swift开发基础出发,结合Mac mini从M1到M4的性能演进,分享网络请求封装、错误处理、并发编程、本地模型集成以及App上架全流程的实战经验。通过硬件选型 阅读详情

Swift中的let和var背后的编程模式

简介

Swift中有两种声明“变量”的方式,这两种方式分别使用let和var这两个关键字。这应该是借鉴了Scala,因为它们和Scala的val和var有相同的作用。let被用于声明不变量,var被用于声明变量。不变量的值一旦被定义就不能再改变,变量则可以在声明之后被随意赋值。

在其它一些如Java,C这样的命令式编程语言中也有不变量的概念。但多数情况下会被以常量形式使用,常量是静态的不变量。在Java中,通常用static和final一起来定义常量,其中static用于指明其是静态的,final用于指明其是不变的。Java中,我们有多种定义常量的方法:接口中定义,类中定义,使用枚举实现。这些方法之间的区别是在何时何地如何使用static和final。Objective-C,则和C语言一样,使用const关键字说明一个变量不应被改变。

在这类语言中,不变量和变量相比,通常是不寻常的,次一等的概念。如果将一个名字关联到一个值,缺省的会得到一个变量,而不是不变量。如果,你需要一个不会改变,一直和某个特定值绑定的名字,就需要显式说明它是不变的。例如,在Java中使用final,在C中使用const。这种缺省就是变量的情况,甚至影响了我们的语言。当我们需要描述,“声明用于和某个值关联的名字”时,我们说的是“声明变量”。但其实,这个“变量”应该加上引号,因为它其实可能是个不变量。这和指代不明确性别人时,使用“他”而不是“她”是同一类现象。

“缺省的是变量,如果需要不变量,请显式说明”。这是大多数命令式编程语言对变量和不变量的处理方法。这很自然。因为这类语言的设计中,大多数情况下使用的是变量,不变量只是在特殊情况下才需要。Swift(和Scala一样)则对这种设计做出了修改。从缺省是变量,转变为认为变量和不变量的地位是平等的。不变量应该更多被提倡和使用。在Swift的语法中,对这种设计思想的体现是:在定义一个和值关联的名字时,需要明确地使用var或let说明它是变量还是不变量。

Swift,和Java,C,Objective-C等语言相比,为何会有这种对待不变量的观点的变化呢?

变量和不变量其实源于两种不同编程范式。编程范式是编程语言设计者所持有的“世界观”的反映。
变量来源于命令式编程范式。这种编程范式将世界视为一系列独立的对象的组合,这些对象的行为可能会随着时间变化而不断变化。程序语言中的变量被用于模拟对象的状态。

不变量来源于函数式编程范式。这种编程以数学函数为建模核心。试图将世界抽象成为以一系列数学函数。数学函数中的变量其实和命令式编程语言中的变量存在着显著的区别。基于数学的函数式编程中的变量的概念更接近于命令式编程中的不变量。这在后续章节会详细讨论。

我们甚至可以通过对变量的态度来定义命令式编程和函数式编程:广泛采用赋值的程序设计被称为命令式程序设计;不使用任何被赋值的程序设计被称为函数式程序设计。这是因为,赋值操作使得变量可变。没有赋值操作,则变量不可变。

Swift受到了函数式编程的影响,强化了不变量在语言中位置,鼓励不变量的使用。

函数式编程中的变量

函数式编程以数学函数为建模基础。其变量的概念和数学中变量的概念是一致的。所以,我们可以先回顾一下数学函数中变量的概念。由于现在绝大多数程序设计语言是命令式的,所以我们通常所说的变量是命令式编程中的定义,这和数学函数中的变量并不相同。

在数学中,函数是描述每个输入值对应唯一输出值的这种对应关系。数学函数中的变量是一个用于表示值的符号,值是可以是随意的,也可能是未定的。所以,在数学函数中,某个符号我们之所以称其为变量,是因为它可以用于代表不同的值。而需要指明的是:当我们用明确的数值代入函数运算时,变量就拥有了明确的值。而在一次代换过程中,变量一旦被代换为明确的值,就不会再次改变为其它值。数学函数中不存在这种情况:某一次代换过程中,某个变量x一开始被代换为2,然后又变为3。这在数学上,没有任何意义。

这样看起来,数学函数中的变量其实应该可以对应程序语言中的不变量:一旦被定义,就不再变化。纯粹的函数式编程语言就完整继承了这种数学上的变量概念。例如,Haskell就没有可变量的概念,声明一个变量,只能被赋值一次,之后就不会再变化。而命令式编程语言中,变量被定义之后,仍然能够随意被赋予其它的值。

比如我们有一个简单的数学函数:

f(x) = 2*x + x * x

如果,我们遵循数学函数对变量的看法,可以将其翻译为如下的Swift函数。这个程序函数和上面的数学函数,在概念上是等价的。

func foo(x: Int) -> Int {
    return 2*x + x * x
}

当然,这个Swift函数foo,还有其它现实方法。函数的另外一种实现bar为了展示y是一个命令式编程里的变量,而稍显怪异。但它仍然能得到和上面的函数相同的答案:代入任意相同的x值,两个函数都会得到相同的返回值。但由于数学函数中不存在y这样的一开始等于某个值,而后又被赋为另一个值这样的命令式编程中的变量概念。所以,我们没有办法将下面这样的Swift函数bar还原为一个概念上一致的数学函数。

func bar(x: Int) -> Int {
    var y = 2 * x
    y =  y + x * x
    return y
}

Swift中提供let声明不变量,更为重视不变性,明确鼓励在更多的场合使用不变量。这都是受函数式编程中变量的不变性的影响。后面会讨论Swift为何会受到这种影响。

命令式编程中的变量

命令式编程语言中的变量的概念为大多数程序员所熟悉。我们将其和函数式编程中的变量做一个对比:在函数式编程中,变量其实并不可变,这种变量只是一个代表了某个值的符号。而在命令式编程中,由于变量是可变的,变量就不仅仅是简单代表一个值的符号,而是索引了一个可以保存值的位置,在这个位置上可以存放不同的值。

我们的世界中每个对象都有着自己随着时间变化的状态。而在不同时刻,变量可以代表了不同的值,使得变量拥有了时序上的概念。我们就可以使用变量来模拟和刻画现实世界中的对象的状态。这其实也是为何会引入赋值,使得变量可变的原因。

引入赋值的好处

如果使用过一些函数式编程语言,就会发现部分函数式编程语言并没有完全抛弃赋值。在Scheme中,我们仍然可以用(set! x 15)这样的语句为变量赋值,变量将在赋值前后和不同的值关联。为何这些函数式编程语言没有完整地贯彻变量的不变性呢?

函数式编程语言出现的时间很早,最早的函数式编程语言Lisp是历史第二悠久的高级编程语言(仅次于Fortran)。但现在函数式编程语言并没有成为绝大多数程序员的工作语言。为何现今流行的编程语言:C,Java,C++,Python都是命令式编程语言呢?

这是因为,引入赋值,使得变量可变。就引入了一个简单直观又易于模块化的程序语言建模方法。这在设计大型软件系统时是一个巨大的优势。

命令式编程的建模思想是一种直观的世界观:“世界是由聚集在一起的一系列独立的对象组成的”。但这仅仅是在一个维度上的描述。另外一个时间维度上的描述通常不被提及:“每个对象都有着随时间变化的状态”。综合来说就是:“世界由对象组成,对象都有状态”。将这种直观的世界观引入程序设计所带来的好处是,建模更为简单了。使用这种思想的编程语言对于程序员来说也更为简单直观了。那么将一个实际问题用这种编程语言中的概念来描述,也就变得更轻松了。因为,程序员通常能够为实际问题中的事物一一对应地构建对象,并按时序描述每个对象的状态。

如果将赋值和局部变量结合,构造带局部状态的对象,就可以提供一种有利于系统模块化设计的技术。这是一种强大的设计策略,原因在于它的简单和直观。我们可以直接构造那些用于模拟真实物理系统的对象。对于问题域里的每个对象,我们都可以构造一个与之相对应的计算机程序里的对象。如果,我们能把对象的“状态”局限在对象内部,使之成为“局部状态”(这其实就是封装)。然后,将各自具有“局部状态”的对象组合,这会是一个良好的模拟真实世界的手段。

我们之所以可以使用UML(Unified Modeling Language)来分析项目需求,是因为我们将在项目中使用命令式编程语言。从UML这种图形化的辅助建模方式中,我们可以更明显地看到如何将真实世界中的对象和程序语言中的对象一一对应,如何将真实世界中的对象的一个个属性和程序语言中的对象的变量一一对应。

如果,使用函数式编程语言,UML将不再能起到任何作用。你需要的是一个类似将现实问题抽象为数学问题的过程。这种数学的建模方式对大多数人来说可能都会更为困难一些。

引入赋值的代价

在函数式编程中引入赋值,存在着一些争议。仍然有如Haskell这样的函数式编程语言,坚持纯粹的函数式编程思想,不使用任何赋值操作(当然,仍然有使用不变量难以描述的情况存在。Haskell社区称这部分为有副作用的,不纯的。这部分代码会被限制在Monad中实现)。

也有Swift和Scala这样的新兴语言,重新思考函数式编程语言中不变性的意义。在语言设计中,强调和重视不变性。

这是因为没有免费的午餐。引入赋值,除了上节所说的带来了一个简单直观又易于模块化的程序语言建模方法之外,也引入了一些缺陷,我们需要为此付出一些代价。其中一些缺陷使得我们在构建大规模软件系统时,遇到了一些难以克服的困难。

更复杂的计算模型

为函数式编程语言引入赋值语句,使得变量可变。看起来只是多了赋值语法,但其实这并不是一件简单的事情。赋值的引入对编程语言造成的影响是巨大的:随着赋值的引入,我们必须为编程语言引入一种更为复杂的计算模型。

在没有赋值语句之前,纯函数式编程语言可以使用数学上的代换模型来构建语言的计算模型:一个变量可以安全地被代换为它所代表的表达式或者值。求值一个纯函数式编程语言中的函数,和求值一个数学函数并没有什么区别。你可以认为编程语言的运行方式和数学的运算方式是一样的。这种代换模型其实是一个相当简单的语言模型。

但在引入赋值之后,变量在程序运行的某些时刻代表一个值,在另一些时刻代表另外一个值。代换模型就不再有效了。因为,代换模型基于数学模型。数学上并没有在某些时刻代表一个值,在另一些时刻代表另外一个值的变量概念。如果尝试对带有赋值操作的函数进行代换,会发现当遇到赋值语句时,代换过程无法进行下去。因为变量已经不能被再被看做是某个值的名字了。此时的变量以某种方式指定了一个“位置”,我们可以将任何值存储在该“位置”。那到底是将哪个值代入变量呢?在代换模型中,无法解决该问题。

为了解决这个问题,我们引入更为复杂的环境模型。变量将维持在我们称为“环境”的结构中。环境包含一系列约束,这些约束将一些变量的名字关联到对应值。在环境模型中,变量的值将取决于其所处的环境。程序运行过程中,环境时常变化,变量的值也就随之改变。

引入更复杂的计算模型意味着实现编程语言变得更为困难了。

同一问题的复杂化

相等的判断

我们抛开具体的程序语言讨论一下如何判断对象相等。在程序语言中,有一种从效果上判断相同的方法:如果在任意计算中用一个对象替换另外一个对象,都不会改变结果,那么我们就可以认为这两个对象相等。

如果,没有赋值操作存在。我们判断对象相等会简单一些。例如,在下面例子中,let使Point的实例变量x和y都成为不变量。p1和p2的x,y相等,而且两个点的x,y值都不会改变。所以,可以认为在任何时候的任何计算中,p1和p2都是可以相互替换的。我们就可以认为p1和p2相等。

struct Point
 {
  let x: Double
  let y: Double
}
let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)

但是,如果我们使用var来声明Point的实例变量。下面例子中的p1和p2相等的结论就不一定正确了。因为,我们可以使用赋值操作来改变点的实际坐标了。当执行p1.x = 2之后,显然它们就无法在任何计算中相互替换了。我们不能认为p1和p2相等了。

struct Point {
  var x: Double
  var y: Double
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1, y: 2)

可以看到在引入赋值之后,判断两个对象是否相等的问题变得更为复杂了。

别名

在拥有赋值操作后,另外一个经常引起困惑和错误的是别名问题。一个对象可以通过多个名字访问的的现象称为别名。下面展示了一个别名的最简单的例子:

class Point {
  var x: Double
  var y: Double
  init(x: Double, y: Double) {
    self.x = x
    self.y = y
  }
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1, y: 2)
var p3 = p1
p3.x = 2

上面代码中,p1和p2是两个独立对象,p3是p1的别名。这两组关系之间有微妙的区别,我们常常在实际编程过程中混淆两者。p1和p2对各自的修改互不影响,可以认为它们是两个独立的点。而p1和p3可以认为是一个点。对其中任何一个的修改都会造成另一个也同样被修改。如果,我们想在程序中搜索出p1可能被修改的地方,就必须记住,也要检查那些修改了p3的地方。然而在实际编程中,特别是在大型复杂系统中,我们常常会忘记,或者根本就不知道p3是某个对象(这里是p1)的别名。要么,修改了p3,却不知道也造成了p1的修改。这种副作用常常防不胜防,在编程中经常出现。要么,在需要对修改操作做重新设计时,只顾及了p3,而忘记同时也要修改p1的地方。这种别名常常难以被识别而被遗忘。

但是,如果没有赋值操作,别名造成的困扰就消失了。即使在实际物理内存上,这两组关系并不相同:p1和p2指向两块不同的内存地址,p1和p3指向同一块内存地址。但你仍然可以认为p1,p2,p3是相等的对象。因为,在没有赋值的情况下,它们在任何计算中都可以相互替换。是否是别名在计算中并没有什么区别。

值类型和引用类型

也许有人发现:开始,我们使用结构体(struct)实现Point,而后在解释别名问题时又改用类(class)实现Point。这是因为Swift扩大了值类型的使用范围。

在Java中,可以认为原始类型(int,long,float,double,short,char,boolean)是值类型,而其它继承自Object的类型都是引用类型。

而在Swift中,结构体被设计成一种值类型。整数,浮点数,布尔值,字符串,数组和字典在Swift中都是以结构体的形式实现的,所以,它们也都是值类型。特别是数组,字典这种常用集合类型也被实现为值类型,使得值类型在Swift中的使用范围大大扩展了。

值类型在被赋给一个变量,或者被传递给函数时,实际上是做了一次拷贝。与值类型对应是引用类型。引用类型在被赋给一个变量,或者被传递给函数时,是传递的是引用。类(class)仍然是引用类型。所以,类实现的Point会有别名的问题。而值类型不会有这类别名所带来的问题。

在下面用结构体实现Point的例子中,p3不再是p1的别名,而是p1的一个拷贝。

struct Point {
  var x: Double
  var y: Double
}
var p1 = Point(x: 1, y: 2)
var p2 = Point(x: 1, y: 2)
var p3 = p1

我们可能会问一个问题:如果每次赋值都进行拷贝,是否会大大增加内存开销呢?如果每次赋值都进行对象拷贝,确实会增大内存开销。Swift的解决方案是:只在值类型发生改变时才进行拷贝。就上面的结构体实现的Point的例子而言,var p3 = p1虽然进行了赋值,但这时还并没有发生拷贝操作。这时,p3其实仍然是p1的别名,它们指向同一个内存地址。直到我们改变p3了,比如执行p3.x = 2时,才会先发生拷贝,然后在拷贝的副本上进行赋值修改操作。这么做当然节省了内存开销。而可以这么做的根据是:没有赋值操作时,同一问题更简单了,别名并不会带来问题。在这种没有赋值的情况下,值类型和引用类型其实可以被认为是等效的。

扩大值类型的使用范围是Swift减缓别名问题的一种方式。另外一种方式,则是我们在本文中一直讨论的:由于赋值操作的引入,使得同一问题复杂化了。那么,即使现在做不到完全去除赋值操作,一定程度上鼓励不变性,在需要的环境中使用不变量,也能缓解这种复杂性所带来的问题。

赋值顺序

可以举一个求阶乘的例子来说明,赋值语句的相对顺序对结果的影响

func factorial(n: Int) -> Int {
  var product = 1
  var i = 1
  while i <= n {
    product = i * product
    i = i + 1
  }
  return product
}

并发问题

在单线程环境中,考虑赋值操作的相对顺序对程序运行结果正确性的影响,仍然可以算是一个相对简单可控的问题。但如果是在多线程环境中,就会延伸出一些更严重的问题。

我们考虑一个简单银行账户系统,并考虑一下并发存款或者取款的情形:

class account {
  var balance: Double
  init(balance: Double) {
    self.balance = balance
  }
  func withdraw(amount: Double) {
    let newBalance = self.balance - amount // #1
    self.balance = newBalance              // #2  
  }
  func deposit(amount: Double) {
    let newBalance = self.balance + amount
    self.balance = newBalance
  }
}
let george = account(balance: 100)
let paul = george
george.withdraw(10)
paul.withdraw(20)

这当然是错误的结果,余额最开始为100元,George取了10元,Paul取了20元,余额应该是70元。银行因为这个并发错误亏损了10元。仔细查看以上过程,可以发现错误发生在Paul将余额更新为80元时,其实存在一个前提:更新之前余额应该是100元。但不幸的是在George将余额修改为90元之后,上述前提已再不合法。更不幸的是,在实际情况中,这类错误并不是每次都会发生。这取决于各个线程以何种顺序执行代码。而这种不能稳定复现的错误,常常难以修复。

这个错误也揭示了,时间在程序中所产生的影响。计算结果需要依赖各个赋值发生的顺序。并发情况下,正确地控制这种顺序变得更加复杂了。

很多工具和并发控制策略被发明出来用于解决并发问题:原子操作,阻塞,信号,锁。但这些工具和策略仍然很复杂,让程序员掌握这些工具并不容易,有些还会影响程序的运行效率。而且例如死锁这样的问题,即使引入复杂的死锁避免技术,在一些地方也仍然无法完全避免。

引入赋值之前,程序没有时间的问题,变量任何时候具有某个值,将总是具有这个值。引入赋值之后,我们就必须开始考虑时间在计算中的作用。在并发情况下,由赋值引入的复杂性变得更加严重了。需要在程序中考虑时间的作用的负担变得越来越严重了。

时至今日,要编写线程安全的,且性能可靠的并发环境下执行的程序,对命令式编程语言来说,仍然是严峻的考验。这个问题直接促使Swift,Scala这样的新兴语言开始从函数式编程语言中寻找灵感,来解决或者缓解并发问题。

总结

Swift中有两个声明变量的关键字:let和var。这两个关键字背后存在着两种截然不同的编程思想:函数式编程和命令式编程。Swift对这两种编程思想进行了融合:它允许你使用引入赋值所带来的简单直观的建模方法。同时也鼓励你使用不变性缓解各类并发问题。

32、Swift 并发编程:异步任务与序列的深度解析 本文深入解析了 Swift 中的并发编程技术,涵盖 async let、任务组(Task Groups)和异步序列(Asynchronous Sequences)等核心特性。通过具体示例,展示了如何高效地处理多个异步任务,包括固定数量任务的并发执行(分叉与合并架构)、动态数量任务的处理(任务组)、以及异步数据流的遍历(异步序列)。同时探讨了线程调度机制、主线程与后台线程的区别、线程安全问题的解决方案(如 Actor),并提供了并发编程的最佳实践,包括错误处理、减少锁的使用、合理分配任务和异步 I/O 操作。 阅读详情

相关推荐

基于QT的华为网络摄像头二次开发

第一次使用华为的网络摄像头并进行二次开发,网上关于QT对华为网络摄像头的二次开发的文章很少,基于项目的需求做了一些工作,特此记录。 目标: 1、实时预览 2、手动控制录制视频 3、监听告警型号 4、实时获取元数据中的划定区域内的人数 摄像头型号如下: 准备工作: 在购买摄像头后,应当在官网下载对应的开发资料,如果需要权限,则联系华为的技术支持人员。下载资料如下: SDC Development Kit Demo 8.0.T9 SDK DEMO下载地址:https://bbs.hua

xumingyifrend的博客 3169

1. Swift 中的 let 和 var

Swift 中的变量声明相对于 OC 中简化了很多。可变与不可变取决于声明的方式,不在取决于类型了,这样简化了很多不必要的类型。比如 OC 中可变的为 NSMutableArray,不可变的是 NSArray,而 Swift 中的数组不管可变与不可变就是 Array 。 Swift 中变量的声明 在 Swift 中声明变量有两种方式: let,var Swift 中的变量的声明方...

Yan 1万+

Mac用户解决WPS中的俄语显示红线问题(WPS添加外语拼写检查)

我所使用的WPS版本:AppleStore 下载的 MacOs版 3.3.0 首先,对于 在访达-应用程序中,找到WPS,右键-点击“显示包内容” 然后按照下图的索引目录(Contents\Resource\office6\dicts\spellcheck),最后进入一个叫“spellcheck”的文件夹 最后将下载好的字典包(例如上图中的 en_US就代表美式英语;ru_RU就代表俄语) 关于字典包资源下载,我在网上找到一个Google云盘,里面包含了法语,德语,西语,意大利语,葡萄牙.

qq_48159150的博客 7893

Swift -> Let & Var 背后编程模式 探讨

简介 Swift中有两种声明“变量”的方式,这两种方式分别使用let和var这两个关键字。这应该是借鉴了Scala,因为它们和Scala的val和var有相同的作用。let被用于声明不变量,var被用于声明变量。不变量的值一旦被定义就不能再改变,变量则可以在声明之后被随意赋值。 在其它一些如Java,C这样的命令式编程语言中也有不变量的概念。但多数情况下会被以常量形式使用,常量是静态的...

weixin_30906671的博客 282

心电图机原理及电路超详细讲解

前置心电放大电路(虚线框:这是隔离电路的分界,在隔离电路分析中知道,完整隔离包括信号隔离(脉宽调制)、控制信号隔离(光电耦合)和电源隔离)后级放大电路。

fcxgfdjy的博客 2万+

【ROS】usb_cam相机标定

1. 唠叨两句 当我们要用相机做测量用途时,就需要做相机标定了,不然得到的计算结果会有很大误差,标定的内容包括三部分:内参,外参还有畸变参数。所以标定的过程就是要求得上面这些参数。 以前弄这个事估计挺麻烦,需要做实验和计算才能得到,现在通过ros的开源包几分钟就能完成相机标定,感激! 具体的内外参和畸变系数的说明,可以看看Reference里面的第一个链接,写得很详细。 2. 准备工作 要准备三样东西: usb_cam的ROS启动包 相机标定的ROS包 还有一个用于标定的棋盘格 1)usb_cam启动包

Will_Ye的博客 8201

Swift 中 let 和 var 区别

let 是不可变对象,对象首地址不能发生变化,也就是说内存地址不能改变,但也可以修饰可变对象 例: let str:String=“aaa” str=“bbb”//报错,let修饰的变量不能改变指针指向 let array:NSMutableArray=NSMutableArray() array.add(“jjjj”)//不报错,数组没有重新指向 var代表可变对象,内存地址可以发生变化,用来修...

weixin_46642112的博客 1130

从d、q轴电流到电机性能:FOC控制中角度偏移的实战影响与调试策略

本文深入探讨了永磁同步电机FOC控制中d、q轴电流与角度偏移对电机性能的影响及调试策略。通过分析角度偏移导致的效率下降、电流异常等问题,提供了实用的四步调试法和参数优化技巧,帮助工程师有效提升电机控制精度和效率。

weixin_29264201的博客 294

Swift 专题二 语法速查

在这个例子中,area是一个计算属性,它只需要返回矩形的面积,不需要设置值,所以可以省略get关键字。当访问area属性时,就会执行花括号内的代码来计算并返回矩形的面积。

m0_61164038的博客 1365

第14.8章 高翔博士最新开源框架Lightning-LM源码解读(八):位姿平滑器PoseSmoother深度剖析,从原理到代码实现

本文深度解析了高翔博士开源框架Lightning-LM中的位姿平滑器PoseSmoother模块。该模块通过设置平滑因子,在保证位姿准确性的同时减少突变,使位姿变化更平稳。文章详细讲解了模块功能、数据流程和核心代码实现,包括构造函数、拷贝构造函数以及关键的PushDRPose和PushPose函数。重点分析了位姿平滑算法原理,涉及运动有效性校验、位姿队列维护和平滑因子调节等核心机制。此外还介绍了相关C++语法(如显式构造函数、默认拷贝构造)和优化方向。本解析为SLAM学习者提供了位姿平滑处理的实用实现范例。

行知SLAM 600

基于YOLO26深度学习框架的文档表格识别系统的算法研究实现方法

本文研究了基于YOLO26深度学习框架的文档表格识别系统实现方法。系统采用YOLO26网络架构,通过数据预处理、模型训练、检测推理和表格分析四个模块实现高效表格识别。研究详细阐述了数据归一化、增强等技术处理,以及采用迁移学习策略优化模型训练过程。实验表明,该系统能有效识别多种表格结构并生成结构化数据,为文档自动化处理提供了可靠解决方案。

一键难忘的博客 971

Swift基本语法-简例

1,变量与常量 var mutableVal = 11 mutableVal = 30 let val = 20 let valDouble:Double = 50 2,字符串拼接 let value1 = "swift is" let value2 = 666 let total = value1 + String(value2) let value3 = "I think \(value1) best." 3,多行字符串 let value = "is sure." let moreValue =

MOON_YZM的博客 1039

安全运营面试总结

答案可以在我的博客中查找 1.自我介绍 2.TCP/IP七层模式 3.端口号(常见协议 数据库) 4.Owasp top 10 5.渗透测试过程 6.漏扫工具 7.中间件漏洞 8.应急响应 9.项目 10.SQL注入类型 11.TCP 三次握手 四次挥手 12.你有什么要问我的 1.自我介绍 2.项目介绍 3.宽字节注入的原理 4.渗透流程 5....

qq_51577576的博客 4413

swift1.1-常量和变量

定义 let被用于声明不变量,不变量的值一旦被定义就不能再改变 var被用于声明变量,变量则可以在声明之后被随意赋值。 变量名 常量和变量名可以包含任何字符,包括 Unicode 字符. 常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。 一旦你将常量或者变量声明为确定的类型,你就不能使用...

Sweety 337

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Swift 6高级编程技巧:泛型与元编程实战指南

Swift 6为开发者带来了前所未有的泛型与元编程能力,这两个核心特性共同构成了现代Swift开发的基石。泛型提供了类型安全的抽象机制,而元编程则让编译时代码生成成为现实,两者的结合使得Swift在保持高性能的同时,实现了前所未有的代码复用和开发效率提升。本文将深入探讨如何在实际项目中巧妙运用这些高级特性,构建更加灵活、高效的Swift应用程序。 ## 一、Swift泛型系统的深度解析 🚀

gitblog_00208的博客 651

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Natalie源码解析:Swift代码生成器背后的核心架构与设计模式

Natalie 是一款专为 Swift 打造的 Storyboard 代码生成器,它能自动读取工程中的 `.storyboard` 文件并生成强类型 Swift 代码,让你彻底告别字符串硬编码的 Segue 和 Storyboard 标识符。本文从源码角度拆解这款 Swift 代码生成器的核心架构、模块分工与设计模式,帮助你快速读懂它的设计精髓,也为想自己动手写代码生成工具的同学提供一份完整的参考

gitblog_00575的博客 1086

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Swift Evolution 常见被否决提案清单解析:读懂 Swift 语言设计决策背后的权衡

本篇技术指南围绕 swift-evolution 仓库中由语言指导组(LSG)维护的「常见被否决提案」清单 [commonly_proposed.md](https://link.gitcode.com/i/bfa48dbdda00e119061ca2dcdbc23f19) 展开,系统梳理 Swift 对缩进语法、分号、逻辑运算符、`guard`、强制解包、GC 等高频提案的否决理由,并结合仓库内

gitblog_01053的博客 977

SE-0205 深度解析:Swift 中 `withUnsafePointer` 与 `withUnsafeBytes` 对不可变值(let)的只读指针访问

本文围绕 Swift 标准库提案 [SE-0205](https://link.gitcode.com/i/248b99fc1b5f4606d3b2b24ce2159e76) 展开,讲解 `withUnsafePointer(to:_:)` 与 `withUnsafeBytes(of:_:)` 这对顶层函数如何从"仅接受 `inout` 可变参数"扩展为"直接接受只读值(`let` 常量与临时值)

gitblog_00523的博客 493

如何用Voice-Clone-Studio生成90分钟多说话人播客:VibeVoice与Qwen3对话引擎完整指南

**Voice-Clone-Studio** 是一个基于 Gradio 的语音克隆 Web 应用,由 Qwen3-TTS 与 VibeVoice 双引擎驱动,内置 Conversation(对话生成)工具,支持一次生成**最长 90 分钟、最多 4 位说话人**的连续播客音频。本文带你从安装、准备声音样本到写出播客剧本,一步步用 VibeVoice 长文本引擎生成多说话人播客,并对比 Qwen3

gitblog_01110的博客 457

go-stock如何完成AI股票分析与选股:免费本地股票分析工具完整实战指南

你关注的股票在行情软件里,新闻在财经电报里,K线在画图软件里,做一次判断要翻好几个窗口。go-stock 把 AI 股票分析、AI 选股和行情提醒放进同一个本地桌面程序:接上大模型后,一句提问就能拿到一份带数据的分析报告,所有数据都留在本地。 ## 📥 下载绿色版,首次初始化几分钟即可用 到项目的 Releases 页面拿文件:Windows 是 `go-stock-windows-amd6

gitblog_00013的博客 1109

Swift Snippets 完全指南:用 SE-0356 为 Swift 包编写可编译、可运行、可嵌入文档的示例代码

本指南以 Swift Evolution 提案 [SE-0356《Swift Snippets》](https://link.gitcode.com/i/99260988704b06591fef7bbbf6c6e023)(Swift 5.7 已实现)为核心,系统讲解 Swift 包中一种全新的示例代码形态——**snippets**:它是短小、单文件、可被 Swift Package Manage

gitblog_00047的博客 792

Swift 6元编程与泛型:5个代码生成技巧提升开发效率

Swift 6为开发者带来了革命性的元编程能力和增强的泛型系统,让代码生成和类型安全达到了新的高度。在这篇文章中,我们将探索如何利用这些新特性来显著提升开发效率,编写更简洁、更安全的Swift代码。 ## 为什么需要元编程和泛型? 在日常开发中,你是否遇到过这些情况? - **重复的样板代码**:每个模型都要手动实现`Equatable`、`Codable`等协议 - **类型安全的缺失**

gitblog_00226的博客 653

Swift 编程语言中文版精读:结构体与类的定义、值语义与引用语义全解析

结构体(`struct`)和类(`class`)是 Swift 程序代码的基石——它们是可以封装数据、提供功能的自定义模型类型。本文以《The Swift Programming Language(中文版)》语言指南中的「结构体和类」章节([TSPL.docc/LanguageGuide/ClassesAndStructures.md](https://link.gitcode.com/i/7e5

gitblog_00622的博客 678

PromiseKit 实战指南:告别常见误用,掌握 Swift Promise 链式编程的核心技巧

PromiseKit 是 Swift 与 Objective-C 生态中成熟的 Promise 库,其价值不仅在于提供 `Promise`、`Guarantee` 等异步抽象,更在于它鼓励一种"可组合、可预测"的异步编程范式。本文基于仓库中的 [Documentation/Appendix.md](https://link.gitcode.com/i/73a0a580087a03c8d3c078f

gitblog_01069的博客 129

PromiseKit 快速上手指南:用 Promise 简化 Swift 与 Objective-C 异步编程

PromiseKit 是一个面向 Swift 与 Objective-C 的完整 Promise 实现,其核心目标是用一条条可读的链式调用取代层层嵌套的回调,把异步编程从"回调地狱"中解放出来。本文以仓库根目录的 [README.md](https://link.gitcode.com/i/cd5799dd636303e21e0ea58ac7cc0f09) 为主线,结合 [Sources](htt

gitblog_00812的博客 353

干草堆上的光影:Swift 6迁移与并发编程实战周报

Swift语言从诞生起就坚守着值类型、协议导向与内存安全等核心原则,这为开发者提供了稳定而可靠的语言地基。在技术演进中,Swift 6语言模式带来了更严格的并发检查,但也引发了大量关于Sendable、@MainActor与AsyncStream的实战问题。理解并发隔离的本质,掌握从Swift 5到Swift 6的渐进式迁移策略,是当前苹果生态开发者提升代码安全性与性能的关键路径。与此同时,SwiftUI与SwiftData的工程实践,以及Xcode编译优化和依赖管理技巧,共同构成了现代iOS开发的高频技能

weixin_34037515的博客 346
上一篇: swift操作符的重载,自定义操作符
下一篇: iOS 启动优化
Philm_iOS
博客等级 码龄8年 74粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值