error类型 swift
2018-05-25 10:45:00 weixin_34326429 阅读数 45

我们定义了一个LoginError, 并实现localizedDescription方法

enum LoginError: Error {
    case usernameEmpty
    case passwordEmpty
    
    var localizedDescription: String {
        switch self {
        case .usernameEmpty:
            return "请输入用户名"
        case .passwordEmpty:
            return "请输入密码"
        }
    }
}

将我们定义的枚举转成Error, 预想的localizedDescription应该为请输入用户名

let error: Error = LoginError.usernameEmpty
print(error.localizedDescription)//The operation couldn’t be completed. (LoginError error 0.)

但是这个打印结果为:
The operation couldn’t be completed. (LoginError error 0.) 并不是我们所期望的

那我们要怎么做呢

只需要实现LocalizedError

enum LoginError: Error {
    case usernameEmpty
    case passwordEmpty
}

extension LoginError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .usernameEmpty:
            return "请输入用户名"
        case .passwordEmpty:
            return "请输入密码"
        }
    }
}

再次测试:

let error: Error = LoginError.usernameEmpty
print(error.localizedDescription) //请输入用户名

这就拿到我们期望的结果了

参考

2014-07-20 02:18:00 weixin_34122548 阅读数 29

swift 类型

变量声明

用let来声明常量,用var来声明变量
可以在一行中声明多个常量或者多个变量,用逗号隔开

    var x = 0.0, y = 0.0, z = 0.0

类型安全

Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。
一个变量是什么类型,通过两种方式来表达:

类型标注
类型标注表示这个变量可以存储某种类型的值,声明中的冒号代表着“是...类型”:
            var welcomeMessage: String
        
类型推断
如果你没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。

注意:当推断浮点数的类型时,Swift 总是会选择Double而不是Float。一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型

变量命名

  • 当你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型
  • 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。
  • 常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错

打印变量和常量

println是一个用来输出的全局函数,输出的内容会在最后换行。
xcode的playground使用println,点击工具条的show the Assistant editor.即能显示输出

  var str = "Hello, playground"
  println(str)

类型转换

SomeType(ofInitialValue)是调用 Swift 构造器并传入一个初始值的默认方法。
注意,你并不能传入任意类型的值,只能传入SomeType内部有对应构造器的值。
不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型)
不同整数类型的变量和常量可以存储不同范围的数字,如果数字超出了常量或者变量可存储的范围,编译的时候会报错:

  let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
//在语言内部,UInt16有一个构造器,可以接受一个UInt8类型的值,所以这个构造器可以用现有的UInt8来创建一个新的UInt16
let twoThousandAndOne = twoThousand + UInt16(one)

类型别名

类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias关键字来定义类型别名。

  typealias AudioSample = UInt16
  var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0

断言

可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。
如果条件判断为true,代码运行会继续进行;如果条件判断为false,代码运行停止,你的应用被终止。

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发,代码运行停止,应用被终止

可以做调试使用

整型 Int

Swift 提供了8,16,32和64位的有符号和无符号整数类型。
Swift 提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同:

  • 在32位平台上,Int和Int32长度相同。
  • 在64位平台上,Int和Int64长度相同。

Swift 也提供了一个特殊的无符号类型UInt,长度与当前平台的原生字长相同:

  • 在32位平台上,UInt和UInt32长度相同。
  • 在64位平台上,UInt和UInt64长度相同。
尽量不要使用UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。
除了这种情况,最好使用Int,即使你要存储的值已知是非负的。
统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断

浮点型 Double和Float

  • Double表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
  • Float表示32位浮点数。精度要求不高的话可以使用此类型。

布尔型 Bool

Swift 有两个布尔常量,true和false:

  let orangesAreOrange = true
let turnipsAreDelicious = false

字符窜 String 和 Character(字符)

Swift 的String类型表示特定序列的Character(字符)类型值的集合,不能用下标取

  for character in "Dog!🐶" {
    println(character)
}
let yenSign: Character = "¥"

字符窜连接通过+号将两个字符窜相连

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化 String 实例

emptyString == anotherEmptyString  //true

if emptyString.isEmpty {
    println("什么都没有")
}

