Swift简介

如果您曾经对开发用于iOS的应用感兴趣,那么可以肯定,您可能偶然发现的第一件事就是它们曾经使用一种叫做Objective-C的语言制作。 而且,如果您对C或C ++不太熟悉,其语法可能会让人感到不知所措,甚至令人恐惧。 但是不要害怕! 苹果大约在四年前推出了一种名为Swift的新编程语言,现在它已成为开发所有iOS应用程序的官方支持的编程语言。 这并不意味着所有了解Objective-C的人都无法继续创建iOS应用,但是Swift引入了一些工具,可以使程序员的生活更轻松。 凭借其简洁的语法和严格但安全的键入,您可以确定Swift与Apple以前的语言相比具有许多巨大的优势。 对于使用Java或Java的人来说,他们可能会觉得它非常熟悉,因此学习曲线对您来说不应该那么陡!

Swift于2014年6月在Apple全球开发者大会(WWDC)上以beta版首次发布,但直到同年9月才为Apple开发人员公开发布了Swift 1.0版本。 但是,自2016年9月起,版本3.0已发布,并且引入了一些有趣的更改。 可以肯定地说,苹果可能会每年或至少在每个主要的iOS版本中继续发布新版本。 尽管新功能总是令人兴奋,但不断发展的语言很容易破坏我们以前的代码。 Mac的官方IDE Xcode包括一个助手,可以帮助开发人员升级其代码。 但是,助手无法解决所有单个冲突,程序员最有可能需要进行手动更改。

根据Swift的项目网站,该语言具有三个主要功能:

  • 安全性 :由于编程语言是强类型化的,因​​此除非有意忽略编译器的警告,否则几乎不可能出现有关数据类型和错误转换的运行时错误。 此外,空指针的使用受到限制,可以使用称为“可选”的特殊数据类型来使用。 也强烈建议使用常量而不是变量。
  • 速度 :Swift承诺与其替代的语言(Objective-C)及其表亲C和C ++一样快,因此您可以期望该语言具有恒定的速度,而不会出现“短暂爆发”的情况。
  • 富有表现力 :它的语法是现代的,并且受益于代码样式的一些最新趋势,从而使程序员可以大大减少他们键入的代码量。 这意味着随着语言的不断发展,它也可能会遭受变化。

出乎意料的是,苹果公司在2015年12月宣布,根据Apache Commons许可(当时的版本为2.2),Swift将作为开源项目重新发布,以及其核心库和其他工具,例如编译器和调试器。 从那时起,开发人员开始采用该编程语言,并正在构建多个库甚至Web支持。 Mac OS本机支持该语言,目前还支持Linux。 尽管Windows暂未在任何地方提及,但程序员可以使用联机编译器进行尝试,但没有任何东西说永远不会支持Microsoft的操作系统。 时间会证明一切。

尽管Swift主要用于开发应用程序,但它不仅仅局限于创建iOS和Mac OS应用程序,因为它被认为是一种多用途的编程语言。 因此,仅凭Swift便无法为Apple的小工具开发应用程序。 那就是两个库发挥作用的地方。 这些称为CocoaCocoa Touch的框架具有相同的用途,分别为iOS和Mac OS X操作系统创建应用程序。 根据Apple的文档,Cocoa是一个术语,用于引用用Objective-C运行时构建的任何类或对象,并且是从NSObject类继承的。 因此,它们包含具有类的特定库,这些类将帮助开发人员创建用户界面并与每个小工具的硬件和软件的各个组件进行交互。 这些库中有一个名为Foundation的多用途库,其中包含一组标准工具,用于国际化的原始数据类型和实用程序,对象持久性,文件管理,XML处理,网络连接等的实现。

此时,您可能想知道“如果这些框架是用Objective-C编写的,那么Swift如何使用它们”? 确实,这些库不是用Swift编写的,但这不会阻止语言使用它们。 如何实现这一点将在本文中进一步解释。

最初,此文章是作为针对试图学习新语言的Java开发人员的网络研讨会的一部分而创建的。 这就是为什么我专注于语言的某些语法特性,这些特性在Java中是不同的,但它们可能仍然非常相似,而在本出版物最初发行之时,Java中还没有包含一些新特性。

变量和常量

与任何编程语言一样,您需要在某个点存储值才能进行计算。 在Swift中,您可以使用var关键字声明变量,并使用let声明常量。 关键词。

  变数= 55 
