美文网首页swift
[Swift] The Swift Programming La

[Swift] The Swift Programming La

作者: 木小易Ying | 来源:发表于2019-12-24 08:11 被阅读0次
Simple Values

常量用let,变量用var,如果声明时赋值可以不用声明类型,会根据值自动推测类型,如果没有初始值可以用:声明类型

let implicitInteger = 70
let implicitDouble = 70.0
var myVariable = 42 
myVariable = 50
let explicitDouble: Double = 70

float和double可以用以下方式区分,注意4.0-3.9 != 3.0-2.9哦~ 浮点数处理精度的问题

let e1 : Float = 4.0
let e2 : Double = 4.0
let e3 = Float.init(3.9)
let e4 = 3.9
let f1 = ((e1 - e3) == (Float.init(6.0) - Float.init(5.9)))
let d1 = ((e2 - e4) == (Double.init(6.0) - Double.init(5.9)))

print(type(of: e1))
print(type(of: e2))
print(type(of: e3))
print(type(of: e4))
print(f1)
print(d1)

输出:
Float
Double
Float
Double
true
false

类型转换:

let label = "The width is "
let width = 94
let widthLabel = label + String(width)
print(widthLabel)

\(变量)可以将变量值嵌入到字符串,类似stringFormat:

let apples = 3
let appleSummary = "I have \(apples) apples."
print (appleSummary)

创建数组or字典:

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

空初始化:
let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()

可以给已知类型的数组或字典赋值[]以及[:],但是如果不知道类型例如声明的时候不能这么做哦,下面的会报错的:

let blankArr = []

Control Flow

if-else,for-in,switch的条件可以不加括号,但是block体一定要用括号括起来哦:

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}

if(score)是不OK的,swift木有OC那种可以if(变量)的用法,必须是一个bool表达式,例如if(score > 0)

可选类型XXX?就是这个变量的值可能是nil也可能有值,用if&let可以处理判断可选类型是不是有值:

var optionalString: String? = "Hello"
optionalString = nil

var optionalName: String? = "John Appleseed"

var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
} else {
    greeting = "Hello, blank"
}

let name2 = optionalName
print(name2)

输出:
Optional("John Appleseed")

如果可选类似是nil,if判断的时候 let name = optionalName就是false,如果不为nil就会解包并让这个值在括号里面可用。

注意如果可能是nil一定要用可选类型,下面酱紫会报错哦:

var optionalString = "Hello"
optionalString = nil

switch支持各种条件,比OC灵活好多:

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

输出:
Is it a spicy red pepper?

如果木有default这种情况因为string会有很多很多,不写default会报错没有穷尽。

在执行完switch里面的语句以后,代码就会推出了,所以swift可以不写break哦~

for-in可以遍历字典或者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)

输出25

while循环可以酱紫,我尝试的时候已经不允许用do-while了:

var n = 2
while n < 100 {
    n=n*2
}
var m = 2

...是用于范围的,这种是左右都是闭区间,如果想右边开区间可以0..<3,和for循环可以结合使用例如:

var firstForLoop = 0
for i in 0...3 {
    firstForLoop += I
}

下面这种写法已经被swift抛弃了哦:

var secondForLoop = 0
for (var i = 0; i < 3; i++) {
    secondForLoop += 1
}

Functions and Closures

return type用->来声明,参数用括号的方式声明:

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet(name:"Bob", day:"Tuesday")

swift可以return很多值哦,虽然只是方便的包了一层~

func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
getGasPrices()

还可以通过...传入不限数量的可变参数哦~

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(numbers: 42, 597, 12)

函数还可以内嵌函数哦,当逻辑复杂的时候可以内部分一下~

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

returnFifteen()

函数还可以返回函数,这里其实很像纳姆达表达式了~

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
let eleven = increment(10)
print(eleven)

当然函数还可以作为参数传给另一个函数~

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
}

let numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

不需要名字的函数可以用闭包{}直接声明:

{  
   (参数列表) ->返回值类型 in 
     语句组
}

例如:

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

闭包可以精简一下,如果已知传入或者传出类型,可以省略类型声明:

numbers.map({ number in 3 * number })

你还可以用数字替代参数名称:

sort([1, 5, 3, 12, 2]) { $0 > $1 }

Objects and Classes

定义一个class只要一个名字,variable和method就正常写进去就好啦:

class Shape {
    var numberOfSides = 0

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

使用酱紫:

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

class的初始化函数:

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

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

self可以用于区分参数name和自己的name属性,所有属性都需要被赋值,要么是在初始化init的时候赋值,或者声明的时候类似numberOfSides酱紫,但一定都需要被赋值哦~

deinit方法是在销毁的时候做cleanup滴~

继承用:声明例如class Square: NamedShape。如果覆写的方法必须要override,如果声明override但父类没有这个方法,或者父类有但是没有声明override编译都会报错哦~

子类初始化干三件事:

  • 设置好自己的属性
  • 调用super.init
  • 重新设置父类的属性

getter和setter是酱紫的:

private var _name: String?
var name: String? {
    get {
        return _name
    }
    set {
        _name = newValue
    }
}

setter里面传入的值名字固定叫newValue,如果想改的话可以在setter后面作为参数:

var name: String? {
    get {
        return _name
    }
    set(newName) {
        _name = newName
        print(newName)
    }
}

可以参考一下:https://blog.csdn.net/kuangdacaikuang/article/details/80386104

属性分为两种:计算型属性(执行函数返回其他内存地址)&存储型属性(需要开辟空间,以存储数据)
只实现 getter 方法的属性被称为计算型属性,等同于 OC 中的 ReadOnly 属性。

var title: String {
    get {
        return "Mr " + (name ?? "")
    }
}

计算型属性可以使用以下代码简写

var title: String {
    return "Mr " + (name ?? "")
}

懒加载属性在第一次调用时,执行闭包并且分配空间存储闭包返回的数值,与 OC 不同的是,lazy 属性即使被设置为 nil 也不会被再次调用

lazy var title: String = {
    return "Mr " + (self.name ?? "")
}()

你还可以在改之前willSet之后didSet干点什么:

var numberOfSides: Int = 0 {
    willSet {
        print("will set")
    }
    didSet {
        print("did set")
    }
}

类中的方法相对于函数有一个很大的区别。函数的参数名只能在函数内部使用,但是class里面的func参数名可以在你调用方法是使用。默认情况下,调用时候的名字就是内部的名字,但你也可以指定第二个名字给内部用,或者让调用的时候不用声明名字:

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(amount: 2, numberOfTimes: 7)

如果不想要call时候的名字可以酱紫:

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, _ times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(amount: 2, 7)

当操作optional值的时候,你可以在方法,属性和下标的操作前加上。如果在前面的值是空,则之后的一切东西会被忽略,整个表达式为空。否则,如果这个optional值已经被赋值,则在之后的所有操作都将被执行。无论怎样,这个表达式的值都是一个optional值

let optionalS1: String? = nil
let hashS1 = optionalS1?.hashValue
print(hashS1)

let optionalS2: String? = "hhh"
let hashS2 = optionalS2?.hashValue
print(hashS2)

输出:
nil
Optional(-2378297779058712233)

Enumerations and Structures

swift里面的枚举是可以有方法的~

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 "default"
        }
    }
}

可以指定第一个枚举值的int值,之后的都会依次+1,注意是每个都加一哦,不是一行的是一样的值,类似下面two和three是不一样的哦:

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 .Three:
            return "three"
        case .King:
            return "king"
        default:
            return "default"
        }
    }
}
let ace = Rank.Ace

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

输出:three

比较特殊的时候swift可以指定为string或者float~

enum House: String {
    case Baratheon = "Ours is the Fury"
    case Greyjoy = "We Do Not Sow"
    case Martell = "Unbowed, Unbent, Unbroken"
    case Stark = "Winter is Coming"
    case Tully = "Family, Duty, Honor"
    case Tyrell = "Growing Strong"
}

print(House.Greyjoy)

当然你也可以不提供具体值:

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}

let hearts = Suit.Hearts

注意,上面提到的枚举的Hearts成员的两种方式:当给hearts常量赋值的时候,枚举成员Suit.Hearts被列出全名,因为常量没有被显式的指定类型。在switch中,枚举被简写成.Hearts因为self的值已经知道是Suit类型。你可以使用缩写形式在任何值类型已知的情况下。


Swift 中的结构体与 C++ 和 Objective-C 中的结构体有很大的差别,C++ 和 Objective-C 中的结构体只能定义一组相关的成员变量,而 Swift 中的结构体不仅可以定义成员变量(属性),还可以定义成员方法。因此,我们可以把结构体看做是一种轻量级的类。

可以参考一下:https://www.jianshu.com/p/596864f2c672