let string1 = "hello"
let string2 = " there"
let character1: Character = "!"
let character2: Character = "?"

let stringPlusCharacter = string1 + character1        // 等于 "hello!"
let stringPlusString = string1 + string2              // 等于 "hello there"
let characterPlusString = character1 + string1        // 等于 "!hello"
let characterPlusCharacter = character1 + character2  // 等于 "!?"

字符窜插值

字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。

let multiplier = 3
let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)"

插值字符串中写在括号中的表达式不能包含非转义双引号 (") 和反斜杠 (\),并且不能包含回车或换行符。 插值可以省去额外的加号进行表达式的连接

数组 Array

数组按顺序存储相同类型的数据

声明数组

  var shoppingList: String[] = ["Eggs", "Milk"]
  var shoppingList = ["Eggs", "Milk"]
  var someInts = Int[]()
  var threeDoubles = Double[](count: 3, repeatedValue:0.0)// [0.0, 0.0, 0.0]
  var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)//类型推断[2.5, 2.5, 2.5]

添加数据,修改数据,读取数据,删除数据,

  //添加数据
  shoppingList.append("Flour")
  shoppingList += "Baking Powder"
  shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
  shoppingList.insert("Maple Syrup", atIndex: 0)

  //修改数据
  shoppingList[0] = "aaa"
  shoppingList[4...6] = ["Bananas", "Apples"]//区间操作符,不能越界想数组尾部添加数据

  //读取数据
  var firstItem = shoppingList[0]
  for item in shoppingList {
      println(item)
  }
  //全局enumerate函数来进行数组遍历。enumerate返回一个由每一个数据项索引值和数据值组成的元组
  for (index, value) in enumerate(shoppingList) {
    println("Item \(index + 1): \(value)")
  }

  //删除数据
  shoppingList.removeAtIndex(0)
  let apples = shoppingList.removeLast()
  someInts = [] //重置未空数组

字典 Dictionary

无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)

声明字典

  var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
  var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
  var namesOfIntegers = Dictionary<Int, String>()ßß

读取字典和修改字典数据,删除字典数据(使用nil)

airports.updateValue("Dublin Internation", forKey: "DUB")
airports["APL"] = "Apple Internation"
airports["APL"] = nil  // APL现在被移除了
airports.removeValueForKey("DUB")
namesOfIntegers = [:]//重置为空字典

字典遍历,Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。

  //注意这里没有使用enumerate
for (airportCode, airportName) in airports {
    println("\(airportCode): \(airportName)")
}

for airportCode in airports.keys {
    println("Airport code: \(airportCode)")
}

for airportName in airports.values {
    println("Airport name: \(airportName)")
}
//使用字典创建数组
let airportCodes = Array(airports.keys)
let airportNames = Array(airports.values)

数组和字典的不可变性

  • 当为变量的时候,字典和数组想怎么玩都可以怎么玩
  • 当数组为常量时,不能试着改变任何不可变数组的大小,但是我们可以重新设定相对现存索引所对应的值
  • 当字典为常量时,意味着我们不能替换其中任何现有键所对应的值。不可变字典的内容在被首次设定之后不能更改。

元组 Tuple

元组取下标使用.操作符..数组和字典取下标使用[];

声明元组

  var http404Error = (404, "Not Found")  //默认下标0,1
  var http200Status = (statusCode: 200, description: "OK")//默认下标依然存在

访问元组

  println(http404Error.0);
  println(http200Status.0);
  println(http200Status.statusCode);

分解元组,如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记

  let (statusCode, statusMessage) = http404Error
  let (justTheStatusCode, _) = http404Error

元组很像php中的数组.但是swift是类型安全的.所以如果初始化下标和值后,然后再次赋不同的下标或者不同类型的值,则会报错.

  http200Status = ("ddd","dddd"); //错误
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。

可选类型 Optional

要么为nil,要么有值等于x.

  • nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
  • nil可以用来给字典移除一个键值对

声明可选类型

  var optional:Int?      //值为nil
  let possibleNumber = "123"
  let convertedNumber = possibleNumber.toInt()
  // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"

