一、概要说明
1.1 Swift介绍
Swift语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言。Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题。Swift语言采用安全编程模式,且引入了多种新功能,使得编程工作更加简便,灵活!
2015年12月4日苹果公司宣布Siwft开源。
1.2 在线文档
- The Swift Programming Language苹果官网在线阅读
- TheSwiftProgrammingLanguage 2.0中文手册
二、分享总结
2.1 命名空间概念
swift加入了这个概念,默认是工程名。同一个命名空间下,所有资源共享。
:viewWillAppear
:viewWillAppear
:viewWillAppear
OC中没有这个概念。
2.2 可选类型 ?&!(Optional)
- Optional事实上是一个枚举类型,Optional包含None和Some两种类型,而nil就是Optional.None,非nil就是Optional.Some。如果Optional变量在声明时不初始化,Swift会调用init()来初始化变量为nil,而用非nil的值初始化变量时,会通过Some(T)把该原始值包装,所以在之后使用的时候我们需要通过解包取出原始值才能使用。
enum Optional : LogicValue, Reflectable {
case None
case Some
init()
init(_some: T)
/// Allow use in a Boolean context.
func getLogicValue() -> Bool
/// Haskell's fmap, which was mis-named
func map(f: (T) -> U) -> Bool
func getMirror() -> Mirror
}
// 在声明一个变量时,如果不手动初始化,Swift不会自动初始化该变量为一个默认值的。
var a: String
var b = a // error :因为没有初始化a,a没有值
var a:String?
var b = a // 此时 b 和 a 都为nil
print(a!) // 取值需要在变量后加!,格式Optional(value)
var a:String!
print(a) // error:
- 在对变量操作时
// 这时问号的意思类似于isResponseToSelector,即如果变量是nil,则不能响应后面的方法,所以会直接返回nil。
如果变量非nil,就会拆Some(T)的包,取出原始值执行后面的操作。
var arrayCount = dataList?.count
// 这里表示我确定dataList一定是非nil的,所以直接拆包取出原始值进行处理。
因此此处如果不小心让dataList为nil,程序就会crash掉。
var arrayCount = dataList!.count
2.3 闭包
- 闭包和OC中的Block非常相似,OC中的block类似于匿名函数,闭包是用来定义函数,他们的作用都是 保存一段代码,在需要的时候执行。
// oc 中的block
void (^myblock)(int a,int b);
myblock = ^(int a,int b){
// ...
}
// swift 闭包
{
(参数) -> 返回值类型
in
执行语句
}
{
(parameters) -> returnType
in
statements
}
- 闭包简化 - 尾随闭包
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
someFunctionThatTakesAClosure{ () -> Void in
// 闭包主体部分
}
1.如果没有参数, 没有返回值, in和in之前的东西可以省略
2.如果闭包是函数的最后一个参数, 可以写在()后面 -- 尾随闭包
3.如果只有一个闭包参数, 那么()也可以省略 -- 尾随闭包
- 闭包的循环引用
import UIKit
class ViewController: UIViewController {
var finished: (()->())?
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
loadData { () -> () in
print("更新UI")
// self.view.backgroundColor = UIColor.redColor()
weakSelf!.view.backgroundColor = UIColor.redColor()
}
}
func loadData(finished: ()->()){
print("耗时操作")
// 保存闭包
self.finished = finished
finished()
}
// 类似于dealloc方法
deinit {
print("我滚了")
}
}
2.4 函数特性
- 函数参数与返回值
- 函数参数名称,默认参数值,可变参数,输入输出参数
- swift函数可以返回一个元组里多个值。
- 函数重写,重载
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
// 默认参数值函数
func someFunction(parameterWithDefault: Int = 12) {
// function body goes here
// if no arguments are passed to the function call,
// value of parameterWithDefault is 42
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12
// 注意一般有默认值参数应放在最后。
// 可变参数函数(可以接受零个或多个值)
func arithmeticMean(numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
//函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
// 输入输出参数(关键字:inout)
func swapTwoInts(inout a: Int, inout _ b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
// 返回元组
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.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
- 函数类型
func addTwoInts(a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, _ b: Int) -> Int {
return a * b
}
这两个函数的类型是 (Int, Int) -> Int
var function:(Int,Int)->Int?
// 既然是类型,那可以声明定义,那必然也是可以当作参数来传递的。
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// 嵌套函数
// 默认情况下,嵌套函数是对外界不可见的,但是可以被他们封闭函数来调用。一个封闭函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
2.5 构造器
构造过程是通过定义构造器(Initializers)来实现的,这些构造器可以看做是用来创建特定类型实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
Swift有着超级严格的初始化方法,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。(swift安全语言)
1.指定构造器(类的主要构造器, 要在指定构造器中初始化所有的属性, 并且要在调用父类合适的指定构造器.当定义一个指定构造器的时候, 必须调用父类的某一个指定构造器)
init()
init(frame: CGRect)
2.便利构造器(convenience 修饰,不能被子类重写或从子类中以super的方式被调用的。)
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
let anObj = ClassB(bigNum: true)
// anObj.numA = 10000, anObj.numB = 10001
只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就也可以使用父类的convenience初始化方法了。比如在上面的代码中,我们在ClassB里实现了init(num: Int)的重写。这样,即使在ClassB中没有bigNum版本的convenience init(bigNum: Bool),我们仍然还是可以用这个方法来完成子类初始化
// 开始写构造器的时候
init(){
}
// 点击错误它会让你实现这个方法。
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
因为重载了指定构造器, 所以来自父类的指定构造器并不会被继承.如果子类没有定义任何的指定构造器, 那么会默认继承所有来自父类的指定构造器.而 init(coder aDecoder: NSCoder) 方法是来自父类的指定构造器, 因为这个构造器是 required , 必须要实现. 但是因为我们已经重载了 init() , 定义了一个指定构造器, 所以这个方法不会被继承, 要手动覆写.
Swift 中构造器需要遵循的规则还是很多的, 总结一下, 有以下规则:
1.调用相关
指定构造器必须调用它直接父类的指定构造器方法.
便利构造器必须调用同一个类中定义的其它初始化方法.
便利构造器在最后必须调用一个指定构造器.
2.属性相关
指定构造器必须要确保所有被类中提到的属性在代理向上调用父类的指定构造器前被初始化, 之后才能将其它构造任务代理给父类中的构造器.
指定构造器必须先向上代理调用父类中的构造器, 然后才能为任意属性赋值.
便利构造器必须先代理调用同一个类中的其他构造器, 然后再为属性赋值.
构造器在第一阶段构造完成之前, 不能调用任何实例方法, 不能读取任何实例属性的值, self 不能被引用.
3.继承相关
如果子类没有定义任何的指定构造器, 那么会默认继承所有来自父类的指定构造器.
如果子类提供了所有父类指定构造器的实现, 不管是通过上一条规则继承过来的, 还是通过自定义实现的, 它将自动继承所有父类的便利构造器.
2.6 懒加载
*先来说一下懒加载的好处:
1. 需要的时候初始化内存,对内存开销较小,节省内部资源
2. 代码初始化放在一起,代码块比较好划分,方便别人和自己阅读
import UIKit
class ViewController: UIViewController {
// Swift中的懒加载会在第一个访问的时候执行
// 懒加载其实就是一个提前准备好的的闭包
lazy var dataList: [String] = {
print("我懒加载了")
return ["zj", "hq", "wl"]
}()
let demoFunc = {
() -> [String]
in
print("我懒加载了")
return ["ny", "ny1", "ny2"]
}
// 注意懒加载一定要用var
lazy var dataList2: [String] = self.demoFunc()
override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
print(dataList)
print(dataList)
print(demoFunc())
print(dataList2)
print(dataList2)
}
}
打印结果:
我懒加载了
["zj", "hq", "wl"]
["zj", "hq", "wl"]
我懒加载了
["ny", "ny1", "ny2"]
我懒加载了
["ny", "ny1", "ny2"]
["ny", "ny1", "ny2"]
- 格式:
lazy var 变量: 类型 = { 创建变量代码 }()
oc中的懒加载
2.7 其他特性
2.7.1 getter & setter
class Person: NSObject {
var name: String?
var age: Int?
}
var _name: String?
var name: String? {
get {
return _name
}
set {
_name = newValue
}
}
在
Swift
中以上形式的 getter & setter 很少用在 OC 中,我们通常希望在给某一个变量赋值之后,去做一些额外的操作
var length: Int? {
didSet {
timeStr = String(format: "%02d:%02d:%02d", arguments: [length! / 3600, (length! % 3600) / 60, length! % 60])
}
}
var timeStr: String?
计算型属性
var title: String {
get {
return "Mr " + (name ?? "")
}
}
- 只实现
getter
方法的属性被称为计算型属性,等同于 OC 中的ReadOnly
属性 - 计算型属性本身不占用内存空间
- 不可以给计算型属性设置数值
- 计算型属性可以使用以下代码简写
var title: String {
return "Mr " + (name ?? "")
}
2.7.2 Guard语句
与if语句相同的是,guard也是基于一个表达式的布尔值去判断一段代码是否该被执行。与if语句不同的是,guard只有在条件不满足的时候才会执行这段代码。你可以把guard近似的看做是Assert,但是你可以优雅的退出而非崩溃。
用一个简单的对比来比较一下现在的写法和用全新guard语句的写法:
func fooManualCheck(x: Int?) {
if x == nil || x <= 0 {
// 不符合值的要求时,写点代码
return
}
// 使用x
x!.description
}
// 通过可选绑定让问题变得简单了一些
func fooBinding(x: Int?) {
if let x = x where x > 0 {
// 使用x
x.description
}
}
func fooGuard(x: Int?) {
guard let x = x where x > 0 else {
// 变量不符合条件判断时,执行下面代码
return
}
// 使用x
x.description
}
使用guard语句将上述的3个问题一并解决:
1.是对你所期望的条件做检查,而非不符合你期望的。又是和assert很相似。如果条件不符合,guard的else语句就运行,从而退出这个函数。
2.如果通过了条件判断,可选类型的变量在guard语句被调用的范围内会被自动的拆包 - 这个例子中该范围是fooGuard函数内部。这是一个很重要,却有点奇怪的特性,但让guard语句十分实用。
3.对你所不期望的情况早做检查,使得你写的函数更易读,更易维护。
2.7.3 Swift中的 #available(检查API的可用性)
// 在OC中,查看API是否可用
if ([UIDevice currentDevice].systemVersion.intValue >= 8) {
// 调用8.0以后才支持的API
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
// 调用7.0以后才支持的API
#endif
if (self respondsToSelector:@selector(testAPI)) {
// 调用
[self testAPI];
}
// 在swift中,
// respondsToSelector这个还可以用。
if tableView.respondsToSelector(Selector("reloadData")) {
print("support reloadData")
}
// #available 关键字
// 如果是iOS8.0及其以上版本,就会执行第一个分支,否则执行else分支
let interfaceOrientation : UIInterfaceOrientation
if #available(iOS 8.0, *) {
interfaceOrientation = UIInterfaceOrientation.Portrait
} else {
interfaceOrientation = rootController.interfaceOrientation
}
语法规则:#available(iOS v, OSX v1, ),其中参数不要求全部使用,可以直接使用#available(iOS v, )或者#availabel(OSX, *)
要为Swift的api设置系统版本要求,我们可以通过以下方式:
@available(iOS 9.0, *)
func checkAvailable() {
// ...
}