swift3学习基础教程摘要:
1、OC中nil是指向一个不存在对象的指针。swift中表示任何值的缺省。
2、条件判断的时候,可选行不能为nil;条件不能为false。这样才成立:
if let f = Int("4"),let s = Int("42"), f < s && s < 100 {
print(f)
}
3、如果想判断一个非可选型(没有?声明)变量是否为nil。那么就可以把这个声明为隐式解析可选类型(带!)。这时候就可以对变量进行是否为nil判断了。
var assumedStr:String! = "xxx"
if assumedStr != nil {
assumedStr.appendContentsOf("xx")
}
4、函数可以通过在声明中添加throws关键字抛出错误信息:(错误处理)
do {
try canThrowAnError()
}catch{
}
func canThrowAnError() throws {
}
5、断言,主要用于调试使用:触发断言会终止应用继续执行:
let a = -1
assert(a>=0,"error")
6、 swift中赋值不会返回任何值,所以就避免了oc里有人在if条件里把’==’写成’=’;
swift中的’+’可以对字符串进行操作:”hello”+”world” = “helloworld”;
swift没有了’++’、’–’运算符,用’+=’、’-=’代替,但是有一个需要注意:’a+=1’其实是’a=a+1’的简写,而赋值运算符’=’是没有返回值的,所以swift中’let b = a += 1’是错误的,’a=a+1’没有返回值,即不会给b赋值;
swift中比较运算,元组也可以进行比较,当然Bool值是不允许比较的,所以带Bool值的元组不可以进行比较,元组的比较也是逐个元素进行比较的;
swift中空合运算符’??’是专门针对可选型的三目运算符,’a ?? b’是’a != nil ? a! : b’ 的简写,但是有一点需要非常清楚,a一定要是可选型,不然这个运算符就没有意义了,因为理论上非可选型的值是永远不会为nil的,也就没有这种判断的必要了。
swift还有一个坑需要说明一下的就是’|’运算符。我们在OC里经常会用到这个运算符,尤其是枚举类型的时候,比如设置navigationBar的样式。swift是没有这个运算符的,但是在需要用到多个值修饰的时候,直接使用’[]’就好了: self.navigationController!.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor(),..]
7、swift数组,两个数组相加创建一个新的数组,但是这两个数组类型必须得一样;swift多了一个Set集合类型;
8、 swift3 for循环只能使用for in;
新的repeat..while和do..while 作用是一样的,且swift3没有了do..while;
swift3中,switch中case不需要显示地使用break跳出,但是每个case都必须有一条语句,否则就是无效的。所以case也可以同时满足多个条件(复合匹配),每个条件用逗号隔开;case里也可以是元组,这个时候就可以表示点、面积了:
let point = (1,1)
switch point {
case (0,0):
print("原点")
case (_,0):
print("x轴")
case (0,_):
print("y轴")
case (-2...2,-2...2):
print("在[4*4]的面积里")
default:
print("\(point)")
}
case里也可以进行值绑定,将匹配的值绑定到一个临时的常量或者变量里,在case分支内就可以对其进行操作:
let anotherPoint = (2,0)
switch anotherPoint {
case (var x, 0):
x += 2
print("x轴:\(x)")
case (0, var y):
print("y轴:\(y)")
default:
print("\(point)")
}
print("\(point)")
case里的判断也可以使用where进行一些额外的条件判断:
let yetAntherPoint = (1,-1)
switch yetAntherPoint {
case let(x,y) where x == y:
print("在x==y的分角线上")
case let(x,y) where x == -y:
print("在x==-y的分角线上")
default:
print("\(point)")
}
switch中case不需要使用break让它跳出case,但是也可能需要执行下一个case的情况。所以新的关键字:fallthrough就是用来处理这种情况的;
swift中除了if条件判断外,新的guard也是条件判断,guard语句总是带一个else从句。如果条件为false则执行else里的语句,为true则跳过执行后续语句
9、检测API可用性:后面那个*是必须的
if #available(iOS 10, *) {
print("可用")
}
10、多重返回值函数:返回值可以是可选型
func minMax(array:[Int])->(min:Int,max:Int)? {
if array.isEmpty {
return nil
}
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
}else if value > currentMax {
currentMax = value
}
}
return (currentMin,currentMax)
}
if let bounts = minMax(array: [9,-2,1,3,1000,29,-12]) {
print("min:\(bounts.min),max:\(bounts.max)")
}
11、可变参数函数:可变参数的传入值在函数中为此类型的一个数组,一个函数最多只有一个可变参数:
func averageNum(_ numbers:Double...)->Double {
var total:Double = 0
for number in numbers {
total += number
}
return total/Double(numbers.count)
}
print("\(averageNum(1,2,3,4,5))")
12、输入输出参数:是指传入的参数在函数体内被修改了,修改后的结果在函数体外仍有效果,注意传入的参数必须是var类型的,函数形参用inout关键字修饰:
var someInt = 3
var antherInt = 107
func swapTwoInts(_ a: inout Int,_ b: inout Int) -> Int{
let temparam = a
a = b
b = temparam
return a+b
}
let result = swapTwoInts(&someInt, &antherInt)
print("\(result),\(someInt),\(antherInt)")
13、函数类型:像其他基础类型一样的类型,如普通参数一样使用:
func add(_ a:Int,_ b:Int) -> Int {
return a + b
}
var mathFunc:(Int,Int)->Int = add
print("\(mathFunc(1,2))")
//mathFunc的类型就是:(Int,Int)->Int.
函数类型作为参数:
func printMathFunc(_ mathFunc1:(Int,Int)->Int,_ a:Int,_ b:Int){
print("\(mathFunc1(a,b))")
}
printMathFunc(add, 3, 4)
函数类型作为返回值:
func stepForward(_ input:Int)->Int{
return input+1
}
func stepBackward(_ input:Int)->Int{
return input-1
}
func chooseStepFunc(back:Bool) -> (Int) -> Int {
return back ? stepBackward : stepForward
}
14、嵌套函数:即函数里面嵌套函数:
func chooseStepFunc2(back:Bool) -> (Int) -> Int {
func stepForward2(_ input:Int)->Int{
return input+1
}
func stepBackward2(_ input:Int)->Int{
return input-1
}
return back ? stepBackward2 : stepForward2
}
15、闭包
16、枚举:
enum CompassPoint {
case north
case south
case east
case west
}
swift的枚举成员与OC不同,创建时不会被赋予一个默认的整型值。CompassPoint的成员的类型就是CompassPoint类型,枚举成员本身就是完备的值。但是它们也有隐式原始值,当使用字符串作为枚举类型的原始值,每个枚举成员的隐式原始值为该枚举成员的名称:
CompassPoint.west.rawValue 值为 “west”
17、结构体是值类型,类是引用类型。将一个结构体声明为一个常量,则结构体里的属性都是常量;将一个类声明为常量,类的属性不会都成为常量,而是保持原来的存储属性。
lazy延迟属性:必须是变量,因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成前必须要有初始值,因此无法声明成延迟属性。
计算属性:是提供了getter和一个可选的setter的属性。setter可以使用默认的newValue。计算属性必须用var定义。
属性观察器:每次属性被设置值的时候都会调用属性观察器:willSet、didSet
static:在class里也可以用class关键字代替,其实就相当于类方法一样的效果。
18、mutating:实例方法中修改值类型(主要是结构体和枚举),因为默认情况下,值类型的属性不能在它的实例方法中被修改;给隐含的self赋值:
struct Point {
var x = 0.0,y = 0.0
mutating func moveBy(x:Double,y:Double){
self.x += x
self.y += y
}
}
var somePoint = Point(x:1.0,y:1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("point:\(somePoint.x),\(somePoint.y)")
enum TriStateSwitch {
case off,low,high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
var overLight = TriStateSwitch.low
overLight.next()
overLight.next()
19、类型方法:在func关键字前面加上static,来指定类方法。类还可以用关键字class来允许子类重写父类的方法实现:
struct LevelTracher {
static var high = 1
var current = 1
static func unlock(_ level:Int){
if level > high {
high = level
}
}
static func isUnlocked(_ level:Int)->Bool{
return level<=high
}
@discardableResult //可以忽略返回值
mutating func advance(to level:Int)->Bool{
if LevelTracher.isUnlocked(level) {
current = level
return true
}else{
return false
}
}
}
20、下标语法:下标定义使用subscript关键字,它通过指定一个或多个输入参数和返回类型;与实例方法不同的是下标可以设定为只读或读写。分别由getter和setter实现,同样setter有一个参数newValue,当然也可以不指定参数,它就会有一个默认的newValue,而如果是只读的话,get关键字也是可以省略的:
struct TimesTable{
var multipliter:Int
subscript(index:Int)->Int{
return multipliter * index
}
subscript(index:Int,index2:Int)->Int{
get{
return multipliter * index * index2
}
set{
print("newValue:\(newValue)")
multipliter = newValue
}
}
}
var m = TimesTable(multipliter: 2)
m[0]
m[1,2]
m[0,1] = 6
21、继承:
可以将继承来的只读属性重写成读写属性,提供getter和setter。但是,不可以将继承来的读写属性重写为一个只读属性;
不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。因为这些值是不可以被设置的;
此外,不可以同时提供重写的setter和重写的属性观察器;
防止重写,可以通过把方法、属性或下标标记为final来防治被重写,被final标记了的class是不可以被继承的。
22、为存储型属性设置默认值或在构造器中赋值时,它们的值是被直接设置的,不会触发任何属性观察器。构造过程可以给常量复制,一旦构造过程结束,那么它将不可被修改。结构体有逐一成员构造器。
23、引用计数:每一次赋值,引用计数加一:
class Person {
let name:String
init(name:String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
var refrence1:Person?
var refrence2:Person?
var refrence3:Person?
//当创建一个类的新实例的时候,ARC会分配一块内存来存储该实例信息
refrence1 = Person(name: "John") //引用计数1
refrence2 = refrence1 //引用计数2
refrence3 = refrence1 //引用计数3
refrence1 = nil //引用计数2
refrence2 = nil //引用计数1
refrence3 = nil //引用计数0 释放内存
实例间的循环强引用:
class Person {
let name:String
init(name:String) {
self.name = name
}
var apartment:Apartment?
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment{
let unit:String
init(unit:String) {
var tenant:Person?
deinit {
print("Apartment\(unit) is being deinitialized")
}
}
//这样赋值之后,产生循环强引用。
var john = Person(name: "John")
var unit4 = Apartment(unit: "4A")
john.apartment = unit4
unit4.tenant = john
解决实例之间的循环强引用——用无主引用和弱引用:相互之间的两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合弱引用来解决;而一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能产生循环强引用。这种场景最适合无主引用来解决。
//弱引用
class Apartment{
let unit:String
init(unit:String) {
weak var tenant:Person? //注意这里的Person和Apartment都是可选性的。
deinit {
print("Apartment\(unit) is being deinitialized")
}
}
然而还有一种场景,两个属性都必须有值,并且初始化后永远不会为nil。这种场景下,需要一个类使用无主属性,另外一个类使用隐式解析可选属性。
24、可选型:访问可选类型下标?是在[]之前:test[“dave”]?[0]
25、错误处理:在返回箭头->前添加throws关键字表示该函数、方法或构造器可以抛出错误,并将错误传递到函数被调用时的作用域,并且可以一直传下去。但是在每个调用的位置前添加try关键字。然后在最后用do-catch处理:
enum VendingMachineError:Error {
case invalidSelection //选择无效
case insufficientFunds(coinsNeeded:Int) //金额不足
case outofStock //缺货
}
struct Item{
var price:Int
var count:Int
}
class VendingMachine{
var inventory = [
"Candy Bar":Item(price: 12, count: 7),
"Chips":Item(price: 10, count: 4),
"Pretzels":Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack:String){
print("Dispensing \(snack)")
}
func vend(itemNames name:String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outofStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
let favoriteSnacks = [
"Alice":"Chips",
"Bob":"Licorice",
"Eve":"Pretzels",
]
func buyFavoriteSnack(person:String,vendingMachine:VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNames: snackName)
}
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.invalidSelection {
print("invalidSelection")
} catch VendingMachineError.outofStock {
print("outofStock")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("\(coinsNeeded) coins")
}
可用try?通过错误转换成一个可选值来处理错误。可用try!来禁止错误传递。
指定清理操作:defer语句是在即将离开当前代码块时执行一系列语句。
let file = open(f)
defer {
close()
}
26、类型转换:类型检查用(is)来检查一个实例是否属于特定子类型;可以用类型转换符(as? 或 as! )向下转型;
两个特殊的不确定类型:
. Any : 可以表示任何类型,包括函数类型
. AnyObject : 可以表示任何类类型的实例
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.1415926)
things.append("hello")
things.append((3.0, 5.0))
things.append(Person(name: "fcf"))
things.append({(name:String)->String in "hello"})
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0 :
print("a positive double value of \(someDouble)")
case is Double:
print("aome other double")
case let someString as String:
print("a string value of \(someString)")
case let (x,y) as (Double,Double):
print("an (x,y) point at (\(x),\(y))")
case let person as Person:
print("an person name is \(person.name)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
27、扩展extension:
用extension关键字声明,可以为类添加新的方法、属性、下标、嵌套类型等。甚至可以用扩展添加协议,例如:
extension Person:UITabBarDelegate{
}
28、协议protocol:
协议要求在需要和OC打交道的代码中,协议和可选都要求必须带@objc属性。
29、泛性:
func swapTwoValues<F>(_ a: inout F, _ b: inout F){
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
print("\(someInt),\(anotherInt)")
泛型类型:
struct Strack<FCF>{
var items = [FCF]()
mutating func push(item:FCF){
items.append(item)
}
mutating func pop()->FCF {
return items.removeLast()
}
}
var stackOfStrings = Strack<String>()
stackOfStrings.push(item: "f")
stackOfStrings.push(item: "c")
stackOfStrings.push(item: "e")
print("\(stackOfStrings.pop())")
扩展泛型类型:
//扩展泛型类型
extension Strack{
var topItem:FCF? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
print(stackOfStrings.topItem ?? "")
类型约束:
//因为不是所有的类型都可以用==判断相等,所以必须约束泛型是符合Equatable协议的类型。因为所有任何遵循该协议的类型必须实现==和!=。
func findIndex<F:Equatable>(_ valueToFind: F, in array:[F])->Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
关联类型:
protocol Container{
associatedtype ItemType
mutating func append(item:ItemType)
var count:Int{get}
subscript(i:Int)->ItemType{get}
}
struct IntStrack:Container{
var items = [Int]()
mutating func push(item:Int){
items.append(item)
}
mutating func pop()->Int{
return items.removeLast()
}
typealias ItemType = Int
mutating func append(item: IntStrack.ItemType) {
self.push(item: item)
}
var count: Int{
return items.count
}
subscript(i:Int)->Int{
return items[i]
}
}
通过where子句为关联类型定义约束:
func allItemMatch<C1:Container,C2:Container>(_ someContainer:C1,_ anotherContainer:C2) -> Bool where C1.ItemType == C2.ItemType,C1.ItemType:Equatable{
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
30、访问控制:
open:
public:
internal:
filepart:
private: