swift---->swift tour

Swift is a fantastic way to write software, whether it’s for phones, desktops, servers, or anything else that runs code. Swift defines away large classes of common programming errors by adopting modern programming patterns:

  • 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.
  • Memory is managed automatically.
  • Error handling allows controlled recovery from unexpected failures.

hello world
print("Hello World")
simple value
  • Use let to make a constant and var to make a variable.
var myVariable = 42
myVariable = 50
let myConstant = 42
  • Values are never implicitly converted to another type.
let label = "The width is "
let width = 94
let widthLabel = label + String(width)

if we remove the conversion of String, it will get error following.

image-20200605153358682

  • string interpolation, use ()
let apples = 3
let oranges = 5
let appleSummary = "I have (apples) apples."
let fruitSummary = "I have (apples + oranges) pieces of fruit."
  • multiple lines, use """
let quotation = """
I said "I have (apples) apples."
And then I said "I have (apples + oranges) pieces of fruit."
"""
  • arrays and dictionaries creation, use []
var shoppingList = ["catfish", "water", "tulips"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

To create an empty array or dictionary, use the initializer syntax.

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

If type information can be inferred, you can writean empty array as [] and an empty dictionary as [:]

shoppingList = []
occupations = [:]

Controll flow

Use if and switch to make conditionals, and use for-in, while, and repeat-while to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required.

  • use if and for
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
print(teamScore) // 11
  • use ? as optional value
// error: variable 'hello' used before being initialized 
var hello: String
print(hello == nil);

// error: 'nil' cannot initialize specified type 'String'
var hello: String = nil
print(hello == nil);

// success, print true
var hello: String? = nil
print(hello == nil);
  • use ?? asign nil variable default value
// if name is nil, the defualt value "John Appleseed" is used instead.
let name: String? = nil
let username: String = name ?? "John Appleseed"
  • Switches

Switches support any kind of data and a wide variety of comparison operations—they aren’t limited to integers and tests for equality.

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.")
}
// "Is it a spicy red pepper?"

After executing the code inside the switch case that matched, the program exits from the switch statement.

  • for-in to iterate

You use for-in to iterate over items in a dictionary by providing a pair of names to use for each key-value pair.

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) // 25
  • while and repeat while
var n = 2
while n < 100 {
    n *= 2
}
print(n) // 128

var m = 2
repeat {
    m *= 2
} while m < 100
print(m) // 128
  • ..< or ... to make a range of indexes

Use ..< to make a range that omits its upper value, and use ... to make a range that includes both values.

// total = 1 + 2 + 3
var total = 0
for i in 1..<4 {
    total += i
}
print(total) // 6

// sum = 1 + 2 + 3 + 4
var sum = 0
for i in 1...4 {
    sum += i
}
print(sum) // 10

Function and Closures

Use func to declare a function. Call a function by following its name with a list of arguments in parentheses. Use -> to separate the parameter names and types from the function’s return type.

  • basic function call
func greet(person: String, day: String) -> String {
    return "Hello (person), today is (day)."
}
greet(person: "Bob", day: "Tuesday")
greet("Bob", "Tuesday") // will get compile error

By default, functions use their parameter names as labels for their arguments. We can write a custom argument label before the parameter name, or write _ to use no argument label.

func greet(_ person: String, on day: String) -> String {
    return "Hello (person), today is (day)."
}
greet("John", on: "Wednesday")
greet(person: "John", on: "Wednesday") // will get error
  • tuple to make a compound value

We can use tuple to return multiple values from a function.

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum) // 120
print(statistics.2) // 120
  • nested functions

Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that is long or complex.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
print(returnFifteen()) // 15

Functions are a first-class type. This means that a function can return another function as its value.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
print(increment(7)) // 8

A function can take another function as one of its arguments.

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]
let isMatches = hasAnyMatches(list: numbers, condition: lessThanTen)
print(isMatches) // true
  • closures

Functions are actually a special case of closures: blocks of code that can be called later.

let numbers = [1,2,3]
let result = numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
print(result) // [3, 6, 9]

When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both.

let numbers = [1,2,3]
let result = numbers.map({number in 3 * number})
print(result) // [3, 6, 9]

You can refer to parameters by number instead of by name—this approach is especially useful in very short closures.

// the sorted method with two parameters
let numbers = [1,2,3]
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers) // [3, 2, 1]

Object and Classed

Use class followed by the class’s name to create a class.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

Create an instance of a class by putting parentheses after the class name.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

init method to set up the class when an instance is created.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape named (name) with (numberOfSides) sides."
    }
}

var shape = NamedShape(name: "huhx")
print(shape.simpleDescription()) // A shape named huhx with 0 sides.

Use deinit to create a deinitializer if you need to perform some cleanup before the object is deallocated.


Methods on a subclass that override the superclass’s implementation are marked with override—overriding a method by accident, without override, is detected by the compiler as an error.

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length (sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
print(test.area()) // 27.040000000000003
print(test.simpleDescription()) // A square with sides of length 5.2.

In addition to simple properties that are stored, properties can have a getter and a setter.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length (sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter) // 9.3
triangle.perimeter = 9.9
print(triangle.sideLength) // 3.3000000000000003

In the setter for perimeter, the new value has the implicit name newValue. You can provide an explicit name in parentheses after set.

Notice that the initializer for the EquilateralTriangle class has three different steps:

  1. Setting the value of properties that the subclass declares.
  2. Calling the superclass’s initializer.
  3. Changing the value of properties defined by the superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.

If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet.

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    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) // 10.0
print(triangleAndSquare.triangle.sideLength) // 10.0
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength) // 50.0

When working with optional values, you can write ? before operations like methods, properties, and subscripting.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

Enumerations and Structures

Use enum to create an enumeration. Like classes and all other named types, enumerations can have methods associated with them.

By default, Swift assigns the raw values starting at zero and incrementing by one each time, but you can change this behavior by explicitly specifying values. In the example below, Ace is explicitly given a raw value of 1, and the rest of the raw values are assigned in order.

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        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
print(ace.simpleDescription()) // ace
let aceRawValue = ace.rawValue
print(aceRawValue) // 1

Use the init?(rawValue:) initializer to make an instance of an enumeration from a raw value. I

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
    print(threeDescription) // 3
}

enumeration can have values associated with the case—these values are determined when you make the instance, and they can be different for each instance of an enumeration 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."

Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The (rank.simpleDescription()) of (suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
print(threeOfSpadesDescription) // The 3 of spades

Protocols and Extensions

Use protocol to declare a protocol.

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

Classes, enumerations, and structs can all adopt protocols.

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

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 func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

Use extension to add functionality to an existing type, such as new methods and computed properties.

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

You can use a protocol name just like any other named type

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription) // A very simple class.  Now 100% adjusted.
print(protocolValue.anotherProperty)  // Uncomment to see the error

Even though the variable protocolValue has a runtime type of SimpleClass, the compiler treats it as the given type of ExampleProtocol. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.

Error handling

You represent errors using any type that adopts the Error protocol.

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

Use throw to throw an error and throws to mark a function that can throw an error.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

There are several ways to handle errors. One way is to use do-catch.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
// Prints "Job sent"

You can provide multiple catch blocks that handle specific errors.

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)
}
// Prints "Job sent"

Another way to handle errors is to use try? to convert the result to an optional.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

Use defer to write a block of code that is executed after all other code in the function, just before the function returns.

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) // false

Generics

Write a name inside angle brackets to make a generic function or type.

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)

You can make generic forms of functions and methods, as well as classes, enumerations, and structures.

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

Use where right before the body to specify a list of requirements—for example, to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass.

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
anyCommonElements([1, 2, 3], [3])

原文地址:https://www.cnblogs.com/huhx/p/13050786.html