Swift是为IOS和OS应用开发所创建的新语言。虽然定义为新语言,但Swift与这两个语言有很多相似的地方。
Swift语言将C和OC语言中的基本原理封装成了自己的版本,包括将整形封装成Int,Double和Float代表了浮点数。Bool代表了布尔值;String代表了字符串。而且Swift还提供了2种集合类型的增强版本:集合类型中的Array和Dictionary。
与C类似,Swift使用变量存储值,用变量名引用值。Swift也大量使用不变的值,称为常量,但比C中的常量有更多的功能。当你处理不需要改变的值时,Swift中的常量使代码更安全和简洁。
除了熟悉的类型,Swift还扩展了OC中没有的类型。例如元组,元组可以创建和传递不同类型的值。元组也可以让你从函数中返回用不同的值组合而成的复合值。
Swift也引用了可用来处理缺省值的optional(可选)类型。Optionals代表这个值为未知的,或者压根就没有值。Optionals类似与OC中的nil指针,但是Optionals不仅仅处理类,它可以处理处理任何类型。Optionals比OC中的nil指针更安全且更有表达力,而且也是Swift最重要特性的核心功能。
Optionals证明了Swift语言是个安全的语言。Swift帮助你更清晰的去处理你代码中的类型值。如果你的代码中想要String类型.类型安全会阻止你错误的传递一个Int类型的值。这使你在开发过程中更容易捕获和修复错误。
常量和变量就是将一个特殊类型的值(比如整形 10
或者字符串 "Hello"
)与一个名称(比如 maximumNumberOfLoginAttempts
或welcomeMessage
)相关联。常量值一旦赋值不可被改变,而变量可以在以后被改变。
变量和常量在使用之前要被声明。用关键字let定义常量,用关键字var定义变量。下面的例子说明常量和变量如何去计算用户登录的次数:
let maximumNumberOfLoginAttempts = 10 var currentLoginAttempt = 0
定义了一个常量maximumNumberOfLoginAttempts,值为10。定义一个变量currentLoginAttempt,值为0.
在这个例子中,最大的登录数为常量maximumNumberOfLoginAttempts的值10.因为常量的值是不会改变的。当前登录的序号用变量currentLoginAttempt定义,因为这个值会在每次登录失败后会有所改变。
你可以在一行代码中定义多个常量和多个变量。
var x = 0.0, y = 0.0, z = 0.0
注意 如果你想去定义一个不准备改变的值,一般用let关键值定义一个常量。用变量来定义的话一般都会被改变。
当你定义一个常量或者变量时,为了能清楚的表达该变量或者常量可以存储哪种类型的 值,可以通过类型注解的方式达到该目的。类型注解的写法在常量或者变量的名称后面加上一个冒号(:),然后跟上一个空格,空格后面写上该名称所要用作的类型。
下面用一个变量的类型注解的例子来讲解一下,变量名为welcomeMessage,类型为String.
var welcomeMessage: String
声明中的冒号(:)代表的意思为“..of type ..,”。所以上面的代码可以理解为
定义了一个叫welcomeMessage的,类型为String的变量
welcomeMessage = "Hello"
注意 事实上,在你编写代码的过程中,很少会用到类型注解。如果在变量或者常量初始化时,你就赋上值了。那么swift可以利用在Type Safety and Type Inference中所描述的,推算出常量或者变量的类型。例如上面的welcomeMessage的例子,生命变量时没有赋值。 所以才会用注解的方式特别之处该变量的类型
你几乎可以使用任何你喜欢的字符去命名变量和常量。包括unicode字符
常量名和变量名不可以包含:数序符号,箭头,私用或无效的unicode代码符号,行间距,绘图符号。也不可以以数字开头,但是数字可以放在其他任何地方。
一旦你定义了一个常量或者变量,且有确切的类型。你不可以用该name再次定义其他变量或者常量,也不可以改变其类型。变量和常量相互之间不可以转换。
Note 如果你需要使用预留关键字去定义个常量或者变量的话,你可以使用反单引号(`)来包裹关键字。当然最好不要这样使用。
你可以将已有的变量值改变成其他兼容类型的值。例如下面的变量friendlyWelcome的值从Hello!变成了Bonjour!:
var friendlyWelcome = "Hello!" friendlyWelcome = "Bonjour!" // friendlyWelcome现在的值为Bonjour!
与变量不同的是,常量的值一旦设定就不能再改变。视图改变常量值的代码会在编译时出错。
let languageName = "Swift" languageName = "Swift++" // 编译错误,languageName的值不能改变
你可以使用println函数打印常量或变量当前的值:
println(friendlyWelcome) // prints "Bonjour!"
println是一个全局函数,为了看起来舒服,打印完值以后会跟一个换行符。如果你用过Xcode,举个例子,在Xcode的输出面板里打印它的输出(第二个函数:print。具有相同的作用,但是它不输出换行符。)
println函数可以打印你传入的任何字符串。
println("This is a string") // prints "This is a string"
println("The current value of friendlyWelcome is \(friendlyWelcome)") // prints "The current value of friendlyWelcome is Bonjour!"
注意 你可以用字符串擦入的选项都定义在<a target=_blank href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_381">String Interpolation</a>
使用注释可以在代码中添加一些不会被执行的文本,这些文本起到提醒和警示的作用,在编译的时候swift的编译器会忽略注释中的内容。Swift中的注释与C中的注释类似,单行注释用双斜杠(//):
// this is a comment
/* this is also a comment, but written over multiple lines */
/* this is the start of the first multiline comment /* this is the second, nested multiline comment */ this is the end of the first multiline comment */
和其他语言不同的是,Swift语言不需要你在每行代码的后面写分号来标识行结束。当然啦,你如果愿意你也可以去添加。但是如果你要在一行上写多行语句,那么分号就必要要写。
(昨晚写的都没保存,Calais!)
整型就是无小数部分的,例如42和-23.整型包括有符号(整数,0和负数)、无符号(整数,负数)。Swift对8位/16位/32位和64位这四种比特形式都提供了有符号和无符号的整型。他们有点类似于C的命名规范。8位的无符号整型称为UInt8,32为的有符号整型成为Int32。和Swift中其他类型一样,这些整型类型都有大写名称。
你可以通过获取整型类型的属性来得到其最大值和最小值,min代表最小值,max代表最大值。
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8 let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
在一般情况下,你编写代码时,你不需要去选择特定大小的整型,因为Swift已经提供了一个附加的整型类型:Int。它会适配你的系统版本号,根据你的系统而自动选择哪一种整型类型。
On a 32-bit platform, Int is the same size as Int32. On a 64-bit platform, Int is the same size as Int64.
Swift也提供了无符号整型:UInt.大小根据当前系统版本而定。
On a 32-bit platform, UInt is the same size as UInt32. On a 64-bit platform, UInt is the same size as UInt64.
注意 使用UInt的场景是:当你有特殊需要一个无符号的与平台整型字节大小相同的整型的时候。如果不是这种情况,Int就够了 甚至我们知道存储的是非负的值的情况。统一使用Int整型值可以使得代码更通用,避免了2种不同数字类型之间的转换, 而且也符合Type Safety and Type Inference的机制。
浮点数是有小数部分的数字,例如3.14159, 0.1, 和 -273.15. 浮点型比整型表示的范围要大,可以保存更大或者更小的数字。
Swift提供两种有符号的的浮点数字类型:
注意 双精度至少15位小数,而浮动的精度可以是6位小数。使用适当的浮点类型取决于代码中使用的值的性质和范围。
Swift是一个类型安全的语言,一个类型安全的语言鼓励你要明白你所写code的值的类型。如果你的代码期望是一个String类型的,你就不能传递一个错误的Int类型。
因为Swift是类型安全的,它会在编译代码时进行类型检查和标出任何不匹配的类型错误。这可以让你在开发过程中尽早捕捉和修复错误。
类型检查帮助你在处理不同类型的值时避免错误。但是这不意味着你在声明变量或者常量时必须去指定其类型。
如果你不特殊执行你所需要的值的类型,Swift会通过类型判断来推算出最合适的类型。类型判断可以通过简单的代码审查,使编译器在编译代码时自动推断出特定表达式的类型。
因为类型判断,Swift需要的类型定义比C和OC少很多,常量和变量仍然是显式类型,但是你的大多数工作Swift都会为你提供明确的类型。
当你定义一个常量或者变量时,初始化其值,类型判断是非常有用的。它会通常在声明该变量或常量时分配一个文本值。(文本值是一个直接出现在你的源代码中的值,例如下面例子中的43和3.14159)
举例说明:如果你为一个新的不知道其类型的常量,分配一个42的文本值,Swift会推断出你想要的类型为Int,因为你初始化该值用的是一个数字,该数字看起来像一个整型。
let meaningOfLife = 42 // meaningOfLife is inferred to be of type Int
同样的,如果你不为浮点数的文本值指定一个类型,Swift也会推断出你想要创建的是一个Double类型的值。
let pi = 3.14159 // pi is inferred to be of type Double
let anotherPi = 3 + 0.14159 // anotherPi is also inferred to be of type Double
文本值3,就其本身而言是没有显式类型的。所以Double类型合适的输出类型是从浮点数临时值的一部分推断出来的。
整型文本可以写成如下格式:
所有的整型文本表示10进制数17:
let decimalInteger = 17 let binaryInteger = 0b10001 // 17 in binary notation let octalInteger = 0o21 // 17 in octal notation let hexadecimalInteger = 0x11 // 17 in hexadecimal notation
对于10进制的指数exp而言,其表示基数乘级10exp
1.25e2
代表 1.25 × 102, or125.0
.
1.25e-2
代表 1.25 × 10-2, or0.0125
.
对于16进制的指数exp而言,其表示基数乘级为2exp
0xFp2
代表 15 × 22, or 60.0
.
0xFp-2
代表 15 × 2-2, or3.75
.
所有的浮点数来表示12.187
:
let decimalDouble =12.1875
let exponentDouble =1.21875e1
let hexadecimalDouble =0xC.3p0
为了使数字文本更容易阅读,它还可以有其他格式。整形和浮点数都可以添加额外的0和下划线来使代码更易阅读。这种格式的数字文本不会影响其本身的值。
let paddedDouble = 000123.456 let oneMillion = 1_000_000 let justOverOneMillion = 1_000_000.000_000_1
使用Int类型来表示所有的常规代码中的整数常量和变量,即使他们是非负的。在任何情况下使用默认整形类型意味着常量和变量。都可以在代码中立即进行交互操作,而且会推断出整形文本值的类型。
只有在特殊情况下才会使用其他整形类型,例如来自外部资源的确切大小的数据,比如性能,内存使用或者其他必要的优化选项。在这种情况下使用确切大小的类型有助于捕获任何溢出值和隐藏的用于原始数据的文档。
保存在一个整形常量或者变量数字的范围因数字类型的不同而有所区别,一个Int8的常量或者变量可存储的数字范围为-128到127;而UInt8可存储的数字范围为0-255.超出其整形类型范围的数字会在代码编译时报错。
let cannotBeNegative: UInt8 = -1 // UInt8不能保存负数, 报错 let tooBig: Int8 = Int8.max + 1 // Int8的值超出了最大值会报错,
let twoThousand: UInt16 = 2_000 let one: UInt8 = 1 let twoThousandAndOne = twoThousand + UInt16(one)
SomeType(ofInitialValue)
可以默认调用Swift类型初始器,然后传递一个默认的值。在后台,UInt16有一个接受UInt8的初始器,因此这个初始器会叫UInt8的值转化为一个UInt16的值。这里你不可以传递任何类型,然而它提供给初始器的类型必须是UInt16。扩展已有类型是为了提供具有新类型(包括自己定义的类型)的初始器。
整型和浮点型之间的转换必须是显式的。
let three = 3 let pointOneFourOneFiveNine = 0.14159 let pi = Double(three) + pointOneFourOneFiveNine // pi 推断出类型为Double
这里的three会创建一个新的Double类型的值,以至于加号两边都是相同的类型。如果不进行转换,这样的加运算是不被允许的。
相反,浮点型也可以转换成整型,这种情况下,Double或者Float类型会在整型初始器中被转换为整型类型。
let integerPi = Int(pi) // integerPi equals 3, and is inferred to be of type Int
用这种方式将浮点数转换为整型,会将浮点数的精度降低,4.75会变成4,-3.9会变成-3
注意 这种组合数字常量和变量的规则是不同于数字文本的。数字文本3是可以直接与0.14159相加,因为数字文本自身是没有明确的类型。当编译器编译的时候会推断出其类型。
类型别名可以将现有的类型定义成其他的名称,使用typealias关键字。当你认为源码中的类型名很不容易理解,这个时候,你可以通过类型别名的方式自己定义其名称,让其看起来很容易理解。如果你正在处理一个特定大小的外部数据:
typealias AudioSample = UInt16
一旦你定义了别名,你就可以在任何原来使用这些名称的地方使用你新定义的名称。
var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound = 0
Swift定义了一个基础布尔类型:Bool。布尔值称为逻辑值,因为它只有true和false两个值。Swift提供了2个布尔常量:true和false.
let orangesAreOrange = true let turnipsAreDelicious = false
常量orangesAreOrange和turnipsAreDelicious被推断为布尔类型的值,尽管它后面跟的是布尔文本值true和false.之前将的Int和Double,在创建它们的时候,如果你给它们赋值true或者false,你不需要定义bool类型的常量或者变量。类型推断会使Swift的代码在用其他类型的值来初始化常量或者变量的时候,显得更加简洁和易读。
当你使用条件语句时,布尔值是很有用的,例如if语句:
if turnipsAreDelicious { println("Mmm, tasty turnips!") } else { println("Eww, turnips are horrible.") } // prints "Eww, turnips are horrible."
Swift的类型安全机制会阻止不是bool默认值的其他值传入到布尔类型的常量或者变量中。下面的例子会在编译时报错:
let i = 1 if i { // this example will not compile, and will report an error }
<div class="code-sample"><ul class="code-lines"><li><code class="code-voice"><span class="kt">let</span> <span class="vc">i</span> = <span class="m">1</span></code></li><li><code class="code-voice"><span class="kt">if</span> <span class="vc">i</span> == <span class="m">1</span> {</code></li><li><code class="code-voice"> <span class="c">// this example will compile successfully</span></code></li><li><code class="code-voice">}</code> </li></ul></div>
表达式i==1的结果是一个Bool类型的值,所以第二个例子会通过类型检查。类似于i==1的详细说明会在Basic Operators中详细说明。
与其他Swift类型安全的例子一样,这种方法避免了意外的错误并确保特定部分的代码,他的意图总是明确的。
元组可以将多个值组织后放入一个混合值中,元组中的值可以是不同类型的,甚至任何两个值都是不同的类型。下面的例子中,(404, "Not Found"
)是一个描述Http状态码的元组。Http状态码是当你请求一个网页的时候,网站服务器返回的一个特殊值。如果你请求的网站不存在,服务器就会返回404 Not Found
状态码。
let http404Error = (404, "Not Found") // http404Error is of type (Int, String), and equals (404, "Not Found")上面的元组将返回的状态码中的Int类型的数字和一个String类型的描述文本组合在一起,可以描述成(Int,String)类型的元组。
你可以创建任意顺序的类型元组,可以包含你喜欢的任何类型。对于你创建元组这件事,我只想说:根本停不下来。比如(Int,Int,Int)、(String,Int)或者你需要的任一组合。
你可以将一个元组的内容分解成常量或者变量,然后你就可以像处理普通值一样处理它们。
let (statusCode, statusMessage) = http404Error println("The status code is \(statusCode)") // prints "The status code is 404" println("The status message is \(statusMessage)") // prints "The status message is Not Found"
let (justTheStatusCode, _) = http404Error println("The status code is \(justTheStatusCode)") // prints "The status code is 404",忽略了NotFound的值。
println("The status code is \(http404Error.0)") // prints "The status code is 404" println("The status message is \(http404Error.1)") // prints "The status message is Not Found"
在定义元组的时候,你可以给元组中的每个元组定义一个名称。
let http200Status = (statusCode: 200, description: "OK")
println("The status code is \(http200Status.statusCode)") // prints "The status code is 200" println("The status message is \(http200Status.description)") // prints "The status message is OK"
注意 元组对于临时组织一些相关联的值是很有用的,但是他不适合创建复杂的数据结构,如果你的数据结构可能超出了临时的范围,选择使用Class或者结构体模型比使用元组更有用。 想要了解更多信息,请看<a target=_blank href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_94"><span class="x-name">Classes and Structures</span></a> 一文
在缺省值的情况下你可能需要使用可选项功能,一个可选项可以理解为:
注意 在C和OC中不存在可选项的定义,OC中最接近这种概念的就是方法返回Object,如果不返回Object就返回nil值。nil值代表着有效对象的缺省值,但是它只对对象有效,对结构体, C基本类型,枚举类型值是无效的。对于这些类型,OC中的方法会返回一个特殊的值(例如NSNotFound)来表示这是缺省值。这种方式假设方法的调用者知道有特殊值要去检查和测试。 Swift的缺省值让你可以为任何类型的值定义缺省值,却不需要特殊的变量。
举例说明,Swift的String类型有一个方法叫toInt,这个方法的作用是试图将String类型的值转换为Int类型的值。但是,不是所有的String类型的值都可以被转换成Int类型。“123”可以被转换成整型,但是“hello,world”是不可以转换成整型的。下面是调用toInt将String转换成Int的例子:
let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() // convertedNumber is inferred to be of type "Int?", or "optional Int"
你可以使用if语句来判断一个缺省值是否包含值,如果缺省值包含值,会赋值true,否则赋值false.
一旦你确定缺省值包含值,你就可以通过在缺省值后面添加一个感叹号(!)处理它所代表的值,感叹号代表的意思是说:我知道这个缺省值肯定含有值,你可以使用它。这就是所谓的缺省值的强制展开。
if convertedNumber { println("\(possibleNumber) has an integer value of \(convertedNumber!)") } else { println("\(possibleNumber) could not be converted to an integer") } // prints "123 has an integer value of 123"
注意 尝试使用感叹号(!)去处理不存在的缺省值会触发一个运行时异常,在使用感叹号(!)强制展开前一定要确定缺省值包含一个非nil值。
使用缺省绑定的方式来判断缺省值是否含有值,如果含有值的情况,将其值作为临时的常量或者变量。缺省绑定可以使用if语句或者while语句检查缺省值中的值,然后将其值取出来赋给常量或者变量。if和while的更多信息会在以后的文章中详解。
用if语句使用缺省绑定:
if let constantName = someOptional { statements }
possibleNumber
的例子:
if let actualNumber = possibleNumber.toInt() { println("\(possibleNumber) has an integer value of \(actualNumber)") } else { println("\(possibleNumber) could not be converted to an integer") } <code class="code-voice"><span class="c">// prints "123 has an integer value of 123"</span></code>
如果上面的转换是成功的,if语句会执行第一个分支里的代码,而且常量actualNumber的值也可以在这个分支里使用,用缺省值所包含的值初始化这个常量,而且不需要使用感叹号的后缀去处理这个值,该例中,actualNumber只是简单的用于打印转换的结果而已(还有很多其他的用处)。
你可以使用常量和变量来进行缺省绑定,如果你想在if语句的第一个分支中操作actualNumber的值,你可以用变量的形式代替(var actualNumber=...),这是缺省值中包含的值会当做变量来使用。
如果你将一个缺省变量定义成nil值,代表该值没有任何值。
var serverResponseCode: Int? = 404 // serverResponseCode contains an actual Int value of 404 serverResponseCode = nil // serverResponseCode now contains no value
注意 nil不能用在非缺省的常量和变量上, 如果你代码中的常量或者变量,在特定情况下需要去处理缺省值,将其声明成合适类型的缺省值。
如果在不提供默认值的情况下,你要定义一个可选的常量或者变量时,那么该变量或者常量会自动设置成nil.
var surveyAnswer: String? // surveyAnswer is automatically set to nil
注意 Swift中的nil值和OC中nil值是不一样的,OC中nil代表的是指向不存在的值的指针。 但是Swift中的nil不是一个指针,它是一个确定类型的缺省值 任何类型的可选项都可以设置成nil,不局限于对象类型
综上所述,可选项表示一个常量或者变量可以没有值。可以使用if语句来检查其是否存在值。也可以通过可选绑定的方式去展开不存在的可选值。
有时候,在第一次赋值以后,含有值的可选项的代码结构式很清晰的。在这种情况下,它有助于减少在每次访问可选项的时候所做的检查和展开。因为有值的情况下,他们都是安全而无需检查。
这种类型的可选项称为隐式展开可选项(implicitly unwrapped optionals)。如果项隐式展开可选项的话,在你想要处理的可选项后面加上感叹号儿不是问号感。
当一个可选项的值在初始定义后就确定是存在的,而且可以肯定它会存在于某一个点上,这个时候,使用隐式是很有用的。在Swift中使用隐式展开主要用于类的初始化,预知详情,请看Unowned References and Implicitly Unwrapped Optional Properties.一文。
一个隐式展开的可选项在后台是一个正常的可选项,但是它也可以用于不是可选项的值,处理这样的值时就不需要展开它。下面的例子展示了String可选项和隐式打开可选项用法上的不同。
let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string." let assumedString: String! = "An implicitly unwrapped optional string." println(assumedString) // no exclamation mark is needed to access its value // prints "An implicitly unwrapped optional string."
注意 当你想处理一个不包含的任何值的隐式展开的可选项时,会引发一个错误,这和在没有任何值的可选项后面加上感叹号的结果是一样的。
if assumedString { println(assumedString) } // prints "An implicitly unwrapped optional string."
if let definiteString = assumedString { println(definiteString) } // prints "An implicitly unwrapped optional string."
注意 当一个变量有可能为nil值不应使用隐式地拆开可<pre name="code" class="plain">项在一个变量的生命周期里,如果你需要检查零值通常使用正常的可选项。
可选值需要我们去检查其中是否存在值。在编写代码时,可以很好的的处理可选值。然而在某些情况下,如果值是不存在或者特定情况下值是不安全的,此时想让代码继续执行是很困难的。在这种情况下,你就需要使用断言来终止你代码的执行,使得你有机会去调试无值的原因。
断言用于在运行时检查某个逻辑条件是否为true.从字面上来说断言就是判断一个条件是否为true.使用断言是为了确保之前的代码都是正确的。如果条件为true,代码会正常执行。如果条件为false,你的代码执行结束,你的应用也就结束啦。
如果在调试的时候,你的应用执行的过程中触发了断言,应用会立即停止,你能看到你的代码哪里出现了问题。在触发断言的地方你就可以检查一下应用的状态。断言是为了更好的帮助你调试你的代码。
通过调用assert函数设置断言,给函数传递一个表达式,返回的结果是布尔值。如果返回的结果是false,函数里的信息就会被显示出来。
let age = -3 assert(age >= 0, "A person's age cannot be less than zero") // this causes the assertion to trigger, because age is not >= 0
在上面的例子中,如果age>=0也就是age为非负值时会返回true,代码继续执行。如果age的值为负数,如上面的代码一样,那么age>=0的表达式会返回false,断言就会触发,应用终止。
断言信息不可以使用字符串插入。可以省略断言里面的输出信息,如下面的例子:
assert(age >= 0)
何时使用断言?正常情况下判断条件都为true,但是也有一定几率出现false的情况,这时候就可以使用断言来帮助你准确的捕捉这些某些异常:
预知更多请看 Subscripts 和Functions.
注意 断言会导致应用停止,如果无效情况出现的概率接近为0时,反而会使你的代码看起来不怎么简洁。 尽管如此,在无效情况可能会发生的情况下或者应用发布之前,断言是一种有效的检查bug的方式
(一个礼拜才翻译一篇。加油!!)