number = 10 //为变量分配新值
让boxWidth = 78

不用说,变量可以随时更改其值,而常量在初始化之后就不能更改其值,因为在运行时这样做会导致运行时异常。 正如您可能在前面的代码片段中已经注意到的那样,声明并未在任何位置指示变量是哪种数据类型。 这并不意味着Swift没有类型,相反,Swift是一种强类型的编程语言。 但是,它使用一种称为类型推断的机制来确定变量应为哪种类型。 因此,如果在初始化期间为变量分配一个整数,Swift将知道您希望它的类型为Int 。 如果使用十进制数字或为它们分配一个字符串,也会发生相同的情况。 接下来是有关数据类型的更多信息。

Swift的一个显着特征是,您可以用来命名变量的字符可以是任何有效的Unicode字符。 这样就可以使用其他语言中的字符,例如西班牙语中的ñ或亚洲语言中的字符和符号。 可能令人惊讶的是,这还使变量名也可以成为表情符号。

  令π= 3.14159 
let你好=“你好世界”

唯一的限制是来自Apple文档的以下限制:

“常量和变量名称不能包含空格字符,数学符号,箭头,专用(或无效)的Unicode代码点或线条和框形图字符。 它们也不能以数字开头,尽管数字可能包含在名称中的其他位置。”

如果您开始阅读Swift文档,您可能会开始注意到它如何使用常量声明,这在其他编程语言中并不常见。 有人可能会说他们甚至滥用了常数,但这是有目的的。 这就是我所说的Swift的“ taboos”之一。 您可以使用vars,但除非完全必要,否则不应该使用它们。 编译器实际上会警告您,您的变量可能在某些代码块中其值永远不会改变,并且建议您将声明从var更改为let 。 Swift背后的哲学是通过使用常量,代码更安全,因为如果您知道值不会更改,发现错误会变得更容易,并且不会得到值不断变化的意外行为。

资料类型

Swift的原始数据类型包括:

  • 整数
  • 布尔

其中。 但是,这些是您最有可能使用的。 如前所述,Swift可以通过在初始化期间分配一个值来推断您希望变量或常量为哪种类型。 但是,如果您要引用的类型可能有些混乱,或者您想在类中声明属性,但还不知道它们应该包含哪个值,则可以明确表示。 要明确显示数据类型,您可以在冒号后面加上类型名称,如下所示:

  var号:整数= 55 
let boxWidth:浮动= 10.00
let boxHeight:Double = 78.99

最后但并非最不重要的一点是,值不会隐式更改类型,例如在将IntString串联时。 您必须通过创建所需类型的实例(广播)来实现。

选装件

这也许是Swift的定义功能之一,这使其与其他编程语言有所不同(并且将给您带来不小的麻烦)。 您创建的任何原始数据类型以及任何其他类型(类,结构,枚举)都可以是可选的。 这是什么意思? 这意味着,通过使数据类型为可选,您可以授予它完全没有值或为nil的可能性。 就像在Java中一样,类的值可以为null 在Swift中,允许类和数据类型的值为nil ,但前提是您明确指定了该值。

  var number:Int = 42 //变量不允许为nil 
number = nil //错误:仅可选值可以为nil var anotherNumber:整数? = 11 //变量IS可以为零
anotherNumber = Int(“ Hello”)//字符串解析失败,并且
返回nil
anotherNumber = nil //也有效分配nil

在前面的示例中,我们有两个变量,一个是Int类型,另一个是Optional Int类型。 要声明变量应为可选类型,请在类型名称旁边添加问号。 不允许“正常”的Int缺少值,因此将nil分配给它将是一个错误。 但是,一个可选的Int可以有一个值或根本没有值,并且两者都是完全有效的。 那么,如何检查“ Hello”到Int的转换是否成功? 如果存在某种类型的错误,则Int()初始化程序将返回nil ,因此我们可以使用简单的if语句检查anotherNumber的值:

  如果anotherNumber!= nil { 
print(“字符串成功转换为整数”)
}其他{
print(“将字符串转换为整数时出现问题”)
}

但是,这种方法并未得到广泛使用,这就是为什么我喜欢将其称为Swift的另一个“禁忌”。 相反,Swift鼓励您使用一种称为“ 可选绑定”的方法,该方法包括将可选变量的值分配给if语句中的另一个变量或常量。 如果该值恰好是nil ,则if会简单地评估为false,否则它被认为是true,然后您可以使用刚刚获得的non-nil值执行所需的任何任务。

  如果让x = anotherNumber { 
print(“ X中包含的数字为\(x)”)
}其他{
print(“变量anotherNumber包含一个零值”)
}