可选类型的变量不能直接使用,需要先判断其确实有值,然后使用!解析,注意!号是放在变量名后面,放在前面是代表操作符 否的意思

由于使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。所以最好使用if进行判断

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

可选绑定语句,可以省略解析!.可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。

if let actualNumber = possibleNumber.toInt() {     //
    println("\(possibleNumber) has an integer value of \(actualNumber)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

隐式解析可选类型

隐式解析可选类型是一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。

  let possibleString: String? = "An optional string."
  println(possibleString!) // 需要惊叹号来获取值

  let assumedString: String! = "An implicitly unwrapped optional string."
  println(assumedString)  // 不需要感叹号
  // 输出 "An implicitly unwrapped optional string."

除了不用使用!强制解析外,其他的和可选类型一样. 同样如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。

  if assumedString {
      println(assumedString)
  }
  // 输出 "An implicitly unwrapped optional string."
  if let definiteString = assumedString {
    println(definiteString)
  }
// 输出 "An implicitly unwrapped optional string."
2014-07-20 02:18:00 weixin_30826095 阅读数 1

swift 类型

变量声明

用let来声明常量,用var来声明变量
可以在一行中声明多个常量或者多个变量,用逗号隔开

    var x = 0.0, y = 0.0, z = 0.0

类型安全

Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。
一个变量是什么类型,通过两种方式来表达:

类型标注
类型标注表示这个变量可以存储某种类型的值,声明中的冒号代表着“是...类型”:
            var welcomeMessage: String
        
类型推断
如果你没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。

注意:当推断浮点数的类型时,Swift 总是会选择Double而不是Float。一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型

变量命名

  • 当你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型
  • 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。
  • 常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错

打印变量和常量

println是一个用来输出的全局函数,输出的内容会在最后换行。
xcode的playground使用println,点击工具条的show the Assistant editor.即能显示输出

  var str = "Hello, playground"
  println(str)

类型转换

SomeType(ofInitialValue)是调用 Swift 构造器并传入一个初始值的默认方法。
注意,你并不能传入任意类型的值,只能传入SomeType内部有对应构造器的值。
不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型)
不同整数类型的变量和常量可以存储不同范围的数字,如果数字超出了常量或者变量可存储的范围,编译的时候会报错:

  let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
//在语言内部,UInt16有一个构造器,可以接受一个UInt8类型的值,所以这个构造器可以用现有的UInt8来创建一个新的UInt16
let twoThousandAndOne = twoThousand + UInt16(one)

类型别名

类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias关键字来定义类型别名。

  typealias AudioSample = UInt16
  var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0

断言

可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。
如果条件判断为true,代码运行会继续进行;如果条件判断为false,代码运行停止,你的应用被终止。

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发,代码运行停止,应用被终止

可以做调试使用

整型 Int

Swift 提供了8,16,32和64位的有符号和无符号整数类型。
Swift 提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同:

  • 在32位平台上,Int和Int32长度相同。
  • 在64位平台上,Int和Int64长度相同。

Swift 也提供了一个特殊的无符号类型UInt,长度与当前平台的原生字长相同:

  • 在32位平台上,UInt和UInt32长度相同。
  • 在64位平台上,UInt和UInt64长度相同。
尽量不要使用UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。
除了这种情况,最好使用Int,即使你要存储的值已知是非负的。
统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断

浮点型 Double和Float

  • Double表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
  • Float表示32位浮点数。精度要求不高的话可以使用此类型。

布尔型 Bool

Swift 有两个布尔常量,true和false:

  let orangesAreOrange = true
let turnipsAreDelicious = false

字符窜 String 和 Character(字符)

Swift 的String类型表示特定序列的Character(字符)类型值的集合,不能用下标取

  for character in "Dog!🐶" {
    println(character)
}
let yenSign: Character = "¥"

字符窜连接通过+号将两个字符窜相连

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化 String 实例

emptyString == anotherEmptyString  //true

if emptyString.isEmpty {
    println("什么都没有")
}

let string1 = "hello"
let string2 = " there"
let character1: Character = "!"
let character2: Character = "?"

let stringPlusCharacter = string1 + character1        // 等于 "hello!"
let stringPlusString = string1 + string2              // 等于 "hello there"
let characterPlusString = character1 + string1        // 等于 "!hello"
let characterPlusCharacter = character1 + character2  // 等于 "!?"

字符窜插值

字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。

let multiplier = 3
let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)"