class 可以继承,而 struct 不可以。

struct也是支持method,init和变量的,它和class唯一的区别是:class传递的时候是传递引用,struct传递的时候是copy的

Swift 中 struct 是值类型,而 class 是引用类型,所以这篇文章 struct 的行为也可以用到所有的值类型上面,相同地 class 的行为也可以用到引用类型上。

值类型的变量直接包含他们的数据,而引用类型的变量存储对他们的数据引用,因此后者称为对象,因此对一个变量操作可能影响另一个变量所引用的对象。对于值类型都有他们自己的数据副本,因此对一个变量操作不可能影响另一个变量。

// 类赋值
let classCoder = ClassCoder()
classCoder.name = "CJ"
classCoder.age = 18
// classCoder.name=CJ,classCoder.age=18

let classCoder1 = classCoder
classCoder1.name = "NOTCJ"
classCoder1.age = 100
// classCoder.name=NOTCJ,classCoder.age=100,classCoder1.name=NOTCJ,classCoder1.age=100

// 结构体赋值
var structCoder = StructCoder()
structCoder.name = "CJ"
structCoder.age = 18
// structCoder.name=CJ,structCoder.age=18 

var structCoder1 = structCoder
structCoder1.name = "NOTCJ"
structCoder1.age = 100
// structCoder.name=CJ,structCoder.age=18,structCoder1.name=NOTCJ,structCoder1.age=100

class 在实例化的时候不能自动把 property 放在 constructor 的参数里面去,想要和 struct 一样的效果就需要我们自己去创建构造函数了。

// 类实例化
let classCoder = ClassCoder()
// class 不能直接用 ClassCoder(name:"CJ",age:18) 必需要自己创建构造函数才可以
classCoder.name = "CJ"
classCoder.age = 18

// 结构体实例化
var structCoder = StructCoder(name:"CJ",age:18)
// 另外一种实例化方法
var structCoder = StructCoder()
structCoder.name = "CJ"
structCoder.age = 18

枚举还可以提供相关值,在switch的时候只看枚举值,不会对比相关值是不是一致,例如下面的Result(String, String)

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}
 
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
 
switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

if let类似,switch let干了些神马嘞:

let request = (0,"success")  
switch request {  
    case (0, let state):  
        state    //被输出:success  
    case (let errorCode, _):  
        "error code is \(errorCode)"  
}  // 涵盖了所有可能的case,不用写default了  

转成了:
let request = (0,"success")  
switch request {  
    case let (errorCode, state):  
        state    //被输出:success  
    case (let errorCode, _):  
        "error code is \(errorCode)"  
} 

把let放在外面和放在里面为每一个元素单独写上let是等价的。
当你在一个case里使用Value Binding的时候,如果你同时也在它的上一个case里使用了fallthrough,这是编译器所不允许的,你可能会收到这样一个编译错误:

fallthrough

只要把下面的errorCode去掉就行了,当然,考虑好自己的逻辑。


Protocols and Extensions

使用protocol来声明一个协议。

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

注意struct中如果方法修改了属性值,需要标记mutating。但class不用啦,大概是因为struct是值copy不是指针copy。

使用extension来添加一个功能给已经存在的类型,就像新的方法和计算性能。你可以使用extension添加一个Protocol。

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

你可以像其他任何一种命名类型一样使用Protocol,例如,创建一个有不同类型的对象的集合,但是这些对象都符合同样的Protocol。当你使用一种被定义为协议类型的类型的值时,那么定义在协议外面的方法将不可用,即使它原本的class有这个属性,但是因为它的类型声明是Protocol就只能读Protocol暴露给外部的接口。

let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty  // Uncomment to see the error

Generics

泛型就用<type>来定义:

func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
    var result = ItemType[]()
    for i in 0..times {
        result += item
    }
    return result
}
repeat("knock", 4)

class,enum和struct都可以定义一个函数的泛型形式。

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

where可以用于要求泛型继承自XXX,实现了XXX协议等,两个类型一致T.GeneratorType.Element == U.GeneratorType.Element

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

在简单的情况下,你可以省略where,只在冒号后面写协议或者类的名称。书写<T : Equatable>和<T where T : Equatable>是一样的。

Reference:
https://www.jianshu.com/p/3850ce9b081d

相关文章

网友评论

    本文标题:[Swift] The Swift Programming La

    本文链接:https://www.haomeiwen.com/subject/biannctx.html