假设这次您尝试解析包含值“ 20”的字符串,并且成功获取了分配给变量anotherNumber的新Int 。 如果您尝试同时添加numberanotherNumber会发生什么?

  var number:Int = 42 //字符串解析成功并返回20 
var anotherNumber:整数? = Int(“ 20”)

//错误:使用其值之前,必须先解开可选
令x =数字+ anotherNumber

即使42和20均为Int ,也请记住, anotherNumber被声明为Optional Int 。 因此,为了使用它的值,必须首先将其解包 。 是的,就像礼物一样。 当您要访问可选值时,该术语将与可选值一起使用。 这样做将导致运行时异常。 那么我们如何解开可选值呢? 您已经知道的第一种方法:使用可选绑定。 实际上,可选绑定的作用是解开变量中包含的可选值,以便您根据需要使用它。 第二种方法是显式拆开可选的包装 。 这是通过在要解包的值旁边放置一个感叹号来完成的。

  var number:Int = 42 //字符串解析成功并返回20 
var anotherNumber:整数? = Int(“ 20”) // 20被解包,现在可以与常规Ints一起使用
令x =数字+ anotherNumber!

第三种方法是将变量声明为隐式展开的变量。 通过放置一个 而不是 声明变量或常量时,类型名称旁边。 效果与以前相同。 该值一直是Optional,但是您以后不必解开它,因为它将始终被解开。 作为可选,它仍然可以包含nil值。

  var number:Int = 42 //现在将变量声明为隐式展开的Int 
var anotherNumber:整数! = Int(“ 20”) //无需使用! 现在在可选变量旁边
让x =数字+ anotherNumber //仍然完全有效
anotherNumber =无

但是,所有这些都有一个陷阱。 将可选内容视为可能包含炸弹的礼物很有用。 只要您不打开礼物,炸弹就不会爆炸。 但是,如果您仍要打开它并且它恰好具有零值(炸弹),它将自动爆炸。 对于我们的应用程序,它将立即崩溃。 隐式解开一个nil的值会使我们的应用程序立即崩溃,因此,应谨慎使用此方法,并且在绝对确定您将拥有与nil不同的值的情况下。

  var number:Int = 42 //现在将变量声明为隐式展开的Int 
var anotherNumber:整数! = nil //错误:发现隐式解包nil时。 程序崩溃让x =数字+ anotherNumber

因此,建议使用可选绑定来处理包含nil的可选对象。 安全使用可选选项的另一种方法是使用??。 操作员。 如果一个可选结果为nil ,则可以使用默认值,并且程序不会崩溃。

  让昵称:字符串?  =无 
let fullName:String =“ John Appleseed”
让非正式的问候=“嗨\(nickName ?? fullName)”

还有另一种方法来处理恰好为nil 可选变量 ,称为可选链接 ,当尝试访问类,枚举或结构的属性时使用。 当我们进入类部分时,将详细解释该方法。

弦乐

在Swift中,字符串是Unicode字符序列。 您可以使用+运算符将其他字符串连接到现有的字符串。 但是,如前所述,您不能将值连接到字符串,并不能期望它们自动转换为字符串。 在Java中,这有效:

  整数美元= 10; 
System.out.print(“您当前有$” +美元+“ USD”);

但是在Swift中却没有。 您可以使用String()初始化程序或使用称为字符串插值的方法来强制转换值 。 这包括在字符串中包括一对被反冲转义的括号。 在内部,您可以放置​​任何返回值的语句,因此不仅限于放置变量。

  令美元= 10 
print(“您当前有$ \(美元)USD”) let pesos = 200
print(“您当前有$ \(String(pesos))MXN”) 令磅= 8
print(“您当前有£” +字符串(磅)+“ GBP”)

包含了通常的转义特殊字符,新添加了“ \ u {}” ,它表示Unicode标量。 在花括号内,您可以指定一个代表有效Unicode字符的十六进制数字。 String类型还包括您可能希望从String类获得的所有实用程序功能,例如删除或添加字符,追加字符串,转换为大写或小写等。

由于Swift中的字符串是值类型,而不是引用类型,因此您可以使用==运算符比较两个字符串(与Java不同,在Java中,您需要使用.equals()方法)。