插值字符串中写在括号中的表达式不能包含非转义双引号 (") 和反斜杠 (\),并且不能包含回车或换行符。 插值可以省去额外的加号进行表达式的连接

数组 Array

数组按顺序存储相同类型的数据

声明数组

  var shoppingList: String[] = ["Eggs", "Milk"]
  var shoppingList = ["Eggs", "Milk"]
  var someInts = Int[]()
  var threeDoubles = Double[](count: 3, repeatedValue:0.0)// [0.0, 0.0, 0.0]
  var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)//类型推断[2.5, 2.5, 2.5]

添加数据,修改数据,读取数据,删除数据,

  //添加数据
  shoppingList.append("Flour")
  shoppingList += "Baking Powder"
  shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
  shoppingList.insert("Maple Syrup", atIndex: 0)

  //修改数据
  shoppingList[0] = "aaa"
  shoppingList[4...6] = ["Bananas", "Apples"]//区间操作符,不能越界想数组尾部添加数据

  //读取数据
  var firstItem = shoppingList[0]
  for item in shoppingList {
      println(item)
  }
  //全局enumerate函数来进行数组遍历。enumerate返回一个由每一个数据项索引值和数据值组成的元组
  for (index, value) in enumerate(shoppingList) {
    println("Item \(index + 1): \(value)")
  }

  //删除数据
  shoppingList.removeAtIndex(0)
  let apples = shoppingList.removeLast()
  someInts = [] //重置未空数组

字典 Dictionary

无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)

声明字典

  var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
  var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
  var namesOfIntegers = Dictionary<Int, String>()ßß

读取字典和修改字典数据,删除字典数据(使用nil)

airports.updateValue("Dublin Internation", forKey: "DUB")
airports["APL"] = "Apple Internation"
airports["APL"] = nil  // APL现在被移除了
airports.removeValueForKey("DUB")
namesOfIntegers = [:]//重置为空字典

字典遍历,Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。

  //注意这里没有使用enumerate
for (airportCode, airportName) in airports {
    println("\(airportCode): \(airportName)")
}

for airportCode in airports.keys {
    println("Airport code: \(airportCode)")
}

for airportName in airports.values {
    println("Airport name: \(airportName)")
}
//使用字典创建数组
let airportCodes = Array(airports.keys)
let airportNames = Array(airports.values)

数组和字典的不可变性

  • 当为变量的时候,字典和数组想怎么玩都可以怎么玩
  • 当数组为常量时,不能试着改变任何不可变数组的大小,但是我们可以重新设定相对现存索引所对应的值
  • 当字典为常量时,意味着我们不能替换其中任何现有键所对应的值。不可变字典的内容在被首次设定之后不能更改。

元组 Tuple

元组取下标使用.操作符..数组和字典取下标使用[];

声明元组

  var http404Error = (404, "Not Found")  //默认下标0,1
  var http200Status = (statusCode: 200, description: "OK")//默认下标依然存在

访问元组

  println(http404Error.0);
  println(http200Status.0);
  println(http200Status.statusCode);

分解元组,如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记

  let (statusCode, statusMessage) = http404Error
  let (justTheStatusCode, _) = http404Error

元组很像php中的数组.但是swift是类型安全的.所以如果初始化下标和值后,然后再次赋不同的下标或者不同类型的值,则会报错.

  http200Status = ("ddd","dddd"); //错误
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。

可选类型 Optional

要么为nil,要么有值等于x.

  • nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
  • nil可以用来给字典移除一个键值对

声明可选类型

  var optional:Int?      //值为nil
  let possibleNumber = "123"
  let convertedNumber = possibleNumber.toInt()
  // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"

可选类型的变量不能直接使用,需要先判断其确实有值,然后使用!解析,注意!号是放在变量名后面,放在前面是代表操作符 否的意思

由于使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。所以最好使用if进行判断

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

可选绑定语句,可以省略解析!.可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。

