swift学习

345 阅读7分钟

swift设计原则

  • Variables are always initialized before use.
  • 变量使用之前必须已经被初始化。
  • Array indices are checked for out-of-bounds errors.
  • 数组下标被检查过,防止出现数组越界。
  • Integers are checked for overflow.
  • 数字会被检查过,防止出现数字溢出。
  • Optionals ensure that nil values are handled explicitly.
  • nil类型会被代码明确处理,使用Optionals封装,防止空指针异常。
  • Memory is managed automatically.
  • 内存自动管理。
  • Error handling allows controlled recovery from unexpected failures.
  • 错误处理允许恢复状态

swift优势

  1. 性能优化
  2. 强大的类型推断
  3. 语法简洁明确

特点

  1. 类型永远不会自动转换。例如 int 赋值给 float会编译不通过,而c语言可行。使用String()等
  2. 字符串支持插值(变量或者表达式)
  3. """多行字符串支持
  4. 集合类型,不管是array还是dictionary都使用中括号[]
  5. 创建空Arraylet emptyArray = [String]()
  6. swift中涉及到bool值的必须是bool类型,不能是对象和正整数。
  7. 如果某一个对象可能为空,我们应该使用Optionnal包装它。
  8. 支持元组,一般用于返回多个数据,而不需要返回array或者dic。
  9. 支持函数嵌套,函数中可以定义函数,与局部变量类似。

示例代码

1. if let用于接受可能为空的值或者??提供默认值

对一个非optional的值赋值为nil会编译失败

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
//  如果optionalName为nil,if条件不成立,将执行else
if let name = optionalName {
    greeting = "Hello, \(name)"
} else {
    
}

也可以使用??为optional提供一个默认值
let fullName = "fullName"
greeting = "Hello, \(name ?? fullName)"

?进一步思考,如果我们的数据来源于网络或者其他地方,数据为空,会出现什么现象。

{
   "status": 0,
   "data": null
}

json解析有两个用途,解析成map然后再赋值给model。 解析成map
在oc中,json中的null会被解析成NSNull对象,而不是nil。swift会将null解析成nil。

2. switch很全能

switch的条件可以直接作为参数,并且不需要写break。

let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}

3. for-in迭代器

在oc中也有for-in,但是oc中的for-in是无序的,而swift中的有序(对于array)。

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)

4. range

var total = 0
for i in 0..<4 {
    total += i
}
for i in 0...4 {
    total += i
}
print(total)

5.函数

// 基本操作
func greet(person: String, day: String) -> String {
    return "function"
}
greet(person: "John", day: "Wednesday")

// 隐藏调用时的参数名
func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

// 闭包函数(闭包内作用域与闭包定义位置同级)
func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

//闭包作为参数
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

// 匿名的闭包函数
numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})

// 闭包函数省略参数类型和返回值类型,交给编译器。
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

类与对象

// 定义class方法(与其他语言基本相同)
// class可以不继承自任何对象,为基类。
// 属性必须有一个值,要么声明的时候设置,要么在init方法中设置。
// 子类覆盖父类方法必须使用override ,oc不需要。
class Shape {
    var numberOfSides = 0
    var name: String
    var sideLength: Double
    
    // 计算属性,一个值依赖于其他属性,以前一般使用方法来提供,swift中特意设置成计算属性。
    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }
    init(name: String, sideLength: Double) {
        self.name = name
        self.sideLength = sideLength
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

// 使用class (与java等语言相同,与oc系列不同,oc必须先alloc再init)
// 这里调用默认构造函数即可,不需要调用alloc,也不需要使用new关键词。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

// 如果class中多个参数之前有需要关联的地方,使用willset和didset
// 如下,两个参数一个修改了,需要立刻修改另外一个,但是两个参数没有某一种直接的联系。
class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
        didSet {
            print(666)
        }
    }
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
// Prints "10.0"
print(triangleAndSquare.triangle.sideLength)
// Prints "10.0"
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
// Prints "50.0"

optional values ?

// 可选值,也就是可能为nil的包装类型。
// 如果一个值可能为nil,在加上?以后,如果这个值为nil,?后面的将会忽略,表达式将直接返回nil

var op:String?   //nil
op = nil         //nil
op?.append("8")  //nil
op = "String"    //"String"
op?.append("9")  //()
op               //"String9"
// 我们也可以不用op?操作,这样,表达式的结果,就变成了一个optional类型。

enum struct

// struct是值类型,赋值的时候,会复制一份新的。

// swift中enum特性多很多
// enum的每个case的类型为enum,在oc中枚举类型的每个case值为数字。swift中为enum类型
enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        //switch必须包含所有的case,或者提供一个default
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
let aceRawValue = ace.rawValue


// switch不仅能存储固定的值。还能存储变量。
// 可以将其理解为,每个case能存储变量。
enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
    print("Failure...  \(message)")
}
// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."

protocol 与 extension

oc中也有协议和category,extension
swift中的extension与oc中基本一致
swift中protocol也可以定义变量

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

swift中enum和struct基本一致,他们和class一样可以实现protocol

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    // 结构体中方法声明mutating才能修改结构体其他属性,class不需要
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

// extension可以添加计算属性,和方法。还可以通过这个来实现protocol

extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number \(self)"
    }
    mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)

错误处理

// 自定义错误,通过实现error
enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}
//throw抛出异常,方法立刻结束。用throws表明一个方法可以抛出异常
//异常如果没有被捕获,会导致程序crash

// 处理方法一 捕获错误
do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}

// 处理方法二 try?
// 这种方式会忽略错误,表达式的值为nil
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

defer延迟执行

// 使用defer代码块,会在函数内等其他代码执行完,再最后执行defer代码块,在真正return之前(不是return语句)。
// 即使抛出异常,defer代码库还会执行,所以可以用来做一些清理。

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
print(fridgeIsOpen)
// Prints "false"


泛型

// 泛型代表一种
// 通常来讲,泛型为类或者方法提供一个类型参数,以方便某个参数类型保持前后的一致性。
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)