要访问字符串中的各个字符,可以使用for...in循环,也可以指定索引。 但是,由于字符的大小可能不同(以字节为单位),因此必须使用名为Index的特殊类来访问它们。 不能像C中那样通过整数访问单个字符,但是您使用带下括号[]的下标语法。

功能

您可以使用func关键字,后跟一个括号,其中列出了用于声明函数的参数。 与Java不同,Swift中的函数在圆括号的末尾指定返回类型,如下所示:

  func getGreeting()->字符串{ 
返回“你好!”
}

您使用箭头运算符->指示返回类型。 如果指定参数,则它们必须全部指定必须为哪种类型:

  func getGreeting(name:String)->字符串{ 
返回“嗨,那里,” +名字+“!”
}

要调用函数,只需使用其名称,并在参数内部指定要发送的值。 但是请注意,从Swift 3.0开始,必须始终使用参数的标签才能调用函数。 标签只是一个名称,函数外部的每个人都可以在调用时使用它来指定每个参数。 如果未指定标签,则将变量的名称用作标签:

  func getGreeting(妻子person1:字符串,丈夫person2:字符串)->字符串{ 
返回“嗨,”“ + person1 +”和“ + person2 +”!
} getGreeting(妻子:“ Brenda”,丈夫:“ Josh”)

如您所见,您可以使用标签名称和冒号来调用该函数。 在Swift的API文档中,通过使用函数名称及其所有标签来引用函数。 例如,前面的示例将被引用为getGreeting(wife:丈夫:) 。 如果省略标签,则使用参数名称。 但是,可以在调用函数时通过在每个参数前加下划线_来明确指定省略参数的标签。 例如:

  func getGreeting(person1:字符串,person2:字符串)->字符串{ 
返回“嗨,”“ + person1 +”和“ + person2 +”!
} getGreeting(person1:“ Brenda”,person2:“ Josh”) func addNumbers(_ number1:Int,_ number2:Int)-> Int {
返回数字1 +数字2
} addNumbers(5,10)

当在文档中引用其参数省略其标签的函数时,也会发现下划线。 因此,例如,最后一个函数将被引用为addNumbers(_:_ 🙂

Swift功能的其他功能包括:

  • 它们可以具有默认值,方法是在每个参数的数据类型之后使用=符号
  • 就像在C语言中一样,可以通过引用传递变量,方法是在函数调用中的每个变量名称前使用&并在函数签名中的每个参数的每种数据类型之前指定关键字inout 。 这些参数称为输入输出参数。
  • 支持嵌套函数
  • 函数可以用作类型,甚至可以存储在变量中,就像在Javascript中一样。
  func swapTwoInts(_ a:inout Int,_ b:inout Int){ 
让临时A = a
a = b
b =临时A
} var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt,&anotherInt)
print(“ someInt现在为\(someInt),anotherInt现在为\(anotherInt)”)
//打印“ someInt现在为107,anotherInt现在为3” func getGreeting(name:String =“ Human”){
打印(“ Hello \ {name)!”)
} getGreeting()//打印“ Hello Human!”
getGreeting(name:“ Eduardo”)//打印“ Hello Eduardo!”

关闭

闭包是可以在您的应用程序中存储和传递的代码块。 函数是闭包的特例,有一个名字。 如果您使用过Javascript,则可能使用了闭包,因为您可以将函数作为参数传递给其他函数,以供以后执行。 在Java 8中引入了lambda表达式,它们基本上也是闭包。 闭包的优势在于,即使父函数执行完成,也可以访问其父范围的变量以在其他地方使用。 Swift已为闭包授予了一些优化,这些优化在特定情况下极大地减少了代码量。 闭包的基本语法如下:

  {(参数)-> returnType输入 
陈述
}

假设您创建了一个对两个给定数字进行计算的函数。 因此,前两个参数是计算中要使用的两个数字。 但是,您可以自由决定用它们计算什么。 然后,第三个参数将是执行给定计算的闭包。

  func compute(a:Int,b:Int,operation:(Int,Int)-> Int){ 
令结果=运算(a,b)
print(“结果为\(结果)”)
}

