Swiftのオプションタイプ
Swift の Optional 型。欠損値の処理に使用されます。Optional は、「そこに値があり、それが x に等しい」または「そこに値が存在しない」ことを意味します。
Swfit言語はサフィックスを定義しますか? 名前付き型 Optional の短縮形として、次の 2 つの宣言は同等です。
var オプション整数: 整数?
varOptionalInteger: Optional<Int>
どちらの場合も、変数optionalIntegerはオプションの整数型です。type と? の間にはスペースがないことに注意してください 。
Optional は、None と Some(T) の 2 つのケースを含む列挙で、 value があるかどうかを示すために使用されます。どの型もオプションの型として明示的に宣言 (または暗黙的に変換) できます。オプションの型を宣言するときは、必ず括弧を使用して ? 演算子に適切なスコープを指定してください。たとえば、オプションの整数配列を宣言するには、(Int[]) と記述する必要がありますか? Int[]? と記述すると、エラーが報告されます。
初期値を指定せずにオプションの変数またはオプションのプロパティを宣言すると、その値はデフォルトで nil になります。
オプションのオプションは LogicValue プロトコルに準拠しているため、ブール環境で使用できます。この場合、オプションの型 T? に型 T の値が含まれている場合 (つまり、その値が Optional.Some(T) である場合)、オプションの型は true に等しく、それ以外の場合は false になります。
オプションの型のインスタンスに値が含まれている場合は、後置演算子を使用できます。この値にアクセスするには、次のようにします。
オプション整数 = 42
オプションの整数! // 42
演算子を使おう!値が nil のオプションの変数を取得すると、実行時エラーが発生します。
オプションのチェーンとオプションのバインディングを使用して、オプションの式に対する操作を選択的に実行できます。値が nil の場合、操作は実行されず、実行時エラーは報告されません。
Swift でのオプションの型の使用を理解するために、次の例を詳しく見てみましょう。
ココアを輸入する
myString:String はどこにありましたか? = なし
if myString != nil {
print(myString)
}それ以外{
print("文字列がnilです")
}
上記のプログラムの実行結果は次のようになります。
文字列がnilです
オプションの型は、Objective-C のポインターの nil 値に似ていますが、nil が役立つのはクラスのみであるのに対し、オプションの型はすべての型で使用でき、より安全です。
強制解析
オプションの型に実際に値が含まれていることを確認したら、オプションの名前に感嘆符 (!) を追加することで値を取得できます。この感嘆符は、「このオプションに値があることはわかっています。それを使用してください。」を意味します。これは、オプション値の強制アンラップと呼ばれます。
注:
を使用して!
存在しないオプションの値を取得すると、実行時エラーが発生します。!
値の解析を強制するためにオプションを使用する前に、オプションに非値が含まれていることを必ず確認してくださいnil
。
オプションの値の解析を強制するには、感嘆符 (!) を使用します。
ココアを輸入する
myString:String はどこにありましたか?
myString = "こんにちは、スウィフト!"
if myString != nil {
// 強制的に解析します
print( myString! )
}それ以外{
print("myString 値は nil")
}
自動解析
オプションの変数を宣言するときは、疑問符 (?) の代わりに感嘆符 (!) を使用できます。このように、オプションの変数を使用するときに値を取得するために感嘆符 (!) を追加する必要はなく、自動的に解決されます。
ココアを輸入する
ここで、myString:String!
myString = "こんにちは、スウィフト!"
if myString != nil {
print(myString)
}それ以外{
print("myString 値は nil")
}
オプションのバインディング
オプションのバインディングを使用して、オプションの型に値が含まれているかどうかを判断し、含まれている場合は、その値を一時定数または変数に割り当てます。オプションのバインディングを if ステートメントと while ステートメントで使用して、オプションの値を評価し、その値を定数または変数に割り当てることができます。
次のように if ステートメントにオプションのバインディングを記述します。
if let constantName = someOptional {
ステートメント
}
型の注釈
定数または変数を宣言するときに、定数または変数に格納される値の型を示す型注釈を追加できます。型の注釈を追加する場合は、定数または変数名の後にコロンとスペースを追加してから、型名を追加する必要があります。
var constantName:<データ型> = <オプションの初期値>
事例:
constB:Float = 3.14159 とします。
定数の名前付け
Swift は大文字と小文字を区別する言語であるため、大文字と小文字は異なります。
次の例のように、定数名には単純な Unicode 文字を使用することもできます。
Hello = "Hello World" にしましょう
印刷(こんにちは)
上記プログラムの実行結果は次のようになります:
Hello world
一定出力
変数と定数は、print (swift 2 では println を print に置き換えます) 関数を使用して出力できます。
次の例のように、括弧とバックスラッシュを使用して、文字列に定数を挿入できます。
let name = "新人チュートリアル"
let site = "http://www.runoob.com"
print("\(name) の公式 Web サイトのアドレスは: \(site)")
上記のプログラムの実行結果は次のようになります。
ルーキーチュートリアルの公式 Web サイトのアドレスは次のとおりです。 http://www.runoob.com
整数リテラル
整数リテラルには、10 進数、2 進数、8 進数、または 16 進数の定数を指定できます。2 進数の場合は 0b、8 進数の場合は 0o、16 進数の場合は 0x が付き、10 進数の場合は接頭辞がありません。
let binaryInteger = 0b10001 // 17 - バイナリ表現
let octalInteger = 0o21 // 17 - 8 進数表現
let hexadecimalInteger = 0x11 // 17 - 16 進数表現
浮動小数点リテラル
浮動小数点リテラルには、整数部、小数点、小数部、指数部があります。
特に指定がない限り、浮動小数点リテラルのデフォルトの導出型は、Swift 標準ライブラリ型では Double であり、64 ビット浮動小数点数を表します。
浮動小数点リテラルは、デフォルトでは 10 進数 (接頭辞なし) で表現されますが、16 進数 (接頭辞 0x を付ける) で表現することもできます。
10 進浮動小数点リテラルは、10 進数の数字文字列と、その後に続く小数部または指数部 (またはその両方) で構成されます。小数部は、小数点とそれに続く 10 進数の文字列で構成されます。指数部は、大文字または小文字の e に、10 の累乗と e の前の数量を掛けた値を表す 10 進数の文字列が接頭辞として付けられたもので構成されます。たとえば、1.25e2 は 1.25 ⨉ 10^2、つまり 125.0 を意味します。同様に、1.25e-2 は 1.25 ⨉ 10^-2、つまり 0.0125 を意味します。
16 進浮動小数点リテラルは、接頭辞 0x と、それに続くオプションの 16 進の小数部分と 16 進の指数部分で構成されます。16 進数の小数部は、小数点とそれに続く 16 進数の文字列で構成されます。指数部は、接頭語としての大文字または小文字の p と、その後に続く 2 の累乗と p の前の数量を表す 10 進数の文字列で構成されます。たとえば、0xFp2 は 15 ⨉ 2^2、つまり 60 を意味し、同様に、0xFp-2 は 15 ⨉ 2^-2、つまり 3.75 を意味します。
負の浮動小数点リテラルは、単項演算子のマイナス記号 - と浮動小数点リテラル (-42.5 など) で構成されます。
文字列リテラル
文字列リテラルは、次の形式の二重引用符で囲まれた文字列で構成されます。
「キャラクター」
文字列リテラルには、エスケープされていない二重引用符 (")、エスケープされていないバックスラッシュ (\)、キャリッジ リターン、または改行文字を含めることはできません。
エスケープ文字 | 意味 |
---|---|
\0 | ヌル文字 |
\\ | バックスラッシュ \ |
\b | バックスペース (BS)、現在位置を前の列に移動します |
\f | ページ送り (FF)、現在位置を次のページの先頭に移動します |
\n | 改行文字 |
\r | キャリッジリターン |
\t | 水平タブ |
\v | 垂直タブ |
\' | アポストロフィ |
「 | 二重引用符 |
\000 | 1 ~ 3 桁の 8 進数で表される任意の文字 |
\xhh... | 1 ~ 2 桁の 16 進数で表される任意の文字 |
ブールリテラル
ブール リテラルのデフォルトのタイプは Bool です。
ブール リテラルには 3 つの値があり、Swift では予約済みのキーワードです。
-
trueは真実という意味です。
-
false は 偽を意味します。
-
nil は 値がないことを意味します。
ビット演算子
ビット単位の演算子は、バイナリ ビットの演算に使用されます。次の表に示すように、~、&、|、^ はそれぞれ、否定、ビット単位の AND、ビット単位の AND、ビット単位の AND、および XOR 演算です。
p | q | P&Q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
ビット演算を実行します。
オペレーター | 説明する | 図 | 例 |
---|---|---|---|
& | ビット単位の AND。ビット単位の AND 演算子は 2 つの数値を演算し、新しい数値を返します。この数値の各ビットは、両方の入力数値の同じビットが 1 の場合にのみ 1 になります。 | (A & B) 結果は 12 で、2 進数では 0000 1100 となります。 | |
| | ビット単位の OR。ビットごとの OR 演算子 | 2 つの数値を比較し、新しい数値を返します。この数値の各ビットを 1 に設定する条件は、2 つの入力数値の同じビットが 0 でないことです (つまり、どちらか一方が 1、または両方とも 1)は 1 1) です。 | (A | B) 結果は 61 で、2 進数では 0011 1101 となります。 | |
^ | ビット単位の XOR。ビット単位の XOR 演算子 ^ は 2 つの数値を比較し、数値を返します。2 つの入力数値の同じビットが異なる場合、この数値の各ビットは 1 に設定されます。同じ場合は、0 に設定されます。 。 | (A ^ B) の結果は 49、2 進数では 0011 0001 になります。 | |
~ | ビットごとの否定演算子 ~ は、オペランドの各ビットを否定します。 | (~A ) は -61 になります。これは、2 進数では 2 の補数形式で 1100 0011 になります。 | |
<< | ビット単位で左にシフトします。左シフト演算子 (<<) は、オペランドのすべてのビットを指定されたビット数だけ左にシフトします。 | 次の図は、11111111 << 1 (11111111 を 1 ビット左にシフト) の結果を示しています。青色の数字はシフトされたビットを表し、灰色は破棄されたビットを表し、空のビットはオレンジ色の 0 で埋められます。 |
<< 2 の結果は 240 となり、2 進数では 1111 0000 になります。 |
>> | ビット単位で右にシフトします。右シフト演算子 (>>) は、オペランドのすべてのビットを指定されたビット数だけ右にシフトします。 | 次の図は、11111111 >> 1 (11111111 を 1 つ右にシフト) の結果を示しています。青色の数字はシフトされたビットを表し、灰色は破棄されたビットを表し、空のビットはオレンジ色の 0 で埋められます。 |
A >> 2 结果为 15, 二进制为 0000 1111 |
Swift 提供了两个区间的运算符。
运算符 | 描述 | 实例 |
---|---|---|
闭区间运算符 | 闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间,b必须大于等于a。 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在for-in循环中: | 1...5 区间值为 1, 2, 3, 4 和 5 |
半开区间运算符 | 半开区间(a..<b)定义一个从a到b但不包括b的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。 | 1..< 5 区间值为 1, 2, 3, 和 4 |
运算符优先级
在一个表达式中可能包含多个有不同运算符连接起来的、具有不同数据类型的数据对象;由于表达式有多种运算,不同的运算顺序可能得出不同结果甚至出现错误运算错误,因为当表达式中含多种运算时,必须按一定顺序进行结合,才能保证运算的合理性和结果的正确性、唯一性。
优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。
相同优先级中,按结合顺序计算。大多数运算是从左至右计算,只有三个优先级是从右至左结合的,它们是单目运算符、条件运算符、赋值运算符。
基本的优先级需要记住:
- 指针最优,单目运算优于双目运算。如正负号。
- 先乘除(模),后加减。
- 先算术运算,后移位运算,最后位运算。请特别注意:1 << 3 + 2 & 7 等价于 (1 << (3 + 2))&7
- 逻辑运算最后计算
Swift 运算符优先级 (从高到低):
运算符 | 实例 |
---|---|
位运算符 | >> &<< &>> >> |
乘法运算符 | &* % & * / |
加法运算符 | | &+ &- + - ^ |
区间运算符 | ..< ... |
类型转换运算符 | is as |
nil 的聚合运算 | ?? |
比较运算符 | != > < >= <= === == |
逻辑与运算符 | && |
逻辑或运算符 | || |
波浪箭头 | ~> |
三元运算符 | ?: |
箭头函数 | ( ) |
赋值运算符 | |= %= /= &<<= &>>= &= *= >>= <<= ^= += -= |
合并空值运算符:??
- 合并空值运算符 a ?? b 如果可选项 a 有值则展开,如果没有值,是 nil,则返回默认值 b。
- 表达式 a 必须是一个可选类型,表达式 b 必须与 a 的存储类型相同
- 合并空值运算符,实际上是三元运算符作用到 Optional 上的缩写 a != nil ? a! : b
- 如果 a 的值是非空,b的值将不会被考虑,也就是合并空值运算符是短路的。
var fff : [String]?
let k=["test"] as? [String] ?? ["str1", "str2", "str3"]
print(k)
let ff=fff as? [String] ?? ["str1", "str2", "str3"]
print(ff)
结果:
["test"]
["str1", "str2", "str3"]
循环类型
Swift 语言提供了以下几种循环类型。点击链接查看每个类型的详细描述:
循环类型 | 描述 |
---|---|
遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。 | |
该循环方式在 Swift 3 中已经弃用。 用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。 |
|
运行一系列语句,如果条件为true,会重复运行,直到条件变为false。 | |
类似 while 语句区别在于判断循环条件之前,先执行一次循环的代码块。 |
循环控制语句
循环控制语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Swift 以下几种循环控制语句:
控制语句 | 描述 |
---|---|
告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。 | |
中断当前循环。 | |
如果在一个case执行完后,继续执行下面的case,需要使用fallthrough(贯穿)关键字。 |
创建字符串
你可以通过使用字符串字面量或 String 类的实例来创建一个字符串:
// 使用字符串字面量
var stringA = "Hello, World!"
// String 实例化
var stringB = String("Hello, World!")
空字符串
你可以使用空的字符串字面量赋值给变量或初始化一个String类的实例来初始值一个空的字符串。 我们可以使用字符串属性 isEmpty 来判断字符串是否为空:
// 使用字符串字面量创建空字符串
var stringA = ""
if stringA.isEmpty {
print( "stringA 是空的" )
} else {
print( "stringA 不是空的" )
}
// 实例化 String 类来创建空字符串
let stringB = String()
字符串常量
你可以将一个字符串赋值给一个变量或常量,变量是可修改的,常量是不可修改的。
// stringA 可被修改
var stringA = "菜鸟教程:"
stringA += "http://www.runoob.com"
print( stringA )
输出结果:
菜鸟教程:http://www.runoob.com
字符串中插入值
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。 您插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中:
var varA = 20
let constA = 100
var varC:Float = 20.0
var stringA = "\(varA) 乘于 \(constA) 等于 \(varC * 100)"
print( stringA )
以上程序执行输出结果为:
20 乘于 100 等于 2000.0
字符串连接
字符串可以通过 + 号来连接,实例如下:
let constA = "菜鸟教程:"
let constB = "http://www.runoob.com"
var stringA = constA + constB
print( stringA )
以上程序执行输出结果为:
菜鸟教程:http://www.runoob.com
字符串长度
字符串长度使用 String.count 属性来计算,实例如下:
Swift 3 版本使用的是 String.characters.count
import Cocoa
var varA = "www.runoob.com"
print( "\(varA), 长度为 \(varA.count)" )
以上程序执行输出结果为:
www.runoob.com, 长度为 14
字符串比较
你可以使用 == 来比较两个字符串是否相等:
var varA = "Hello, Swift!"
var varB = "Hello, World!"
if varA == varB {
print( "\(varA) 与 \(varB) 是相等的" )
} else {
print( "\(varA) 与 \(varB) 是不相等的" )
}
以上程序执行输出结果为:
Hello, Swift! 与 Hello, World! 是不相等的
Unicode 字符串
Unicode 是一个国际标准,用于文本的编码,Swift 的 String 类型是基于 Unicode建立的。你可以循环迭代出字符串中 UTF-8 与 UTF-16 的编码,实例如下:
import Cocoa
var unicodeString = "菜鸟教程"
print("UTF-8 编码: ")
for code in unicodeString.utf8 {
print("\(code) ")
}
print("\n")
print("UTF-16 编码: ")
for code in unicodeString.utf16 {
print("\(code) ")
}
以上程序执行输出结果为:
UTF-8 编码:
232
143
156
233
184
159
230
149
153
231
168
139
UTF-16 编码:
33756
40479
25945
31243
字符串函数及运算符
Swift 支持以下几种字符串函数及运算符:
序号 | 函数/运算符 & 描述 |
---|---|
1 | isEmpty 判断字符串是否为空,返回布尔值 |
2 | hasPrefix(prefix: String) 检查字符串是否拥有特定前缀 |
3 | hasSuffix(suffix: String) 检查字符串是否拥有特定后缀。 |
4 | Int(String) 转换字符串数字为整型。 实例: let myString: String = "256" let myInt: Int? = Int(myString) |
5 | String.count Swift 3 版本使用的是 String.characters.count 计算字符串的长度 |
6 | utf8 您可以通过遍历 String 的 utf8 属性来访问它的 UTF-8 编码 |
7 | utf16 您可以通过遍历 String 的 utf8 属性来访问它的 utf16 编码 |
8 | unicodeScalars 您可以通过遍历String值的unicodeScalars属性来访问它的 Unicode 标量编码. |
9 | + 连接两个字符串,并返回一个新的字符串 |
10 | += 连接操作符两边的字符串并将新字符串赋值给左边的操作符变量 |
11 | == 判断两个字符串是否相等 |
12 | < 比较两个字符串,对两个字符串的字母逐一比较。 |
13 | != 比较两个字符串是否不相等。 |
字符串分割数组 -- 基于空格
let fullName = "First Last" let fullNameArr = fullName.characters.split{$0 == " "}.map(String.init) // or simply: // let fullNameArr = fullName.characters.split{" "}.map(String.init) //现在的方法 var fullNameArr = fullName.split(separator: " ") fullNameArr[0] // First fullNameArr[1] // Last
字符串的遍历 Swift 5:
let str = "菜鸟教程runoob.com" print("---- 正序遍历 ----") var i = 0 for ch in str { print("\(i): \(ch)") i += 1 } print("---- 逆序遍历 ----") var j = str.count for ch in str.reversed() { j -= 1 print("\(j): \(ch)") } print("---- 基于索引的正序遍历 ----") for i in 0..<str.count { let ch: Character = str[str.index(str.startIndex, offsetBy: i)] print("\(i): \(ch)") } print("---- 基于索引的逆序遍历 ----") //for i in stride(from: str.count - 1, to: -1, by: -1) { for i in stride(from: str.count - 1, through: 0, by: -1) { let ch: Character = str[str.index(str.startIndex, offsetBy: i)] print("\(i): \(ch)") } print("---- 基于EnumeratedSequence的遍历 ----") for (i, ch) in str.enumerated() { print("\(i): \(ch)") }
字符串连接字符
以下实例演示了使用 String 的 append() 方法来实现字符串连接字符:
import Cocoa var varA:String = "Hello " let varB:Character = "G" varA2.append(" \(varB) " ) print("varC = \(varA)")
以上程序执行输出结果为:
varC = Hello G
修改数组
你可以使用 append() 方法或者赋值运算符 += 在数组末尾添加元素,如下所示,我们初始化一个数组,并向其添加元素:
import Cocoa
var someInts = [Int]()
someInts.append(20)
someInts.append(30)
someInts += [40]
var someVar = someInts[0]
print( "第一个元素的值 \(someVar)" )
print( "第二个元素的值 \(someInts[1])" )
print( "第三个元素的值 \(someInts[2])" )
以上程序执行输出结果为:
第一个元素的值 20
第二个元素的值 30
第三个元素的值 40
我们也可以通过索引修改数组元素的值:
import Cocoa
var someInts = [Int]()
someInts.append(20)
someInts.append(30)
someInts += [40]
// 修改最后一个元素
someInts[2] = 50
如果我们同时需要每个数据项的值和索引值,可以使用 String 的 enumerate() 方法来进行数组遍历。实例如下:
import Cocoa
var someStrs = [String]()
someStrs.append("Apple")
someStrs.append("Amazon")
someStrs.append("Runoob")
someStrs += ["Google"]
for (index, item) in someStrs.enumerated() {
print("在 index = \(index) 位置上的值为 \(item)")
}
合并数组
我们可以使用加法操作符(+)来合并两种已存在的相同类型数组。新数组的数据类型会从两个数组的数据类型中推断出来:
count 属性
我们可以使用 count 属性来计算数组元素个数:
isEmpty 属性
我们可以通过只读属性 isEmpty 来判断数组是否为空,返回布尔值:
创建数组的方法不推荐使用文章中记载的方法。
推荐:
var names: [String] = []
var lookup: [String: Int] = [:]
不推荐:
var names = [String]()
var lookup = [String: Int]()
创建字典
我们可以使用以下语法来创建一个特定类型的空字典:
var someDict = [KeyType: ValueType]()
以下是创建一个空字典,键的类型为 Int,值的类型为 String 的简单语法:
var someDict = [Int: String]()
以下为创建一个字典的实例:
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
访问字典
我们可以根据字典的索引来访问数组的元素,语法如下:
var someVar = someDict[key]
修改字典
我们可以使用 updateValue(forKey:) 增加或更新字典的内容。如果 key 不存在,则添加值,如果存在则修改 key 对应的值。updateValue(_:forKey:)方法返回Optional值。实例如下:
import Cocoa var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"] var oldVal = someDict.updateValue("One 新的值", forKey: 1) var someVar = someDict[1] print( "key = 1 旧的值 \(oldVal)" ) print( "key = 1 的值为 \(someVar)" ) print( "key = 2 的值为 \(someDict[2])" ) print( "key = 3 的值为 \(someDict[3])" )
以上程序执行输出结果为:
key = 1 旧的值 Optional("One") key = 1 的值为 Optional("One 新的值") key = 2 的值为 Optional("Two") key = 3 的值为 Optional("Three")
移除 Key-Value 对
我们可以使用 removeValueForKey() 方法来移除字典 key-value 对。如果 key 存在该方法返回移除的值,如果不存在返回 nil 。实例如下:
import Cocoa
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var removedValue = someDict.removeValue(forKey: 2)
print( "key = 1 的值为 \(someDict[1])" )
print( "key = 2 的值为 \(someDict[2])" )
print( "key = 3 的值为 \(someDict[3])" )
以上程序执行输出结果为:
key = 1 的值为 Optional("One")
key = 2 的值为 nil
key = 3 的值为 Optional("Three")
你也可以通过指定键的值为 nil 来移除 key-value(键-值)对。实例如下:
someDict[2] = nil
print( "key = 2 的值为 \(someDict[2])" )
key = 2 的值为 nil
遍历字典
我们可以使用 for-in 循环来遍历某个字典中的键值对。实例如下:
import Cocoa
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
for (key, value) in someDict {
print("字典 key \(key) - 字典 value \(value)")
}
字典转换为数组
你可以提取字典的键值(key-value)对,并转换为独立的数组。实例如下:
import Cocoa var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"] let dictKeys = [Int](someDict.keys) let dictValues = [String](someDict.values)
print("输出字典的键(key)") for (key) in dictKeys { print("\(key)") } print("输出字典的值(value)") for (value) in dictValues { print("\(value)") }
以上程序执行输出结果为:
输出字典的键(key) 2 3 1 输出字典的值(value) Two Three One
count 属性
我们可以使用只读的 count 属性来计算字典有多少个键值对:
isEmpty 属性
Y我们可以通过只读属性 isEmpty 来判断字典是否为空,返回布尔值:
函数定义
Swift 定义函数使用关键字 func。
定义函数的时候,可以指定一个或多个输入参数和一个返回值类型。
每个函数都有一个函数名来描述它的功能。通过函数名以及对应类型的参数值来调用这个函数。函数的参数传递的顺序必须与参数列表相同。
函数的实参传递的顺序必须与形参列表相同,-> 后定义函数的返回值类型。
语法
func funcname(形参) -> returntype
{
Statement1
Statement2
……
Statement N
return parameters
}
实例
以下我们定义了一个函数名为 runoob 的函数,形参的数据类型为 String,返回值也为 String:
import Cocoa
func runoob(site: String) -> String {
return (site)
}
print(runoob(site: "www.runoob.com"))
以上程序执行输出结果为:
www.runoob.com
函数参数
函数可以接受一个或者多个参数,这些参数被包含在函数的括号之中,以逗号分隔。
以下实例向函数 runoob 传递站点名 name 和站点地址 site:
import Cocoa
func runoob(name: String, site: String) -> String {
return name + site
}
print(runoob(name: "菜鸟教程:", site: "www.runoob.com"))
print(runoob(name: "Google:", site: "www.google.com"))
以上程序执行输出结果为:
菜鸟教程:www.runoob.com
Google:www.google.com
不带参数函数
我们可以创建不带参数的函数。
语法:
func funcname() -> datatype {
return datatype
}
元组作为函数返回值
函数返回值类型可以是字符串,整型,浮点型等。
元组与数组类似,不同的是,元组中的元素可以是任意类型,使用的是圆括号。
你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。
下面的这个例子中,定义了一个名为minMax(_:)的函数,作用是在一个Int数组中找出最小值与最大值。
import Cocoa
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("最小值为 \(bounds.min) ,最大值为 \(bounds.max)")
minMax(_:)函数返回一个包含两个Int值的元组,这些值被标记为min和max,以便查询函数的返回值时可以通过名字访问它们。
以上程序执行输出结果为:
最小值为 -6 ,最大值为 109
如果你不确定返回的元组一定不为nil,那么你可以返回一个可选的元组类型。
你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如(Int, Int)?或(String, Int, Bool)?
注意
可选元组类型如(Int, Int)?
与元组包含可选类型如(Int?, Int?)
是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
前面的minMax(_:)
函数返回了一个包含两个Int
值的元组。但是函数不会对传入的数组执行任何安全检查,如果array
参数是一个空数组,如上定义的minMax(_:)
在试图访问array[0]
时会触发一个运行时错误。
为了安全地处理这个"空数组"问题,将minMax(_:)
函数改写为使用可选元组返回类型,并且当数组为空时返回nil
:
import Cocoa
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("最小值为 \(bounds.min),最大值为 \(bounds.max)")
}
以上程序执行输出结果为:
最小值为 -6,最大值为 109
没有返回值函数
下面是 runoob(_:) 函数的另一个版本,这个函数接收菜鸟教程官网网址参数,没有指定返回值类型,并直接输出 String 值,而不是返回它:
import Cocoa
func runoob(site: String) {
print("菜鸟教程官网:\(site)")
}
runoob(site: "http://www.runoob.com")
以上程序执行输出结果为:
菜鸟教程官网:http://www.runoob.com
函数参数名称
函数参数都有一个外部参数名和一个局部参数名。
局部参数名
局部参数名在函数的实现内部使用。
func sample(number: Int) {
println(number)
}
以上实例中 number 为局部参数名,只能在函数体内使用。
外部参数名
你可以在局部参数名前指定外部参数名,中间以空格分隔,外部参数名用于在函数调用时传递给函数的参数。
如下你可以定义以下两个函数参数名并调用它:
import Cocoa func pow(firstArg a: Int, secondArg b: Int) -> Int { var res = a for _ in 1..<b { res = res * a } print(res) return res } pow(firstArg:5, secondArg:3)
以上程序执行输出结果为:
125
可变参数
可变参数可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数,其数量是不确定的。
可变参数通过在变量类型名后面加入(...)的方式来定义。
import Cocoa func vari<N>(members: N...){ for i in members { print(i) } } vari(members: 4,3,5) vari(members: 4.5, 3.1, 5.6) vari(members: "Google", "Baidu", "Runoob")
以上程序执行输出结果为:
4
3
5
4.5
3.1
5.6
Google
Baidu
Runoob
常量,变量及 I/O 参数
一般默认在函数中定义的参数都是常量参数,也就是这个参数你只可以查询使用,不能改变它的值。
如果想要声明一个变量参数,可以在参数定义前加 inout 关键字,这样就可以改变这个参数的值了。
例如:
func getName(_ name: inout String).........
此时这个 name 值可以在函数中改变。
一般默认的参数传递都是传值调用的,而不是传引用。所以传入的参数在函数内改变,并不影响原来的那个参数。传入的只是这个参数的副本。
当传入的参数作为输入输出参数时,需要在参数名前加 & 符,表示这个值可以被函数修改。
使用函数类型
在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它:
var addition: (Int, Int) -> Int = sum
函数嵌套
函数嵌套指的是函数内定义一个新的函数,外部的函数可以调用函数内定义的函数。
实例如下:
import Cocoa
func calcDecrement(forDecrement total: Int) -> () -> Int {
var overallDecrement = 0
func decrementer() -> Int {
overallDecrement -= total
return overallDecrement
}
return decrementer
}
let decrem = calcDecrement(forDecrement: 30)
print(decrem())
以上程序执行输出结果为:
-30
原始值(Raw Values)
我们刚刚了解了关联值类型的枚举的使用,Swift 的枚举类型还提供了另外一个叫做原始值(Raw Values)的实现。和关联值不同,它为枚举项提供一个默认值,这个默认值是在编译的时候就确定的。而不像关联值那样,要再实力化枚举值的时候才能确定。
这也就是说,原始值对于同一个枚举项都是一样的。而关联值对于同一个枚举项只是值的类型相同,但具体的取值也是不同的。
下面我们来看一下定义枚举原始值 (Raw Values) 的方法:
-
enum WeekDayWithRaw : String {
-
case Monday = "1. Monday"
-
case Tuesday = "2. Tuesday"
-
case Wednesday = "3. Wednesday"
-
case Thursday = "4. Thursday"
-
case Friday = "5. Friday"
-
case Saturday = "6. Saturday"
-
case Sunday = "7. Sunday"
-
}
还是表示星期的枚举类型,我们对每个枚举项都定义了一个默认的原始值,注意一下我们定义枚举的第一行代码,enum WeekDayWithRaw : String
我们在枚举定义的最后,多加了一个String
关键字,这就表示这个枚举的原始值(Raw Values) 是 String
类型的。
在我们下面的定义中,也体现了这一点。对于所有的枚举项,我们赋给的原始值都是 String
类型的。
定义好了原始值后,我们就可以用枚举项的 rawValue
属性来输出它:
println(WeekDayWithRaw.Saturday.rawValue) //6. Saturday
我们还可以通过原始值(Raw Values) 来初始化枚举类型:
let day = WeekDayWithRaw(rawValue: "3. Wednesday")
这个初始化方法的返回值是一个 Optionals。所以我们可以用 Optionals 的组合链来使用它的返回值:
-
if let day = WeekDayWithRaw(rawValue: "3. Wednesday") {
-
println(day)
-
}else{
-
println("init fail")
-
}
返回值为 Optionals 的类型,代表这个方法可以返回一个具体的值,也可以返回 nil
, 因为我们传入初始化方法的原始值,可能会不等于我们预设的那几个值,比如我们这样初始化一个枚举:
let day = WeekDayWithRaw(rawValue: "No Exist Value")
如上面所示,"No Exist Value" 这个值和我们定义个原始值列表中任何一项都不对应,所以这个初始化是会失败的,在这种情况下初始化方法会返回 nil,来表示初始化失败。
所以基于这种情况,我们需要对返回值进行判断,这也就是我们上面的 if 判断的用处所在:
-
if let day = WeekDayWithRaw(rawValue: "3. Wednesday") {
-
println(day)
-
}else{
-
println("init fail")
-
}
Swift 闭包
闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。
全局函数和嵌套函数其实就是特殊的闭包。
闭包的形式有:
全局函数 | 嵌套函数 | 闭包表达式 |
有名字但不能捕获任何值。 | 有名字,也能捕获封闭函数内的值。 | 无名闭包,使用轻量级语法,可以根据上下文环境捕获值。 |
Swift中的闭包有很多优化的地方:
语法
以下定义了一个接收参数并返回指定类型的闭包语法:
{(parameters) -> return type in statements }
- 根据上下文推断参数和返回值类型
- 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
- 可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)
- 提供了尾随闭包语法(Trailing closure syntax)
以下闭包形式接收两个参数并返回布尔值:
{(Int, Int) -> Bool in
Statement1
Statement 2
---
Statement n
}
实例
import Cocoa
let divide = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
let result = divide(200, 20)
print (result)
以上程序执行输出结果为:
10
闭包表达式
闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
sorted 方法
Swift 标准库提供了名为 sorted(by:) 的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。
排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。
sorted(by:)方法需要传入两个参数:
实例
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sorted(by: backwards)
print(reversed)
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
如果第一个字符串 (s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 对于字符串中的字符来说,"大于" 表示 "按照字母顺序较晚出现"。 这意味着字母"B"大于字母"A",字符串"S"大于字符串"D"。 其将进行字母逆序排序,"AT"将会排在"AE"之前。
参数名称缩写
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
实例
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted( by: { $0 > $1 } )
print(reversed)
$0和$1表示闭包中第一个和第二个String类型的参数。
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
如果你在闭包表达式中使用参数名称缩写, 您可以在闭包参数列表中省略对其定义, 并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字同样也可以被省略.
运算符函数
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
Swift 的String
类型定义了关于大于号 (>
) 的字符串实现,其作为一个函数接受两个String
类型的参数并返回Bool
类型的值。 而这正好与sort(_:)
方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted(by: >)
print(reversed)
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
以上三种方法方法实现相同:
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sorted(by: backwards)
尾随闭包
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
实例
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] //尾随闭包 var reversed = names.sorted() { $0 > $1 } print(reversed)
sort() 后的 { $0 > $1} 为尾随闭包。
以上程序执行输出结果为:
["S", "D", "BE", "AT", "AE"]
注意: 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把
()
省略掉。reversed = names.sorted { $0 > $1 }
捕获值
闭包可以在其定义的上下文中捕获常量或变量。
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
闭包是引用类型
上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。
这是因为函数和闭包都是引用类型。
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
-
Swift 中使用 enum 关键词来创建枚举并且把它们的整个定义放在一对大括号内:
enum enumname {
// 枚举定义放在这里
}
举中定义的值(如 Sunday
,Monday
,……
和Saturday
)是这个枚举的成员值(或成员)。case
关键词表示一行新的成员值将被定义。
注意: 和 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的
DaysofaWeek
例子中,Sunday
,Monday
,……
和Saturday
不会隐式地赋值为0
,1
,……
和6
。相反,这些枚举成员本身就有完备的值,这些值是已经明确定义好的DaysofaWeek
类型。
var weekDay = DaysofaWeek.THURSDAY
weekDay
的类型可以在它被DaysofaWeek
的一个可能值初始化时推断出来。一旦weekDay
被声明为一个DaysofaWeek
,你可以使用一个缩写语法(.)将其设置为另一个DaysofaWeek
的值:
var weekDay = .THURSDAY
当weekDay
的类型已知时,再次为其赋值可以省略枚举名。使用显式类型的枚举值可以让代码具有更好的可读性。
枚举可分为相关值与原始值。
相关值与原始值的区别
相关值 | 原始值 |
---|---|
不同数据类型 | 相同数据类型 |
实例: enum {10,0.8,"Hello"} | 实例: enum {10,35,50} |
值的创建基于常量或变量 | 预先填充的值 |
相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。 | 原始值始终是相同的 |
相关值
以下实例中我们定义一个名为 Student 的枚举类型,它可以是 Name 的一个字符串(String),或者是 Mark 的一个相关值(Int,Int,Int)。
import Cocoa enum Student{ case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Runoob") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("学生的名字是: \(studName)。") case .Mark(let Mark1, let Mark2, let Mark3): print("学生的成绩是: \(Mark1),\(Mark2),\(Mark3)。") }
以上程序执行输出结果为:
学生的成绩是: 98,97,95。
原始值
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
在原始值为整数的枚举时,不需要显式的为每一个成员赋值,Swift会自动为你赋值。
例如,当使用整数作为原始值时,隐式赋值的值依次递增1。如果第一个值没有被赋初值,将会被自动置为0。
import Cocoa enum Month: Int { case January = 1, February, March, April, May, June, July, August, September, October, November, December } let yearMonth = Month.May.rawValue print("数字月份为: \(yearMonth)。")
以上程序执行输出结果为:
数字月份为: 5。
Swift 结构体是构建代码所用的一种通用且灵活的构造体。
我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。
与 C 和 Objective C 不同的是:
-
结构体不需要包含实现文件和接口。
-
结构体允许我们创建一个单一文件,且系统会自动生成面向其它代码的外部接口。
结构体总是通过被复制的方式在代码中传递,因此它的值是不可修改的。
语法
我们通过关键字 struct 来定义结构体:
struct nameStruct {
Definition 1
Definition 2
……
Definition N
}
实例
我们定义一个名为 MarkStruct 的结构体 ,结构体的属性为学生三个科目的分数,数据类型为 Int:
struct MarkStruct{ var mark1: Int var mark2: Int var mark3: Int }
我们可以通过结构体名来访问结构体成员。
通过 . 号来访问结构体成员的值。
结构体应用
在你的代码中,你可以使用结构体来定义你的自定义数据类型。
结构体实例总是通过值传递来定义你的自定义数据类型。
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
- 结构体的主要目的是用来封装少量相关简单数据值。
- 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
- 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
- 结构体不需要去继承另一个已存在类型的属性或者行为。
举例来说,以下情境中适合使用结构体:
- 几何形状的大小,封装一个
width
属性和height
属性,两者均为Double
类型。 - 一定范围内的路径,封装一个
start
属性和length
属性,两者均为Int
类型。 - 三维坐标系内一点,封装
x
,y
和z
属性,三者均为Double
类型。
结构体实例是通过值传递而不是通过引用传递。
Swift 类是构建代码所用的一种通用且灵活的构造体。
我们可以为类定义属性(常量、变量)和方法。
与其他编程语言所不同的是,Swift 并不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其它代码的外部接口。