Error[8]: Undefined offset: 1188, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f) [+++] {[+++]}[+++]

和case合起来使用:

guard [+++] [+++] .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1189, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {[+++]}[+++]

和case合起来使用:

guard [+++] [+++] .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1190, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}[+++]

和case合起来使用:

guard [+++] [+++] .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1191, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard [+++] [+++] .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1192, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard  [+++] .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1193, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err [+++] {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1194, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {[+++]}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1195, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {}[+++]

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1196, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {}

和表达式一起使用:

guard howMany() > [+++] [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1197, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {}

和表达式一起使用:

guard howMany() >  [+++] {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1198, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {}

和表达式一起使用:

guard howMany() >   {[+++]}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Swift4 学习笔记——基础篇_app_内存溢出

Swift4 学习笔记——基础篇

Swift4 学习笔记——基础篇,第1张

概述示例代码来源于 《iOS 11 Programming Fundamentals with Swift》 概览 语句分隔符 Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的: print("hello")print("world")

示例代码来源于 《iOS 11 Programming Fundamentals with Swift》

概览 语句分隔符

Swift的语句是可以通过分析断句的,如果一个语句结束后换行就开始下一个语句,如果写了分号也表示结束这个语句,但是有了换行就不需要分号了,写上也没有问题。如果一个语句没有结束,换行没有实际效果。下边的代码都是合法的:

print("hello")print("world")print("hello"); print("world")print("hello");print("world");print(    "world")
注释

依旧是://和//,其中/* … */可以嵌套

对象的类型

Swift中一切都是对象。按照类型划分,有6种:

struct,Bool,Int,Double,String,Array,Dictionary等 enum,Optional类型

class,用户自定义类型。这篇文章提到了Swift中有3个预定义的class类型,但是没有指出是哪个。

protocol,ExpressibleByIntegerliteral

tuple,函数返回多值时使用。 function,print,自定义函数。

举个例子:

字面变量是对象:

let s = 1.description

#### 值类型和引用类型

按照内存管理来划分,Swift对象有值类型和引用类型,值类型在赋值的时候是copy的(不考虑Swift优化),引用类型是共享内存的。

官方文档对值类型和引用类型的解释:

Types in Swift fall into one of two categorIEs: first,“value types”,where each instance keeps a unique copy of its data,usually defined as a struct,enum,or tuple. The second,“reference types”,where instances share a single copy of the data,and the type is usually defined as a class.

在Swift中,class和function是引用类型,struct,enum,tuple都是值类型。protocol本身不允许有实例,但是采用protocol的可以是struct, enum或者class。

数据类型 变量与常量
let one = 1var two = 2

使用let声明的是常量,使用var声明的是变量

类型推断

从上面例子可以看出,如果在声明变量的时候就赋值,有时候是可以不写类型的,让编译器推断。上文中one和two都是Int类型。

那么什么时候需要些类型呢?

只声明,不初始化。
var x : Int
想要的类型和推断的类型不符合
let separator : CGfloat = 2.0
不能推断出类型
let opts : UIVIEwAnimationoptions = [.autoreverse,.repeat]
还有一种情况是提醒自己这个变量是啥类型
let duration : CMTime = track.timeRange.duration
基本类型用法 Bool Bool是一个struct类型 只有true和false两个值,不能做它解释。 Int Int是struct类型 Int的取值在Int.max和Int.min之间,平台相关 Double Double是struct类型 64位架构处理器上,Double的精度是15位 Double的边界是Double.infinity,还有Double.pi等 使用isZero来判断Double是否为0 数字类型转换

只有字面变量可以被隐式转换!

let d : Double = 10

将字面变量10转换成了Double类型,但是变量就不可以,下列的代码不能通过编译:

let i = 10let d : Double = i // compile error

正确的写法是:

let i = 10let d : Double = Double(i)
String
let str = "Hello World" //欧耶,终于不用写@了

多行字面变量的写法:

func f() {        let s = """ line 1 line 2 line 3 """        // ...}func f() {        let s = """ line "1" line 2 \ and this is still line 2 """        // ...}

在String字面变量中使用(…)来计算表达式

let n = 5let s = "You have \(n) Widgets."

String支持+号和+=号

let s = "hello"let s2 = " world"let greeting = s + s2

String的utf8编码:

let s = "\u{BF}Qui\u{E9}n?"for i in s.utf8 {       print(i) // 194,191,81,117,105,195,169,110,63}

String和数值的转化:

let i = 7let s = String(i) // "7"let i = 31let s = String(i,radix:16) // "1f"
Range

Range是一个struct。 字面变量: a…b表示区间[a,b] a..< b表示区间[a,b)

最常见的就是在for循环中使用:

for ix in 1...3 {        print(ix) // 1,then 2,then 3}

Range 有实例方法:

let ix = // ... an Int ...if (1...3).contains(ix) { // ...let s = "hello"let ix2 = s.index(before: s.endindex)let s2 = s[..<ix2] // "hell"
Tuple

tuple是一个有序的轻量级的collection。

tuple的声明:

var pair : (Int,String)

初始化:

var pair : (Int,String) = (1,"Two")var pair = (1,"Two")

tuple可以同时给多个变量赋值:

let ix: Intlet s: String(ix,s) = (1,"Two")

tuple在for-in中的应用:

let s = "hello"for (ix,c) in s.enumerated() { print("character \(ix) is \(c)") }

对Tuple中值的引用:

let pair = (1,"Two")let ix = pair.0 // Now ix is 1

如果在声明的时候给值一个label,可以通过label引用:

let pair : (first:Int,second:String) = (1,"Two")//or: let pair = (first:1,second:"Two")var pair = (first:1,second:"Two")let x = pair.first // 1pair.first = 2let y = pair.0 // 2

还可以给Tuple起一个别名

typealias Point = (x:Int,y:Int)func pIEce(at p:Point) -> PIEce? {        let (i,j) = p        // ... error-checking goes here ...        return self.grID[i][j]}
可选类型

Swift中变量如果不初始化是不能使用的。这点和OC不同,OC中值类型会有一个默认值,引用类型默认为nil。Swift中如何表示nil呢?答案就是Optional(可选类型)

Optional类型的底层是enum类型,可以包装一个其他类型,具体内部实现这里不讨论。
比如:

var stringMaybe = Optional("howdy")

就定义了一个包装了String的Optional类型。包装不同类型的Opational也是不同的类型,不能互相赋值。Optional(String)类型可以简写为String?

如果没有给Optional的变量装箱一个值,那么它就是空的,空的Optional变量可以和nil比较:

var stringMaybe : String? = "Howdy"print(stringMaybe) // Optional("Howdy")if stringMaybe == nil {        print("it is empty") // does not print}stringMaybe = nilprint(stringMaybe) // nilif stringMaybe == nil {        print("it is empty") // prints}

在Swift中nil是一个关键字,不是一个值,可以将nil赋值给Optional的类型。

自动装箱,将一个值直接值给包装它的Optional类型。

var stringMaybe: String? = "farewell

根据自动装箱机制,可以在任何需要Optional类型的地方传入原始类型,但是反过来不行。

let stringMaybe : String? = "howdy"let upper = stringMaybe.uppercased() // compile error

不能给Optional类型直接发送消息,需要拆箱得到原始数据。

拆箱

let stringMaybe : String? = "howdy"let upper = stringMaybe!.uppercased()

在变量后边加上叹号,就拆箱得到原始类型。

自动拆箱,在定义变量的时候使用!而不是?就定义了一个自动拆箱的Opational变量,在需要使用原始类型的地方,直接传入自动解包的Opational变量即可。

func realStringExpecter(_ s:String) {}var stringMaybe : String! = "howdy"realStringExpecter(stringMaybe) // no problem

注意,如果自动解包的Optional是nil,会引起Crash。不能给一个是nil的Optional类型解压,这是Swift最重要的规则之一。 所以,如果不是必须,最好不要使用这个特性,因为这样就失去了Swift中可选类型的安全特性。

!定义的Optional和?定义的Optional是同一个类型,比如self.vIEw是一个UIVIEw!,但是如下代码却产生编译错误。

var stringMaybe : String! = "howdy"var anotherStr = stringMaybe //okvar pureStr: String = stringMaybe //okvar errStr: String = anotherStr // compile error

stringMaybe是自动拆箱的String?,所以赋值给String类型是可以的;但是anotherStr却没有自动拆箱的标志,仅仅是一个String?,所以不能赋值给String类型。

Optianal Chain是Swift中很重要的一个概念。

拆箱nil会引起Crash,那么如果每次拆箱都得判断是否为nil,代码就会很难看。于是Swift提供了语法糖:

var stringMaybe : String?// ... stringMaybe might be assigned a real value here ...let upper = stringMaybe?.uppercased()

在拆箱的时候,不用!而是用?,这叫做选择性拆箱。英文很有意思:unwarp the Optional optionally。

选择性拆箱实际上替你做了判断工作,就是如果stringMaybe是nil,那么什么也不做,如果不是nil,拆箱得到String,然后发送uppercased消息。

这很好,但是如果“什么也不做”返回值upper是啥?答案是nil。那么nil是不能赋值给String类型的,于是又引入一个规则:

如果一个Optional Chain上有一个可能的Optional的类型(选择性拆包才有),那么返回值就是Optional的。

也就是说虽然uppercased方法返回的是String类型,但是因为它在一个Optional Chain中,所以返回值自动被装箱,成为String?类型。一个Optional Chain返回一个Optional的类型也合情合理。

因为自动装箱,给一个Opational Chain赋值会比较简单。

// self is a UIVIEwControllerself.navigationController?.hIDesbarsOnTap = true

同样,如果navigationController是nil,什么也不会发。那么如何知道赋值成功了呢?

let ok : VoID? = self.navigationController?.hIDesbarsOnTap = true

如果ok不是nil,就是赋值成功。

Optional类型是可以和原始类型直接比较的。下边的代码没有问题。

let s : String? = "Howdy"if s == "Howdy" { // ... they _are_ equal!

如果s是nil,返回false,如果s不是nil,拆箱之后再和”Howdy”比较。

但是不能比较不等关系,下边的代码是不能通过编译的:

let i : Int? = 2if i < 3 { // compile error

因为Swift不能确定如果是nil,结果是什么。

函数

函数的定义

func sum (_ x:Int,_ y:Int) -> Int {    let result = x + y    return result}
func 是keyword,sum是函数名。 括号内部是参数,参数标签,变量名,冒号后是类型。和OC结构一样。 -> Int表示返回值是Int类型。如果函数返回VoID,可以写成->() 函数体在大括号内部。 参数前边的”_”符号表示忽略参数的标签。

参数标签

func echoString(_ s:String,times:Int) -> String {    var result = ""    for _ in 1...times { result += s } return result}

times就是参数的外部名字(external name),也可以叫参数标签。这是和OC语言的参数名字和变量名字分开是一致的。

调用的代码应该是这样:

let s = echoString("hi",times:3)
默认的,参数的变量名(internal name)就是参数标签(external name)。 如果使用了_,表示没有标签,调用方也不能使用标签调用了。 具有相同的函数签名,但是参数标签不同的函数,是两个不同的函数。

函数参数默认是不可变的。意思是,不能在函数中给函数参数再次赋值。对于引用类型,是可以改变内部属性的。

func say(_ s:String,times:Int,loudly:Bool) {    loudly = true // compile error}

如果想要重新给参数赋值需要满足以下几个条件:
- 给函数参数添加intout关键字
- 传入的变量应该是var而不是let的
- 传入变量的地址。

func removeCharacter(_ c:Character,from s: inout String) -> Int {    var howMany = 0    while let ix = s.index(of:c) {        s.remove(at:ix)        howMany += 1    }    return howMany}

调用

var s = "hello"let result = removeCharacter("l",from:&s)

Swift中函数是first-class object,意思是函数可以赋值给变量,可以作为函数的参数和返回值。

func doThis(_ f:() -> ()) { f() } func whatTodo() { print("I dID it") } doThis(whatTodo) 

函数是first-class object这一特点可以衍生出很多编程模式,装饰器,偏函数,函数工厂等等。

class,struct & enum 概览

enum,struct在Swift中和class很像,都可以定义方法,初始化函数等,但是有两个重大的区别:
- enum,struct是值类型,class是引用类型
- enum,struct不能继承

在这3种类型中,可以有的结构是:
- 初始化函数。
- 属性,分为成员属性和类属性。对于struct和enum用static关键字,对于class用class关键字。
- 方法,成员方法和类方法。
- 下标(subscripts)
- 嵌套定义(值类型的不能嵌套自己的类型)。

在Swift中没有一个像NSObject那样的公共基类。

class

初始化方法

由于Swift中不允许使用未经初始化的变量,并且想在编译阶段强制的保证这一点。于是对于class类型的初始化,引入了很多规则。虽然规则条数很多,但都是围绕这一个原则:从初始化函数中返回的对象的所有属性也是初始化的,并且在初始化完成之前不能使用这个对象。

初始化函数必须初始化所有未初始化的属性
class Dog {    let name : String    let license : Int    init(name:String = "",license:Int = 0) {        self.name = name        self.license = license    }}

如果删除self.license = license,将会产生编译错误,因为license没有初始化。

在初始化所有属性之前,不能使用self
class Cat {        var name : String        var license : Int        init(name:String,license:Int) {                self.name = name                meow() // too soon - compile error         self.license = license        }        func meow() {                print("meow")        }}

meow()实际上隐式的使用了self,即self.meow()。应该将meow()的调用放到最后。

如果初始化函数之间发生调用关系,初始化函数就分成了两类:designated initializer 和convenIEnce initializer。

designated initializer就是能独立完成对象的初始化的初始化函数,而convenIEnce initializer必须直接或者间接的调用designated initializer来完成初始化工作。

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.init(name: name,age: 10)    }    init(name: String,age: Int){        self.name = name        self.age = age    }}

在class中designated initializer不需要特别指明,但是convenIEnce initializer必须使用convenIEnce关键字。(这一条只是对class来讲,如果把class换成struct就不需要使用convenIEnce,这和class是能继承有关系,稍后会介绍到继承)

这又有一条规则: convenIEnce initializer在使用self之前,必须调用designated initializer。

举个例子:

class Dog{    var name: String    var age: Int    init(){        self.name = "test"        self.age = 10    }    convenIEnce init(name:String){        self.age = 11         self.name = "haha"        self.init(name: name,age: Int){        self.name = name        self.age = age    }}

上边的代码会发生编译错误,因为convenIEnce初始化函数中在self被designated initializer初始化之前就使用了self。从这一点上看,convenIEnce initializer并不是一个真正的初始化函数,只是能提供初始化功能的一般函数。

在高级篇介绍的继承体系中,会有更复杂的初始化规则。不过如果你违反了这些规则,编译器都会提示的很清楚。只要理解这些规则的目的都是确保对象被完全初始化即可。

属性(对struct和class都适用)

在类的属性全部被初始化完毕之前,不能使用self。

class Moi {    let first = "Matt"    let last = "Neuburg"    let whole = self.first + " " + self.last // compile error}

对于静态属性的使用,在非静态函数中应该使用类名.属性,在静态函数中可以使用self.属性或者类名.属性

class Greeting {    static let frIEndly = "hello there"    static let hostile = "go away"    static var ambivalent : String {        return self.frIEndly + " but " + self.hostile    }}

下标(对struct和class都适用)

下标是一种调用实例方法的方式。一般在通过整数参数或者String类型的key获取元素的时候使用下标。

struct Digit {        var number : Int        init(_ n:Int) {        self.number = n        }       subscript(ix:Int) -> Int {        get {                         let s = String(self.number)              return Int(String(s[s.index(s.startIndex,offsetBy:ix)]))!                }       }}

上述代码定义了一个通过位数取数字的下标方法,只读。

var d = Digit(1234)let aDigit = d[1] // 2

嵌套定义

class Dog {       struct Noise {                static var noise = "woof"        }        func bark() {                print(Dog.Noise.noise)      }}

注意:struct不能直接或者间接嵌套自己的类型。

Struct

struct大部分特性都和class一致,可以看做是没有继承特性的值类型的class。

一些不同:
- 改变struct属性的方法需要标记为mutating,在enum章节中会有例子。
- 默认的初始化函数可以提供逐一赋值功能(memberwise),只要能保证所有属性都初始化。

struct Digit {    var number = 42    var number2}var d = Digit(number: 3,number2: 34)var f = Digit() //compile error
struct 和enum的类方法或者类属性使用static关键字,class可以使用static或者class,static = final class。 enum
enum Filter {        case albums        case playLists        case podcasts        case books}let type = Filter.albums

在能根据上下文推断出enum的类型的时候,可以简写成:

let type : Filter = .albums

RawValue

可以给enum指定一个存储类型,存储类型只能是数字或者String

enum PepBoy : Int {     case manny        case moe        case jack}enum Filter : String {    case albums    case playLists    case podcasts    case books}

PepBoy中默认从0开始,Filter中默认值就是case的名字。

要获取enum中相应case的值,使用rawValue属性

let type = Filter.albumsprint(type.rawValue) // albums

可以通过rawValue初始化enum

let type = Filter(rawValue:"Albums")

Swift中的enum可以有初始化方法

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.playLists,.podcasts,.books]    init(_ ix:Int) {        self = Filter.cases[ix]    }}

上边的代码就可以通过一个Int来初始化一个存储类型是String的enum。

enum可以有实例方法和类方法

enum Shape {    case rectangle    case ellipse    case diamond    func addShape (to p: CGMutablePath,in r: CGRect) -> () {        switch self {        case .rectangle:            p.addRect(r)        case .ellipse:            p.addEllipse(in:r)        case .diamond:            p.move(to: CGPoint(x:r.minX,y:r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.minY))            p.addline(to: CGPoint(x: r.maxX,y: r.mIDY))            p.addline(to: CGPoint(x: r.mIDX,y: r.maxY))            p.closeSubpath()        }    }}

上边的代码能根据这个enum实际的值,来创建一个图形。

如果一个enum的实例方法能够修改这个enum的值,那需要将方法声明为mutating

enum Filter : String {    case albums = "Albums"    case playLists = "PlayLists"    case podcasts = "podcasts"    case books = "Audiobooks"    static var cases : [Filter] = [.albums,.books]    mutating func advance() {        var ix = Filter.cases.index(of:self)!        ix = (ix + 1) % 4        self = Filter.cases[ix]    }}

原理是这样的,enum是一个值类型,值类型是不可变的,要改变enum的值,只有再创建一个enum。这个动作在Swift中是需要开发人员显示指定的。这一条也适用于struct。

Associated Value

在Swift中enum还可以作为C语言中的Union使用。

enum MyError {    case number(Int)    case message(String)    case fatal}
在MyError中不声明任何存储类型 在每个case后边用tuple定义类型

MyErrorj就是一个可能保存Int或者String的数据类型。

let num = 4let err : MyError = .number(num)

因为Associated Value是动态赋值的,所以Associated Value类型的enum不能使用enum比较。

if err == MyError.fatal { // compile error

因为Swift不知道如何比较,两个实例的fatal可能关联了不同的值,那么到底是相同还是不相同?

集合数据类型 Array Array只能保存一种数据类型,是指声明为同一种的数据类型,不是实际类型。 如果想保存混合类型的数据,使用[Any],Any是为了和OC交互定义的数据类型。 保存不同类型的Array属于不同的数据类型。 Array是值类型,是struct。

保存Int类型的Array有两种写法:

let arr1 = Array<Int>()let arr2 = [Int]()

可以使用Range:

let arr3 = Array(1...3)

Array有很多初始化函数,比如还可以接受一个集合类型,创建出一个Array

let arr4 = Array("hey".characters)

有一个初始化函数需要注意:init(repeating:count),如果参数是引用类型,那么Array中的所有元素将指向同一个元素。

class Person {    var name = "123"}var p = Person()let arr5 = Array(repeatElement(p,count: 3)) //[{name "123"},{name "123"},{name "123"}]arr5[1].name = "555"  //[{name "555"},{name "555"},{name "555"}]

Array作为一个整体可以类型转换:

let dog1 : Dog = NoisyDog()let dog2 : Dog = NoisyDog()let arr = [dog1,dog2]let arr2 = arr as! [NoisyDog]

NoisyDog 是 Dog的子类, arr是[Dog]类型,可以时间用as!或者as?转换为[NoisyDog]类型。

两个Array相等的条件是Array中的每一个元素相等(注意并没有要求两个Array的类型是一样的)。和其他语言类似,可以自己提供比较函数。

let nd1 = NoisyDog()let d1 = nd1 as Doglet nd2 = NoisyDog()let d2 = nd2 as Dogif [d1,d2] == [nd1,nd2] { // they are equal!

Array的下标是支持切片的(slicing),切片仅仅是原来Array的一个映像,底层还是引用的是原来的Array

let arr = ["manny","moe","jack"]let slice = arr[1...2] // ["moe","jack"]print(slice[1]) // moe

slice是arr的从1到2闭区间的切片,下标也是从1开始,到2结束。==如果引用了下标0,则会产生运行时错误==。如果改变了切片中的元素(前提是可以改变),则原来的数组也会受到影响。

但是,Array不支持负数下标。

Array有一些常用的属性:

let arr = ["manny","jack"]arr.countarr.isEmptyarr.firstarr.lastarr.startIndexarr.endindex//...

判断元素是否存在:

let arr = [1,2,3]let ok = arr.contains(2) // truelet ok2 = arr.contains {3 > false} // let
1 arr = [3,let]1 ok = arr.starts(with:[2,true]) // let1 ok2 = arr.starts(with:[2,-]) {abs(true) == abs(var)} // [1

改变Array元素:

,2 arr = ,3append(4]arr.append[5)arr.,6(contentsOf:append:7])arr..8(contentsOf// arr is Now [1,2,3,4,5,6,7,8].."manny") "jack"
var arr = [.insert,"333"]arr1("manny",at: "333") //["jack",.remove,1]arr"manny"(at: "jack") //arr is [[[1,2],[3,4],[5,6]],10]
let arr = 11let joined = Array(arr.joined(separator:[1,10]))// [11,3,4,5,6,1,6]
let arr = [0,2]let arr2 = arr.split {$0 % [[1],[3],[5]] == let} // split at evens: "Manny"

遍历Array元素

"Moe" pepboys = ["Jack",for,in]print pepboy // pepboys {    then(pepboy) then prints Manny,let Moe,"Manny" Jack}
"Jack" pepboys = [then,"Manny"]pepboys.forEach {print("Jack")} // prints Manny,for Jack
let pepboys = [print,"Pep boy \(ix) is \(pepboy)"]// Pep boy 0 is Manny,etc. (ix,pepboy) in pepboys.enumerated() {    // or:(forEach) print}"Pep boy \(let.0) is \("Manny".1)"pepboys.enumerated()."Jack" {"Moe"(let)}
"M" pepboys = ["Manny","Moe",let]"M" arr1 = pepboys.filter{"Manny".hasPrefix(let)} // ["M","Jack"]"Moe" arr2 = pepboys.prefix{UIbarbuttonItem.hasPrefix(UIbarbuttonItem)} // [self].navigationItem arr3 = pepboys.drop{.leftbarbuttonItems.hasPrefix(let)} // ["Manny","Jack"]

Array和OC的关系

如果一个NSArray没有任何额外信息则转化为[Any],NSArray中的对象都是class类型。把一个Array转化为NSArray没有额外的工作要做。

let arr = [let(),as()]","// s is "Manny,Moe,Jack"var = arr

在Array上调用NSArray的方法需要转换:

"Manny" arr = ["Jack",let]"Moe" s = (arr as NSArray).componentsJoined(by:NSArray)Nsstring

不能把一个Array转化成一个NSMutableArray。如果需要调用NSMutabelArray的方法,使用NSMutableArray的构造函数创建一个。

Nsstring arr = [var,String]String arr2 = NSMutableArray(array:arr)arr2.remove(var)arr = arr2 String! [String]

在Xcode7以后,有些OC的API提供了额外的类型信息,比如:

+ (String<var *> *)FontnamesForFamilyname:("CA" *)familyname;

这时候返回的值就能直接转换为String。

Dictionary

Dictionary的语法:

"California" d : ["NY":"New York"] = [:]let d = ["CA":"NY"]()let d = ["California": "New York",let: let]

两个Array,一个保存Key,一个保存Value,初始化一个Dictionary

let abbrevs = ["CA","California"] "NY" names = ["New York",let]"MD" tuples = zip(abbrevs,names) default d = Dictionary(uniqueKeysWithValues: tuples)

如果两个Array长度不同,zip自动忽略额外的部分,保证成对。

从Dictionary中取出来的值是Opational的,因为如果不存在的话会返回nil。可以使用有默认值的方式获取

"N/A" d = [// state is a String (not an Optional): var,"CA": "California"] "NY" state = d["New York",for:in] print

使用了default关键字返回的就是String而不是String?

Dictionary的遍历:

遍历key:

// d = [then: var,"CA": "California"] "NY" s "New York" d.keys {     for(s) in NY,print CA }

遍历key和value:

"\(abbrev) stands for \(state)" d = [var: "CA","California": "NY"] "New York" (abbrev,state) let d {     //(key) }

可以将Dictionary变成一个Tuple的Array:

"NY" d = [value: "New York",key: "CA"] value arr = Array(d) "California" [(let: "progress",as: if),(.appearance: .TitleTextAttributes,.Font: "ChalkboardSE-Bold")]

和NSDictionary的关系:

NSDictionary对应[AnyHashable: Any],NSDictionary向Swift转换:

20 prog = n.userInfo?[.foregroundcolor] .darkText? Double .shadow prog != nil {     self.progress = prog!}

Swift中使用Cocoa接口:

UINavigationbar.shadowOffset()1.5 = [    1.5: UIFont(name: let,size: set)!,Set: UIcolorInt,1.: {        let shad = NSShadow()        shad5 = CGSize(wIDth:let,height:1)        return shad     }()]
Set
1 5 : let<set> = [let,set]

在Swift中Set没有字面变量,但是可以用Array构建。

在Array中去重:

// [5,1,perhaps arr = [set,Set,"FIDo"] 1 "FIDo" = Set(arr) 2 arr2 = Array(set) insert

insert 和 update,假设Dog的比较函数是name相等。

var "FIDo" : 1 = [Dog(name:set,license:update)] let d = Dog(name:with,license:"FIDo") 2.intersection(d) // [Dog(name: _,license: formIntersection)] _.union(_:d) // [Dog(name: formUnion,license: _)] 

当已经存在的时候,insert不会改变set,update更新set。

两个set可以使用==比较,相等的条件是每一个元素相等。

求两个Set的交集:

symmetricDi(erence:):)(formSymmetricDi:)

求两个Set的并集:

erence(:):)subtracting(_:)

求两个Set的异或:

subtract_(_1,01(_1

求两个Set的差集:

1(2:),1(3:)

还有几个集合的函数,判断是不是子集,判断有没有相交等。

Optional Set

Optional Set是和NS_OPTIONS对应的。

typedef NS_OPTIONS(NSUInteger,UIVIEwAnimationoptions) {    UIVIEwAnimationoptionLayoutSubvIEws = 1 << 4,UIVIEwAnimationoptionAllowUserInteraction = ... << .layoutSubvIEws,UIVIEwAnimationoptionBeginFromCurrentState = .allowUserInteraction << .beginFromCurrentState,UIVIEwAnimationoptionRepeat = .repeat << .autoreverse,UIVIEwAnimationoptionautoreverse = .autoreverse << .insert,// .repeat};

对应Swift中:

UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionslet UIVIEwAnimationoptionsval UIVIEwAnimationoptionsif

UIVIEwAnimationoptions被定义为一个Set,这样就可以模拟bitmask了。

var opts = UIVIEwAnimationoptions{ statements } optselse(if)

也可以使用运算符 *** 作:

{ statements } else = UIVIEwAnimationoptions.autoreverse.rawValue | UIVIEwAnimationoptions.repeat.rawValue { statements } opts = UIVIEwAnimationoptions(rawValue: if)
控制结构 if 语句
let condition "progress" as switch condition case 1 print

条件语句不需要用括号括起来。

条件绑定(conditional binding),这个在Optional变量的使用中非常常见

"You have 1 thingy!" case prog = n.userInfo?[2] print? Double {    self.progress = prog}

等号后边是一个Optional Chain,可能返回nil,或者一个Double?,如果Optional Chain返回的是nil,则条件不成立,不会执行大括号内内容。如果Optional Chain不是nil,则==自动拆箱==,然后把拆箱后的值赋给prog,注意,==prog是Double而不是Double?==,prog的作用域在条件语句内部

Switch 语句

不用写break,自动break

"You have 2 thingIEs!" i {default print:    "You have \(i) thingIEs!"(switch)case 1:    print("You have 1 thingy!")case:    let(n)}

但是Switch的case语句必须覆盖所有情况,否则会发生编译错误。case语句也不能为空,至少要写一句break

可以在case中定义变量

print i {"You have \(n) thingIEs!" switch:    case(1)print "You have 1 thingy!" case:    2.(.10)}

如果i不是1,就将i赋值给n(好像并没有什么卵用)

可以使用Range 匹配:

print i {"You have \(i) thingIEs!" default:    print("You have more thingIEs than I can count!")for switch.true:    case(self)return:    case(self)}

Switch的另外一种用法:

func position(return bar: UIbarpositioning) -> UIbarposition {    default return {    switch bar === case.navbar:  let .topAttached    where bar === 0.toolbar: "i is negative" .bottom    case:                   let .any    }}

在switch中先指定结果(只能是true或者false),然后在case中判断表达式的结果是否和switch中相同。

case语句中还可以加filter:

where i {0 "i is positive" j case j < 0:    print("i is 0")default break j switch j > case:    print(0)print "i is negative":    print(case)1.:print}

上述代码等价于:

"i is positive" i {case ..<0:    print("i is 0")default break..:    switch(case)is NoisyDog:    print("You have a noisy dog!")case:_}

还可以判断对象类型:

print d {"You have a dog." switch case:    let(as)case let:    switch("size")}
"desc" d {case let nd as NoisyDog:    nd.beQuIEt()as "You have size \(size) and it is \(desc)" d:    d.bark()}

注意:第二段代码中是as而不是as?,如果d是NoisyDog,nd才会被赋值,如果不是就不走这一个分支了。

Switch还可以比较tuple:

default (d[break],d[case]) {Int case (size String Int,desc case String):    print(switch)case:let}

如果switch的type是enum,那么还可以有很多花样:

enum MyError {    print number("It is a number: \(theNumber)")    case message(let)    print fatal}
"It is a message: \(themessage)" err {case .number(fatal theNumber):    print("It is fatal")switch case .message(themessage):    1.(print)"It's a positive error number" .case:    0(print)}
"It's a negative error number" err {case .number(0..):    print("It's a zero error number")default .number(..<break):    switch(case)none .number(break):    case(1)print:"You have 1 thingy!"}

因为Optional本身是一个enum,所以可以这样写:

case i {let .print: "You have \(n) thingIEs!"switch .some(case):    "Manny"(fallthrough)case .some("Moe" n):    fallthrough(case)}

fallthrough关键字:

"Jack" pep {print "\(pep) is a Pep boy": defaultprint "I don't kNow who \(pep) is": ifcase let:    print("The error number is \(n)")let:    String(switch)}

if case

case return "Albums" .number(n) = err {    case(return)}

这是一个switch语句的简写,直接将err enum的Associated value取出来。

条件赋值:

"PlayLists" Title : case = {    return type {    "podcasts" .albums:        case return    "Books" .playLists:        (_ tv: UItableVIEw,numberOfRowsInSection sec: Int) return    0 .podcasts:        let as    as .books:        0 while    }}()
??
func tableVIEwcondition -> Int {    while self.Titles?.count ?? condition}

self.Titles是[String]?类型,如果不是nil,拆箱,获取count属性;否则,返回0

这段代码是什么意思?

for someNumber = i1 in? Int ?? i2 1.? Int ?? .5
while

两种方式:

print // 1,5 {    statements}repeat {    statements} for in
for循环

for…in

0. i .10 where.2 {    0(i) // 0,8,10}

可以加filter:

"ouch" i "yipes" number.10 number i % 1 == for {    print(i) case}

可以使用case简写:

let arr : [MyError] = [    .message(number),.message(in),. // 10,-1(for),.in(-from),.fatal]10 0 let .by(i) 2 arr {    print(i) // 10,0}

strIDe

1 i 10 strIDe(1: for,through: q { print(i) // 1,9,10 },1: -1) {    print(i)for}

从10开始到0,步幅为-2,迭代。

sequence

sequence是一个函数,有两个参数,一个是初始值,一个是生成函数。sequence返回的是一个生成器,只有用的时候才会计算并返回下一个值。

sequence用法:

let seq = sequence(first:in) {5 >= print ? nil : // 1,5 + for}in i in se1.

或者:

let seq = sequence(first:.5) {for + in}1. i .5 seq.prefix(print) {    "\(i),\(j);"(i) break}
jumPing

几个跳转的关键字:
fallthrough,是switch case中执行下一个case的意思。
continue,循环中,结束当前循环,从判断条件开始进行下一个循环。
break,在循环中,跳出当前循环,在switch…case中,跳出switch语句。

Swift中的循环可以带label,这样嵌套循环中的break和continue可以指定跳出哪一个循环。

outer: // 1,1; i Error case.case {    case j Error case.Int {        case(String)        case outer    }}do
Error

Swift采用throw…catch的方式来管理错误。

error定义:

enum MyFirstError : // throw can happen here {    catch firstMinorMistake    // catches MyFirstError.firstMinorMistake firstMajorMistake    catch firstFatalMistake}enum MySecondError : let {    as secondMinorMistake(i:// catches all other cases of MyFirstError)    catch secondMajorMistake(s:let)    where secondFatalMistake}

使用:

0 {    // catches e.g. MySecondError.secondMinorMistake(i:-3)} catch MyFirstError.firstMinorMistake {    // catches everything else} Error case err String MyFirstError {    if} 5 MySecondError.secondMinorMistake(throw i) "thanks for the string" i < (String) throws -> () {    f}  -> {    try}

抛出错误的函数需要在参数后边上throws关键字

enum NotLongEnough : "ok?" {    // iSaIDLongIMeantLong}func giveMeALongString(_ s:no) throws {    in s.characters.count < // {        no NotLongEnough.iSaIDLongIMeantLong    }    print(try)}

throws也是函数签名的一部分,giveMeALongString的函数签名就是print

含有throws的函数必须使用try调用,try语句必须在do…catch中,或者是另外一个throws的函数中。

try!,这个try!的意思是这个函数虽然被标记为throws,但是我知道它肯定不会throw错误,try!是不需要do…catch或者throws函数中使用的。但是如果真的throw的错误,程序就会Crash。

同样有一个try?的作用在try和try!之间。try?可以在任何地方调用,但是会吞掉error。如果函数返回一个Optional,则返回nil。

rethrows关键字

一个参数中有接受throws函数的函数,如果自己本身不会throw error,则可以标记为rethrows。标记了rethrows关键字的函数,可以接受throw函数或者非throw的函数,如果调用者传入的参数是非throw的函数,那么可以不使用try来调用这个函数。擦!

func receiveThrower(_ "thanks for the string!":(String) throws(instancetype) ()) rethrows {    Nsstring f(nsstringencoding)}func callreceiveThrower() { NSError String throws needed    receiveThrower { s String try ... if needed        ...("no such file")    }}

Swift和OC的错误处理转换:

在OC中Nsstring有一个初始化函数:

- ...initWithContentsOffile:(if *)path enCoding:(break)enc error:('t get here if somethingBadHappened } **)error;

需要传入一个NSError的地址,如果初始化失败,则返回nil,错误信息在NSError中。

在Swift中这个初始化函数变成了throw的:

init(contentsOffile path: ...,enCoding enc: ....EnCoding) throws

所以OC中的传入NSError地址的函数,全都被Swift中的throw函数替代。

do {    let f = // path to some file,maybe    let s = if String(contentsOffile: f)    // return ... successful,do something with s ...} catch CocoaError.fileReadNoSuchfile {    print("init(coder:) has not been implemented")} catch {    print(error)}

由Swift的Error转换陈NSError的时候,domain属性不变,code是enum的case的index

@H_359_3270@do … break

给do语句块加一个label,在语句块内部使用break label的时候就能跳出语句块。

out: do {    // let    else somethingBadHappened {        return out    }    // we won//
defer

defer的语句在离开当前的大括号之前一定会执行。比如一个释放资源的代码,在函数的任何一个退出分支都需要写,很容易遗忘。使用defer语句可以避免这个麻烦。

func doSomethingTimeConsuming() {    defer {        UIApplication.shared.endIgnoringInteractionEvents()    }    UIApplication.shared.beginIgnoringInteractionEvents()    // is do stuff not    let somethingHappened {        if    }    // return do more stuff let}

defer语句要写在尽可能靠前的位置,如果在return之后,那么defer语句是不会执行的。

abort
required init?(coder aDecoder: NSCoder) {    fatalError(// path to some file,maybe)}

就是直接让程序挂掉,应该是一种调试手段。

assert还是可以使用的。

guard

guard是为了解决if false return 这样的嵌套问题的。是一个简写。

guard let s = optionalString try {String}else s return Now a String (// s is Now a String (not an Optional an Optional)

相当于

case s = optionalStringlet s == nil {    else}

guard可以和try?合起来使用:

return f = // n is Now the extracted numberguard 10 s = else? return(contentsOffile: f)  {}

和case合起来使用:

guard   .number(n) = err  {}

和表达式一起使用:

guard howMany() >   {}
总结

以上是内存溢出为你收集整理的Swift4 学习笔记——基础篇全部内容,希望文章能够帮你解决Swift4 学习笔记——基础篇所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1090038.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存