让我们分析一下上面的功能。 它称为calculate ,它具有三个参数:两个接收Int参数,称为ab ,以及第三个类型为(Int, Int) -> Int 。 第三个参数是什么意思? 如前所述,函数也可以是数据类型。 因此,第三个参数期望一个函数或闭包,该函数或闭包可以接收两个Int并返回一个Int 。 它的标签是operation ,所以无论我们将什么函数或闭包作为第三个参数传递,我们都将始终通过此标签对其进行调用。 在calculate内部,我们调用operation并将其传递给ab ,它们的结果将存储在一个名为result的常量中。 最后, result被打印出来。

有多种方法可以将函数作为第三个参数传递给calculate 。 第一种选择是编写要传递的函数,并使用其名称传递其引用以calculate调用。

  func add(number1:Int,number2:Int)-> Int { 
返回数字1 +数字2
} //打印“结果为129”
计算(a:77,b:52,操作:加)

函数add符合预期的条件:它接收两个整数并返回一个整数。 因此,将打印此返回的整数。 只要它满足参数类型所要求的条件,在函数标记的操作内部发生的事情就无关紧要。 将函数传递给计算的第三个参数的第二种方法是直接在函数的调用中编写函数。 但是,这只能通过使用闭包来实现:

  //打印“结果为129” 
计算(a:77,b:52,操作:
{(number1:Int,number2:Int)-> Int in
返回数字1 +数字2
})

记住闭包的结构。 您以大括号开始您的块,然后在括号中加上它将接收的参数,然后是返回类型, in关键字,最后是闭包的语句或实际操作。 结果是一样的。 但是我们可以使这种关闭更短。 由于Swift从声明中得知您将期望两个Int作为闭包的参数并返回一个Int ,因此可以省略类型名称,如下所示:

  //打印“结果为129” 
计算(a:77,b:52,操作:
{number1,number2 in
返回数字1 +数字2
})

注意,我们如何去除闭包参数周围的括号以及闭包签名的数据类型。 由于其强大的类型推断功能,这是Swift对闭包的优化之一。 但这可以变得更短! 此优化仅适用于仅具有单个语句(即return语句)的闭包。 在这种情况下,您可以完全删除return关键字,并且将返回执行操作的结果。 因此,我们可以将代码缩水一些,如下所示:

  //打印“结果为129” 
计算(a:77,b:52,操作:{number1,number1中的number2 + number2})

优秀。 该代码现在更具可读性。 但是,我们能做的就是最大程度地减少关闭时间吗? 好吧,我们可以进一步减少它。 Swift会自动为内联闭包提供速记参数名称,因此我们可以将参数称为$ 0,$ 1,$ 2等。 其中的数字代表订单。 这也使我们摆脱了in关键字,只保留了闭包的实际语句。

  //打印“结果为129” 
计算(a:77,b:52,操作:{$ 0 + $ 1})

这是Swift关于闭包的另一种优化。 这是我们可以获得的最小闭合量。 还是可以? 好吧,在某些情况下,实际上还有另一种方法可以减少我们的关闭。 由于我们的add函数所做的与+符号的实际函数实现相匹配,因此我们可以将此运算符用作闭包本身,而Swift会推断出它需要做什么。 因此,我们的关闭最终减少为:

  //打印“结果为129” 
计算(a:77,b:52,操作:+)

谈论方便! 而且如果函数中的最后一个参数是闭包,我们可以忽略该参数的标签,并在函数的右括号旁边写花括号,如下所示:

  //打印“结果为129” 
计算(a:77,b:52){+}

闭包广泛用作函数中的参数,并且通常在异步任务完成执行后用作事件处理程序,因为您无法确定这些任务何时完成处理。

元组

元组是简单的数据集合。 使用元组,您可以混合使用任何数据类型的任何值,以供以后使用。 声明元组后,如果决定使用标签,则可以通过其索引或标签名称访问其元素。 元组用括号括起来,并用逗号分隔。

  let tuple =(“ Eva”,7) 
let name = tuple.0 //包含“ Eva”
let level = tuple.1 //包含7个 let anotherTuple =(name:“ Leo”,level:10)
let anotherName = anotherTuple.name //包含“狮子座”
let anotherLevel = anotherTuple.level //包含10