if let actualNumber = possibleNumber.toInt() {     //
    println("\(possibleNumber) has an integer value of \(actualNumber)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

隐式解析可选类型

隐式解析可选类型是一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。

  let possibleString: String? = "An optional string."
  println(possibleString!) // 需要惊叹号来获取值

  let assumedString: String! = "An implicitly unwrapped optional string."
  println(assumedString)  // 不需要感叹号
  // 输出 "An implicitly unwrapped optional string."

除了不用使用!强制解析外,其他的和可选类型一样. 同样如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。

  if assumedString {
      println(assumedString)
  }
  // 输出 "An implicitly unwrapped optional string."
  if let definiteString = assumedString {
    println(definiteString)
  }
// 输出 "An implicitly unwrapped optional string."

转载于:https://www.cnblogs.com/zhepama/p/3855753.html

2016-10-30 22:20:00 weixin_33778778 阅读数 12

在OC中我们判断类型是通过

- (BOOL)isKindOfClass:(Class)aClass;

方法进行判断类型的,而在Swift3中判断类型的方法为:

func isKind(of aClass: AnyClass) -> Bool

使用方法:判断subView是否为UIImageView类型

(subView as AnyObject).isKind(of:UIImageView.self)

转载于:https://www.jianshu.com/p/0a675c5214bd

2017-11-09 16:55:00 weixin_33975951 阅读数 0

我们使用Swift这个苹果新推出的编程语言已经有一段时间了。其中的一个极大的优点就是苹果称为“optional types”的东西。几乎所有的objective-c程序员都知道用nil来表示某个引用类型的对象是没有值的。但是要把nil和某个变量的类型联系起来还是有些牵强。

这里,我们就来介绍一下Swift提供的optional type(可选类型)。先介绍一些实现的细节,然后指出optional type体系里的几个要点。

类型?

在我们开始进入代码前,先来看看为什么一个类型被定义为可选的。我们遇到的类型一般都是通常的,非可选的类型。包括一般的值类型,比如,Int,Bool和String。还有复杂点的引用类型,比如,UIView,等。

我们声明这些类型的变量的时候,Swift要求必须给这些变量赋值。这一要求非常的严格,如果你想在初始化一个变量之前使用它使编译不过的。 

 

这个表面看起来很郁闷,但其实很有帮助。长远来说,不让这样的代码编译通过,Swift可以避免发生因为使用了未初始化的值而引发的潜在的运行时错误。

let x: Int = nil

然而,有人会尝试给let声明的常量赋值为nil。 

 

这样的代码会直接引发一个错误:类型“Int”不是protocol ’NilLiteralConvertible’类型。对于非optional type的类型,不实现“NilLiteralConvertible”protocol是不可以使用nil来初始化的。所以,简单的x,只是一个Int型的实例,只能被赋值为Int的值而不是nil。

程序里的变量不可能全部都有初值,所以这个时候optional type就该出场了。Swift里,只要在任意类型的后面加一个问号以后就变成了optional type(可控类型)。比如,之前的例子中。只要给Int后面加一个问号就可以将x赋值为nil了。 

这应该是很多写Objective-C的哥们需要的了。变量值可以是“实际”的值,也可以是nil。这只取决于你的代码处理的是哪种情况了。

装箱

你可能会说,“Int只是值类型,不是一个对象怎么能使用nil呢?NSInteger是不能这么用得。。”

的确,你说的没错。NSInteger没有nil值。或者更准确的说经过类型转化后你会得到一个整型值0。所以,在Objective-C得API里定义了很多的标记表明“无值”状态:0,1,NSIntergerMin,NSIntegerMax以及NSNotFound等,都是表明“nothing”的。 

当你仔细考虑就会发现没有一个一致的方式表明“nothing”这个值,而是用不同的自定义值标记“nothing”会增加一定的复杂度。取一个数组中不存在的值返回的事NSNotFound,取一个table view中不存在的row的时候返回的事一个-1。

Swift的optional type提供了一种更加清晰的表示方法。但是如何让任何类型都具有optional type这个功能呢?这些都建立在泛型的基础之上: 

 

上面的就是Swift核心库对于optional type的定义(稍微作了修改)。Swift定义了一个新的类型Optional,它由两个值,一个是“nothing”,叫做None,一个是把某个类型T的值给包装起来之后的值。Swift就是利用这一机制把某了类型的值包装起来,当然这个值可以是空(nothing),也可以不是空。

在这个例子中, 第一个整数就只是一个Int型的值。后面的类型后面跟了问号“?”的其实都是Optional<T>类型的。或者,简短的可以表示为Int?。

有了这个能力,Swift就可以给任何类型表示“nothing”的值nil,即使是Int。Optional type同时可以表示“real”的值,也可以表示“nothing”的值,而不需要其他的特殊的值。

拆箱

这样表示optional value同时也会引发一个问题。现在我们知道optional type是一个独立的类型:Optional<T>。所以,在需要T的地方,不如某个函数需要T类型的参数传入,那么optional<T>的值是不能用的: 

 

我们需要把需要的值从optional的箱子里拿出来。并且,很重要的一点,在那之前需要检查这个值是否存在。Swift提供了叹号“!”操作符来提取值。 

 

记得把x的值修改为100,而不是之前的nil。因为,叹号操作符只适用于optional type的值本身有“real”值的。如果没有的话是会抛出运行时异常的。

所以,在拆箱取值以前,我们需要先判断这个可选(optional)的值是否为空。就和我们在Objective-C中常做的类似。 

 

但是如果这个x是从其他的方法返回回来的呢?我们可以直接调用这个函数来检验返回值, 完全不必要先给局部变量赋值,再检测是否为空。

Swift已经实现了这个功能,叫做optional binding(可选绑定)。使用if和let两个关键字就可以写出一行紧凑的代码来检测函数返回值是否存在。

 

这里我们已经不用叹号操作符来显示的强制拆箱。这是optional binding(可选绑定)的另一个好用的地方。直接在if语句的判定表达式里拆箱optional type(可选类型),就可以确定这个optional type是否有值,不用手动的使用叹号操作符来拆箱。

Chaining

现在我们建立一个准确的检测和拆箱optional value(可选值)的规则。比如,如何在optional value上调用方法?你肯定会在一个可能为空的对象上调用方法,这是一定会发生的。在Objective-C中,在nil对象上调用方法会返回一个nil。

幸好Swift也可以做到这样。使用optional chaining(可选链)的方式来调用可能为空(nil)的方法: 

 

在对象和其调用的方法之间插入一个问号“?”操作符,我们就可以表明是要一个实际存在的值还是要一个“nothing”。这和Objective-C的调用非常类似。

注意:这样调用的方法的返回值一定都是optional type(可选类型)的,即使这个方法的返回值被定义为非可选类型(non-optional type)。所以,在optional value(可选值)上调用的方法链上得任意一点的返回值都是optional的。在处理返回值的时候一定要考虑到值可能为空的可能。

考虑下面的代码: 

  

someMethod()方法的声明中制定返回值为Int型,z还是得到一个optional value(可选值)。因为,我们使用了optional chaining(可选链)来调用方法。这可能看起来有点迷惑,但是很有帮助。尤其是在optional binding(可选绑定)的时候。比如,上面代码中的if let z = y?.someMethod()表达式。

这样可以很简洁的处理一下问题:

  • 如果y是nil(这里已经是nil),optional chaining(可选链)可以保证我们这样写代码而不报错
  • 如果y是nil或者someMethod()方法返回nil,optional binding(可选绑定)不会把nil赋值给non-optional value(非可选值)z。
  • 最终我们会得到z,但是不用手动拆箱。因为这是可选绑定的(optional binding)。

总之,对于处理nil值来说,Swift提供了一个非常清晰的系统。我们或得了额外的类型安全,避免了不必要的特殊定义的值,而且还是像Objective-C一样简洁。

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!

















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/4339617.html,如需转载请自行联系原作者

Swift 判断类型

阅读数 5

Swift 类型嵌套

阅读数 5

iOS设备类型(Swift)

阅读数 681

Swift 关联类型

阅读数 5

Swift的Optional类型

阅读数 774

没有更多推荐了,返回首页