文章目录
  1. 1. 类型推断问题
  2. 2. Optional 相关的几种定义方式
  3. 3. 单例模式

本周整理了 3 个问题,分别是类型推断问题,Optional 相关的几种定义方式和单例模式。

类型推断问题

虽然 Swift 是强类型的语言,但是也引入了弱类型的一个特性点,那就是编译时,去推断出变量或者常量的类型。比如:

1
let i = 12 //Int

常量 i 在编译期会被认为是 Int 类型。

但是在一些复杂的语句中,编译器会推断不出来具体的类型。在社区里面,讨论此问题的帖子很多。本周我就看到 2 次:

让我们看一下第一个例子,第二例子请自行分析:

  • 问题描述:
    看看以下这段代码,会报错:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protocol Named {
var name: String { get }
}
struct Person: Named {
let name: String
var age: Int
}
func printNames(objects: [Named]) {
for object in objects {
print(object.name)
}
}
let person1 = Person(name: "Alice", age: 30)
let person2 = Person(name: "Bob", age: 40)
let persons = [person1, person2]
printNames([person1]) // Works
printNames([person1, person2]) // Works
printNames(persons) // Error: Cannot convert value of type '[Person]' to expected argument type '[Named]'
persons as? [Named] // Error: 'Named' is not a subtype of 'Person'

直接传入匿名数组[person1, person2]会被编译器推断出是[Named]类型。而定义的常量persons传入到printNames时候,会报错。编译器推断不了[Person]是可以当成[Named]传入到函数当中的。提问者已经向苹果官方提交了 bug issue。

  • 问题解答:
    那么遇到这种情况,应该如何能编译通过呢?答案也很简单。就是显式指定常量类型:
1
let persons: [Named] = [person1, person2]

我想苹果也不会轻易去掉类型推断这个好用的特性点。使用强类型语言显式指定类型的方式。类型推断还是很大的提高了代码编写的灵活性。遇到类型编译错误的情况,多分析一下上下文。看看是不是应该去显式定义一下类型。

Optional 相关的几种定义方式

Optional 是 Swift 引入的很重要的一个语法点,社区讨论的比较多,涉及到了对 Optional 的方方面面的一些问题,本周就有一个比较的基础问答点,我觉得有必要拿出来看看:

  • 问题描述:

在这个帖子(Swift: difference in the way to unwrapp an option variable)中, 提问者问以下2个版本代码的区别:

1
2
3
4
5
6
7
8
9
10
11
//version 1 :
let myVariable : String? = "my name"
let myNameVariable : String = myVariable!
//version 2:
let myVariable : String? = "my name"
let myNameVariable : String! = myVariable

主要焦点集中在这些定义的常量是神马类型,这几种类型具体如何使用。

  • 问题解答:

让我们对跟帖中的代码进行分析,得出一些使用的情况:

第一段代码:

1
2
3
4
5
6
7
8
9
let myVariable : String? = "my name"
var myNameVariable = myVariable!
//myNameVariable = nil //Error
var implUnwrapVar:String! = myVariable
implUnwrapVar = nil//OK

逐句解释如下:

  • 定义一个String?的常量,赋值为 my name
  • 定义一个变量myNameVariable, 通过 myVariable的强制拆包,得到my name,赋值给myNameVariablemyNameVariable类型推断为String
  • 注释掉的代码,如果去掉注释编译会报错。因为myNameVariableString类型,所以不能使用nil对其赋值。
  • 定义了一个隐式解包Optional的值,隐式解包Optional,赋值可以是nil,所以下一句代码,对其赋值就没错。但是如果是一个类的话,这种赋值为nil的情况下,调用类中的方法,就会崩溃。关于隐式解包Optional的介绍,大家可以看看喵神的 tips 书,里面有一个tips 提到了,但是他的网站上没有对此的介绍。。于是我就贴不了链接。

第二段代码:

1
2
3
4
5
let myVariable : String? = nil
//var myNameVariable = myVariable! //error
var implUnwrapVar:String! = myVariable // ok

通过第一段代码的解释,第二段代码其实就不用过多介绍,第二句代码编译报错的原因是对myVariable强制拆包时,发现拆包结果为nil而报错。

单例模式

Swift 中的单例模式,一直是入门级别的一个问题。经常还是有人在社区里面提问,比如本周就有一个帖子:

Stack Overflow把这类问题都归结为重复的问题,指向了另外一个帖子:

那里我们究竟应该如何在 Swift 中实现单例模式呢?

总结下来,有以下三种方式实现,在上面的帖子中有回答:

  • 1、通过结构体实现
    Swift 1.2之前,类不支持 static 定义存储属性,所以类中定义单例需要通过内含一个结构体来实现:
1
2
3
4
5
6
7
8
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static let instance: Singleton = Singleton()
}
return Static.instance
}
}
  • 2、最简洁形式
1
2
3
class Singleton {
static let sharedInstance = Singleton()
}

Swift 1.2 以后,可以定义成以上形式.

  • 3、dispatch形式
    这种形式,是把 Objective-C 中的方式改成了 swift 代码,也可以使用,但是没有之前 2 种形式好理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: Singleton? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = Singleton()
}
return Static.instance!
}
}

大家使用第二种方式就好了。也满足多线程的要求。

大家有兴趣也可以看看喵神对单例的tips: 单例

以上问题对应的代码都在这里:下载代码

文章目录
  1. 1. 类型推断问题
  2. 2. Optional 相关的几种定义方式
  3. 3. 单例模式