使用元组,您可以在函数完成执行后同时返回多个值。 要声明为返回类型的数据类型必须与元组的类型匹配,并且可以选择包括标签。

  func getEmployee()->(String,Int){ 
让名称=“伊娃”
让等级= 7
返回(名称,级别)
让employee = getEmployee()
print(“这是\(employee.0),她的级别是\(employee.1)”) func getAnotherEmployee()->(name:String,level:Int){
让名称=“狮子座”
让等级= 10
返回(名称,级别)
让anotherEmployee = getAnotherEmployee()
print(“这是\(anotherEmployee.name),其级别是\(anotherEmployee.level)”)

控制流程结构

您一直使用和喜欢的控制流结构在这里。 它们基本相同,但有一些小变化:

  • 他们不需要在它们所求值的表达式之间加上括号,但是为了清楚起见,可以将它们包括在内
  • 不再支持“经典​​” for循环(因为++和—自Swift 3.0起已弃用)。 因此, for...in循环现在是唯一可以使用的有效循环,必须使用范围运算符.. <才能确定for必须迭代的范围。 如果您不打算像Javascript那样使用范围,也可以接受要迭代的collection。
  • do...while循环存在,并且其行为与Java中相同,但是,从Swift 2.0版本开始, do关键字已由repeat关键字替换,因此现在称为repeat...while 环。
  • while循环与Java中的相同
  • 关键字continuebreak具有与Java相同的目的
  //由于包含...,所以打印“ 1 2 3 4 5” 
因为我在1 ... 5 {
打印(“ \(i)”)
} //打印“ 1 2 3 4”,因为.. <是排他的
对于1中的j。<5 {
打印(“ \(j)”)
}

关于条件结构, if...else语句的工作方式与Java中的相同。 但是, switch语句具有增强的功能:

  • case语句支持范围
  • 元组可用作评估的表达式,类似于可选绑定,您可以将case语句中的元组值分配给在特定案例内使用的变量
  • 无需在每种情况后添加break语句,因为在每种情况后它都会自动“中断”
  • 没有隐式的失败。 若要使此行为明确,必须使用fallthrough关键字。
  • 案例陈述必须详尽无遗。 如果案例无法涵盖域中该数据类型的所有可能值,则必须添加默认案例。 因此,大多数时候您需要包括一个。

通用数据结构

没有一种编程语言就没有它的数据结构。 在Swift中,您将使用的两个主要数据结构是数组和字典。 您可能会知道Javascript中的字典作为对象或Groovy中的地图作为例子。 您可以通过为数组分配一个初始值来创建数组,Swift将从该数组中推断出其数据类型,也可以创建一个Empy数组,但是您必须知道它的类型。 数组的语法为:

  //这会隐式创建一个字符串数组 
let names = [“ Michael”,“ Pam”,“ Jim”,“ Kevin”] //您可以明确说明数组的类型
让数字:[Int] = [1,2,3,4] //数组可以为空,但必须先指定其类型
让emptyArray = [Int]()

与Java相反,Swift数组默认包含仅通过使用List接口的特定实现才能实现的功能。 因此,例如,从元素中删除元素后,Swift reindex中的数组可以在末尾附加新值或将它们插入指定的索引中。 您还可以使用+运算符“添加”数组,甚至可以使用范围来引用数组中元素的子集以及其他功能。

如果是字典,则必须指定键的类型和值的类型。 两者可以不同。 字典中的键和值用冒号分隔,而对则用逗号分隔。 与数组类似,可以通过提供初始值或通过在声明过程中显式说明其类型来创建字典。

  //创建一个带有字符串键和Int值的字典 
let dictionary = [“ Eva”:10,“ Leo”:7,“ Kenny”:12] //显式声明键和值的类型
var airport:[String:String] = [“ YYZ”:“多伦多皮尔逊”,“ DUB”:“都柏林”] //类型为[Int:String]的空字典
让emptyDictionary = [Int:String]()

创建数组或字典后,可以将空文字[][:]分别赋给数组和字典,以指示它们根本没有任何元素。 与Javascript中的对象非常相似,您可以使用方括号[],并在下标关键字的名称,以便使用下标语法访问该对。 您也可以通过相同的方法分配新值,或通过将nil分配为值来删除现有对。 最后,还可以通过使用当前在字典中找不到的键并为其指定值来添加元素。

面向对象

Swift还支持面向对象,熟悉Java的人应该能够毫无问题地使用它。 但是正如您所看到的,Swift引入了一些细微的变化,使其与Java有所不同。 这种显着的差异之一是您不需要为不同的类定义使用单独的源文件。 在其最基本的形式中,一个类由关键字class ,后跟该类的名称以及所有方法和属性所在的花括号组成。

要创建一个类的实例,您只需编写该类的名称,然后加上括号,例如:

  class LunchBox { } 让午餐= LunchBox() 

与Java一样,可以使用点语法访问类内部声明的任何属性。

  午餐盒类{ 
var价格= 129.99
} 让午餐= LunchBox()
打印(“您用餐的价格为$ \(午餐价格)”)

声明为常量的对象仍然可以修改其属性,因为类是引用类型。 在Swift中,有两种属性: 存储属性计算属性 。 简单地说,存储的属性是类内的变量或常量,例如上一个示例中的price。 可以在声明期间为它们提供初始值,也可以在创建类的实例时为其分配初始值。 但是,在创建对象时,所有属性都必须具有值,否则编译器会抱怨,您将无法编译代码。

顾名思义,计算属性是变量,它们根据其他属性的值来计算其值。 对于外部世界,它们的外观和行为类似于常规属性,但是它们是变相的函数。 您可以像声明任何变量一样声明一个计算属性,但是,不是使用=运算符,而是使用{}输入将计算要分配或返回的值的代码。 要在检索属性时计算值,请使用get块,每当需要为其分配值时,请使用set块。 必须为计算属性明确指定类型,并且由于其值始终未知,因此必须将其声明为vars。

  导入基础 类Rectangle { 
var width:Double = 0.0
var height:Double = 0.0
var area:Double {
//计算的getter
得到{
返回宽度*高度
}
//计算的二传手
设置{
//假设尺寸相等(即正方形)
宽度= sqrt(newValue)
高度= sqrt(newValue)
}
}
} let rect = Rectangle()
矩形宽度= 3.0
矩形高度= 4.5
print(“矩形的面积为\(rect.area)”)// = 13.5
rect.area = 9 //现在的宽度和高度都为3.0

如果您打算这样做,则计算属性可以是只读的。 这是通过仅在计算属性内包含get块来实现的。 默认情况下,set块获取接收到的新值并将其存储在名为newValue的常量中,尽管您可以根据需要通过在set关键字旁边和您要常量的名称内包含一对括号来重命名它。替换为newValue

存储的属性可以具有所谓的观察者,即在将新值分配给该属性之后以及紧随其设置之后立即调用的代码块。 要将观察者添加到存储的属性中,请在声明或初始化之后添加{} ,并使用willSetdidSet关键字添加观察者。

  class PropertyObserverExample { 
var number:Int = 0 {
willSet {
print(“即将更改为\(newValue)”)
}
didSet {
print(“只需从\(oldValue)更改为\
(self.number)!”)
}
}
} var观察者= PropertyObserverExample()
rator.number = 4
//打印“即将更改为4”,然后显示“从0更改为4!”。

与用于计算属性的set块相似,观察者会分别为willSetdidSet块获得一个名为newValueoldValue的默认变量,但是如果您想使用相同的方法,则可以更改其名称。

此外,可以将属性或函数声明为静态的(使用static关键字),这样就可以像在Java中一样,即使没有包含它们的类的实例也可以调用它们。 在Swift中,这些类型的属性和方法分别称为类型属性类型方法

初始化器

不出所料,Java中称为构造函数的概念确实存在于Swift中,但名称不同。 在Swift中,构造函数称为初始化器,与Java构造函数必须具有其类名的Java相反,初始化器称为init()方法,该方法当然不会返回任何值。 创建类实例的任何时候都调用初始化程序。 因此,如果您没有为类中的属性分配默认值,那很好,只要您在类的初始化器中为这些属性设置一个值,否则编译器会抱怨。 如果一个类的所有属性都具有默认值,则无需创建初始化程序,因为Swift将提供一个初始化程序。 但是,调用初始化程序的方法与调用方法的方法相同,因此,如果未将下划线赋给它们,则必须使用参数的所有标签。

  华氏度类{ 
var温度:双倍
初始化(度:双){
自温度=度
}
在里面(){
温度= 32.0
}
} var f =华氏()
print(“默认温度为\(f.temperature)°华氏温度”)
//打印“默认温度为32.0°华氏 f =华氏(度:100)
print(“默认温度为\(f.temperature)°华氏温度”)
//打印“默认温度为华氏100.0度

遗产

继承就像Java中一样。 一个类一次只能扩展一个单一的类,但是,当一个类这样做时,它会继承其所有属性和方法。 您可以通过在类的声明中在类名旁边放置一个冒号来继承类(与使用extends关键字相反)。 与Java的主要区别在于,在您创建的Swift类中,您不会从一个通用超类继承。 在Java中,您创建的所有类都从Object扩展,但是在Swift中却不会发生,因此您创建的任何类都将成为基类。 此外,如果需要,可以将观察者分配给继承的属性。 可以通过使用方法签名旁边的override关键字来覆盖方法,以从父类中进行覆盖,并且可以使用super关键字来访问超类中的方法,就像在Java中一样。

  车辆类别{ 
var currentSpeed = 0.0
var description: String {
return "Traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
print("Room Room")
}
} class Bicycle: Vehicle {
var hasBasket = false
} class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
} let b = Bicycle()
// Prints "Traveling at 0.0 miles per hour"
print(b.description) let t = Train()
// Prints "Choo Choo"
t.makeNoise()
let v = Vehicle()
//Prints "Room Room"
v.makeNoise()

通讯协定

A protocol can be thought of as a list of requirements for a class. The equivalent for protocols in Java wold be interfaces. Protocols provide definitions of methods and properties that a class must implement in order to use it. If a class uses a protocol and implements all its properties and methods it is said to conform to the protocol. So, as expected, protocols do not have any actual code and it’s up to the classes to add functionality to those methods. Similarly with Java, a class can conform to several protocols at the same time. You use the protocol keyword to declare a protocol, and the rest is much like a class declaration.

 protocol Student{ 
var studentID: Int { get set }
var grade: Double { get set }
func isApproved() -> Bool
} class Person : Student {
var name = "Donald"
var studentID : Int
var grade: Double

init(id:Int, grade: Double){
studentID = id
self.grade = grade
}

func isApproved() -> Bool {
return grade > 7.0
}
} let p = Person(id: 17891, grade: 6.9)
print("\(p.name) has approved? \(p.isApproved())")

In a protocol you can decide if the properties to implement must be gettable and settable or only settable by specifying this with the get and set keywords. Function to implement must indicate its return type as well. If you conform to a protocol, the compiler won’t complaint. And similarly to Java, confirming to a protocol generates an is-relationship, so for our variable p we could consider it a Person type but also a Student type ( polymorphism ).

错误处理

You might know them as exceptions in Java. In Swift, errors can be handled to prevent your program from crashing. Do you remember how the do...while loop is now called repeat...while ? Well, the do keyword still exists in the language, however it’s now used to handle errors. You can achieve this by using a do try catch defer pattern, whose equivalent in Java would be try catch finally . If you want to create your own errors, you must implement the Error protocol. Unlike Java, that’s the only protocol you must implement in order to convert your class, enum or struct into an error that the catch blocks can handle.

To specify if a method can throw an error, you use the throws keyword, and just like in Java, whenever you want to throw an error you use the throw keyword. In the catch block, similarly to Java, you can assign the error to a variable for use inside the catch block, and if you don’t specify a name, the default variable would be called error .

Additionally, inside the do block, any statement that can potentially throw an error must be prefixed with the try keyword.

 import Foundation enum ArithmeticError: Error{ 
case DivisionByZero
case SquareRootOfNegativeNumber
}
class Operation{
func divide(a: Double, b: Double) throws -> Double {
guard b > 0 else {
throw ArithmeticError.DivisionByZero
}

return a/b
}
func squareRoot(a: Double) throws -> Double {
guard a >= 0 else {
throw ArithmeticError.SquareRootOfNegativeNumber
}

return sqrt(a)
}
}
let operation = Operation() do {
let divisionResult = try operation.divide(a: 3, b: 0)
let squareRootResult = try operation.squareRoot(a: -1)
} catch ArithmeticError.DivisionByZero {
print("You can't divide by zero!")
} catch ArithmeticError.SquareRootOfNegativeNumber{
print("Only positive numbers!")
}
defer {
print("With or without errors, this string is printed!")
}

The guard keyword can be used to make sure that a condition is met, otherwise the else clause is executed, where you can fix any problem. Optional binding is also possible in guard statements and the advantage is that the declared constant or variable in the guard statement can be used throughout the rest of the outer scope. Finally, the defer block can be used to execute code before the program or function exits, which is equivalent to the finally block.

Last Words

Swift has many more features than the ones briefly explained in here, such as support for Generics, Enums with extended capabilities and Structs, which are basically the same as classes but they are passed by type. Another interesting feature of Swift are extensions, which allows you to add code to any existing class with computed properties and your own methods, allowing you to expand the current functionality of any given class at your reach.

If you’re still interested, Appel’s Documentation is a great place to start!