2018-01-30 03:39:01 weixin_33728708 阅读数 0

前言

写这篇文章主要是为了给组内要做的分享准备内容。这段时间几个项目都用到 Swift,在上次 GIAC 大会上就被问到为什么要用 Swift,正好这个主题可以聊聊 Swift 的哪些特性吸引了我。

泛型

先来个例子看下泛型是解决什么问题的。

let nations = ["中国", "美国", "日本"]
func showNations(arr : [String]) {
    arr.map { str in
        print("\(str)")
    }
}
复制代码

我们先定一个字符串数组,然后把里面的字符串打印出来。这里的 map 写法还可以优化下:

arr.map { print("\($0)") }
复制代码

那么还能做什么优化呢。将 showNations 的入参数组泛型以支持多类型,比如 [int],[double] 等。

func showArray<T>(arr: [T]) {
    arr.map { print("\($0)") }
}
复制代码

可以看出泛型能够很方便的将不同类型数据进行相同操作的逻辑归并在一起。

类型约束

先看下我的 HTN 项目里状态机的 Transition 结构体的定义

struct HTNTransition<S: Hashable, E: Hashable> {
    let event: E
    let fromState: S
    let toState: S
    
    init(event: E, fromState: S, toState: S) {
        self.event = event
        self.fromState = fromState
        self.toState = toState
    }
}
复制代码

这里的 fromState,toState 和 event 可以是不同类型的数据,可以是枚举,字符串或者整数等,定义 S 和 E 两个不同的泛型可以让状态和事件类型不相同,这样接口会更加的灵活,更容易适配更多的项目。

大家会注意到 S 和 E 的冒号后面还有个 Hashable 协议,这就是要求它们符合这个协议的类型约束。使用协议的话可以使得这两个类型更加的规范和易于扩展。

Swift 的基本类型 String,Int,Double 和 Bool 等都是遵循 Hashable 的,还有无关联值的枚举也是的。Hashable 提供了一个 hashValue 方法用在判断遵循协议对象是否相等时用。

Hashable 协议同时也是遵守 Equatable 协议,通过实现 == 运算符来确定自定义的类或结构是否相同。

关联类型

在协议里定义的关联类型也可以用泛型来处理。比如我们先定义一个协议

protocol HTNState {
    associatedtype StateType
    func add(_ item: StateType)
}
复制代码

采用非泛型的实现如下:

struct states: HTNState {
    typealias StateType = Int
    func add(_ item: Int) {
        //...
    }
}
复制代码

采用泛型遵循协议可以按照下面方式来写:

struct states<T>: HTNState {
    func add(_ item: T) {
        //...
    }
}
复制代码

这样关联类型也能够享受泛型的好处了。

类型擦除

但是在使用关联类型的时候需要注意当声明一个使用了关联属性的协议作为属性时,比如下面的代码:

class stateDelegate<T> {
    var state: T
    var delegate: HTNState
}
复制代码

先会提示 no initializers 的错误,接着会提示 error: protocol 'HTNState' can only be used as a generic constraint because it has Self or associated type requirements 。意思是 HTNState 协议只能作为泛型约束来用,因为它里面包含必需的 self 或者关联类型。

那么该如何处理呢?这里需要通过类型擦除来解决,主要思路就是加个中间层在代码中让这个抽象的类型具体化。实际上在 Swift 的标准库里就有类型擦除很好的运用,比如 AnySequence 的协议。

Where 语句

函数,扩展和关联类型都可以使用 where 语句。where 语句是对泛型在应用时的一种约束。比如:

func stateFilter<FromState:HTNState, ToState:HTNState>(_ from:FromState, _ to:ToState) where FromState.StateType == ToState.StateType {
    //...
}
复制代码

这个函数就要求他们的 StateType 具有相同类型。

泛型和 Any 类型

这两个类型看起来很相似,但是一定要小心两者的区别。他们区别在于 Any 类型会避开类型的检查,所以尽量少用最好不用。泛型一方面很灵活一方面也很安全,下面举个例子感受下两者的区别:

func add<T>(_ input: T) -> T {
    //...
    return input;
}

func anyAdd(_ input: Any) -> Any {
    //...
    return input;
}
复制代码

这两个函数都是可以允许任意类型的 input 参数,不同在于返回的类型在 anyAdd 函数里是可以和入参不一样的,这样就会失控,在后续的操作中容易出错。

集合

基本概念

先来了解下集合的基本概念,首先集合是泛型的比如:

let stateArray: Array<String> = ["工作","吃饭","玩游戏","睡觉"]
复制代码

集合它需要先有个遍历的功能,通过 GeneratorType 协议,可以不关注具体元素类型只要不断的用迭代器调 next 就可以得到全部元素。但是使用迭代器没法进行多次的遍历,这时就需要使用 Sequence 来解决这个问题。像集合的 forEach,elementsEqual,contains,minElement,maxElement,map,flatMap,filter,reduce 等功能都是因为有了 Sequence 的多次遍历。

最后 Collection 概念是因为 Sequence 无法确定集合里的位置而在 Sequence 的基础上实现了 Indexable 协议。有了 Collection 就可以确定元素的位置,包括开始位置和结束位置,这样就能够确定哪些元素是已经访问过的,从而避免多次访问同一个元素。还能够通过一个给定的位置直接找到那个位置的元素。

以上描述如下图:

迭代器

Swift 里有个简单的 AnyIterator 结构体

struct AnyIterator<Element>: IteratorProtocol { 
    init(_ body: @escaping () -> Element?)
    //...
}
复制代码

AnyIterator 实现了 IteratorProtocol 和 Sequence 协议。通过下面的例子我们来看看如何使用 AnyIterator :

class stateItr : IteratorProtocol {
    var num:Int = 1
    func next() -> Int?{
        num += 2
        return num
    }
}

func findNext<I: IteratorProtocol>( elm: I) -> AnyIterator<I.Element> where I.Element == Int
{
    var l = elm
    print("\(l.next() ?? 0)")
    return AnyIterator { l.next() }
}

findNext(elm: findNext(elm: findNext(elm: stateItr())))
复制代码

首先是定义个遵循了 IteratorProtocol 并实现了 next 函数的类。再实现一个 AnyIterator 的迭代器方法,这样通过这个方法的调用就可以不断的去找符合的元素了。

这里有个对 where 语句的运用,where I.Element == Int。如果把这句改成 where I.Element == String 会出现下面的错误提示

Playground execution failed:

error: MyPlayground.playground:18:37: error: cannot invoke 'findNext(elm:)' with an argument list of type '(elm: stateItr)'
findNext(elm: findNext(elm: findNext(elm: stateItr())))
                                    ^

MyPlayground.playground:11:6: note: candidate requires that the types 'Int' and 'String' be equivalent (requirement specified as 'I.Element' == 'String' [with I = stateItr])
func findNext<I: IteratorProtocol>( elm: I) -> AnyIterator<I.Element> where I.Element == String
     ^
复制代码

编译器会在代码检查阶段通过代码跟踪就发现类型不匹配的安全隐患,这里不得不对 Swift 的设计点个赞先

Sequence

上面的迭代器只会以单次触发的方式反复计算下个元素,但是对于希望能够重新查找或重新生成已生成的元素,这样还需要有个新的迭代器和一个子 Sequence。在 Sequence 协议里可以看到这样的定义:

public protocol Sequence {
    //Element 表示序列元素的类型
    associatedtype Element where Self.Element == Self.Iterator.Element
    //迭代接口类型
    associatedtype Iterator : IteratorProtocol
    //子 Sequence 类型
    associatedtype SubSequence
    //返回 Sequence 元素的迭代器
    public func makeIterator() -> Self.Iterator
    //...
}
复制代码

重新查找靠的是这个新的迭代器,而对于切片这样的会重新生成新 Sequence 的操作就需要 SubSequence 进行存储和返回。

Collection

对 Sequence 进行进一步的完善,最重要的就是使其具有下标索引,使得元素能够通过下标索引方式取到。Collection 是个有限的范围,有开始索引和结束索引,所以 Collection 和 Sequence 的无限范围是不一样的。有了有限的范围 Collection 就可以有 count 属性进行计数了。

除了标准库里的 String,Array,Dictionary 和 Set 外比如 Data 和 IndexSet 也由于遵循了 Collection 协议而获得了标准库了那些集合类型的能力。

map

在泛型的第一个例子里我们就看到了 map 的使用,我们看看 map 的定义:

func map<T>(transform: (Self.Generator.Element) -> T) rethrows -> [T]
复制代码

这里 (Self.Generator.Element) -> T 就是 map 闭包的定义,Self.Generator.Element 就是当前元素的类型。

flatmap

二维数组经过 flatmap 会降维到一维,还能过滤掉 nil 值。下面看看 Swift 源码(swift/stdlib/public/core/SequenceAlgorithms.swift.gyb)中 flatmap 的实现:

//===----------------------------------------------------------------------===//
// flatMap()
//===----------------------------------------------------------------------===//

extension Sequence {
  /// Returns an array containing the concatenated results of calling the
  /// given transformation with each element of this sequence.
  ///
  /// Use this method to receive a single-level collection when your
  /// transformation produces a sequence or collection for each element.
  ///
  /// In this example, note the difference in the result of using `map` and
  /// `flatMap` with a transformation that returns an array.
  ///
  ///     let numbers = [1, 2, 3, 4]
  ///
  ///     let mapped = numbers.map { Array(count: $0, repeatedValue: $0) }
  ///     // [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
  ///
  ///     let flatMapped = numbers.flatMap { Array(count: $0, repeatedValue: $0) }
  ///     // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
  ///
  /// In fact, `s.flatMap(transform)`  is equivalent to
  /// `Array(s.map(transform).joined())`.
  ///
  /// - Parameter transform: A closure that accepts an element of this
  ///   sequence as its argument and returns a sequence or collection.
  /// - Returns: The resulting flattened array.
  ///
  /// - Complexity: O(*m* + *n*), where *m* is the length of this sequence
  ///   and *n* is the length of the result.
  /// - SeeAlso: `joined()`, `map(_:)`
  public func flatMap<SegmentOfResult : Sequence>(
    _ transform: (${GElement}) throws -> SegmentOfResult
  ) rethrows -> [SegmentOfResult.${GElement}] {
    var result: [SegmentOfResult.${GElement}] = []
    for element in self {
      result.append(contentsOf: try transform(element))
    }
    return result
  }
}

extension Sequence {
  /// Returns an array containing the non-`nil` results of calling the given
  /// transformation with each element of this sequence.
  ///
  /// Use this method to receive an array of nonoptional values when your
  /// transformation produces an optional value.
  ///
  /// In this example, note the difference in the result of using `map` and
  /// `flatMap` with a transformation that returns an optional `Int` value.
  ///
  ///     let possibleNumbers = ["1", "2", "three", "///4///", "5"]
  ///
  ///     let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
  ///     // [1, 2, nil, nil, 5]
  ///
  ///     let flatMapped: [Int] = possibleNumbers.flatMap { str in Int(str) }
  ///     // [1, 2, 5]
  ///
  /// - Parameter transform: A closure that accepts an element of this
  ///   sequence as its argument and returns an optional value.
  /// - Returns: An array of the non-`nil` results of calling `transform`
  ///   with each element of the sequence.
  ///
  /// - Complexity: O(*m* + *n*), where *m* is the length of this sequence
  ///   and *n* is the length of the result.
  public func flatMap<ElementOfResult>(
    _ transform: (${GElement}) throws -> ElementOfResult?
  ) rethrows -> [ElementOfResult] {
    var result: [ElementOfResult] = []
    for element in self {
      if let newElement = try transform(element) {
        result.append(newElement)
      }
    }
    return result
  }
}
复制代码

从代码中可以看出打平的原理是将集合中所有元素都添加到另外一个集合里。在第二个 extension 里通过 if let 语句会挡住那些解包不成功的元素。

Reduce

Reduce 是编程语言语义学里的归约语义学,也叫累加器。下面同样可以看看 Swift 源码里对其的实现:

//===----------------------------------------------------------------------===//
// reduce()
//===----------------------------------------------------------------------===//

extension Sequence {
  /// Returns the result of combining the elements of the sequence using the
  /// given closure.
  ///
  /// Use the `reduce(_:_:)` method to produce a single value from the elements
  /// of an entire sequence. For example, you can use this method on an array
  /// of numbers to find their sum or product.
  ///
  /// The `nextPartialResult` closure is called sequentially with an
  /// accumulating value initialized to `initialResult` and each element of
  /// the sequence. This example shows how to find the sum of an array of
  /// numbers.
  ///
  ///     let numbers = [1, 2, 3, 4]
  ///     let numberSum = numbers.reduce(0, { x, y in
  ///         x + y
  ///     })
  ///     // numberSum == 10
  ///
  /// When `numbers.reduce(_:_:)` is called, the following steps occur:
  ///
  /// 1. The `nextPartialResult` closure is called with `initialResult`---`0`
  ///    in this case---and the first element of `numbers`, returning the sum:
  ///    `1`.
  /// 2. The closure is called again repeatedly with the previous call's return
  ///    value and each element of the sequence.
  /// 3. When the sequence is exhausted, the last value returned from the
  ///    closure is returned to the caller.
  ///
  /// If the sequence has no elements, `nextPartialResult` is never executed
  /// and `initialResult` is the result of the call to `reduce(_:_:)`.
  ///
  /// - Parameters:
  ///   - initialResult: The value to use as the initial accumulating value.
  ///     `initialResult` is passed to `nextPartialResult` the first time the
  ///     closure is executed.
  ///   - nextPartialResult: A closure that combines an accumulating value and
  ///     an element of the sequence into a new accumulating value, to be used
  ///     in the next call of the `nextPartialResult` closure or returned to
  ///     the caller.
  /// - Returns: The final accumulated value. If the sequence has no elements,
  ///   the result is `initialResult`.
  public func reduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult:
      (_ partialResult: Result, ${GElement}) throws -> Result
  ) rethrows -> Result {
    var accumulator = initialResult
    for element in self {
      accumulator = try nextPartialResult(accumulator, element)
    }
    return accumulator
  }
}
复制代码

可以看到里面会通过 initialResult 来记录前面的返回结果和当前元素进行在闭包里的操作。

Array

看看数组的基本用法

//创建数组
var nums = [Int]() //创建空数组
var mArray = nums + [2,3,5] + [5,9]//合并多个有相同类型元素数组的值
var animals: [String] = ["dragon", "cat", "mice", "dog"]

//添加数组
animals.append("bird")
animals += ["ant"]

//获取和改变数组
var firstItem = mArray[0]
animals[0] = "red dragon"
animals[2...4] = ["black dragon", "white dragon"] //使用下标改变多个元素
animals.insert("chinese dragon", at: 0) //在索引值之前添加元素
let mapleSyrup = animals.remove(at: 0) //移除数组中的一个元素
let apples = animals.removeLast() //移除最后一个元素

////数组遍历
for animal in animals {
    print(animal)
}
for (index, animal) in animals.enumerated() {
    print("animal \(String(index + 1)): \(animal)")
}
/*
 animal 1: red dragon
 animal 2: cat
 animal 3: black dragon
 animal 4: white dragon
 */
复制代码

弱引用的 Swift 数组

Swift 里的数组默认会强引用里面的元素,但是有时候可能希望能够弱引用,那么就可以使用 NSPointerArray。它在初始化的时候可以决定是用弱引用方式还是强引用方式。

let strongArr = NSPointerArray.strongObjects() // 强引用
let weakArr = NSPointerArray.weakObjects() // Maintains weak references
复制代码

Dictionary 的要想用弱引用可以使用 NSMapTable,Set 对应的是 NSHashTable。

Dictionary

看看基本用法:

//创建 Dictionary
var strs = [Int: String]()
var colors: [String: String] = ["red": "#e83f45", "yellow": "#ffe651"]
strs[16] = "sixteen"

//updateValue 这个方法会返回更新前的值
if let oldValue = colors.updateValue("#e83f47", forKey: "red") {
    print("The old value for DUB was \(oldValue).")
}

//遍历
for (color, value) in colors {
    print("\(color): \(value)")
}

//map
let newColorValues = colors.map { "hex:\($0.value)" }
print("\(newColorValues)")

//mapValues 返回完整的新 Dictionary
let newColors = colors.mapValues { "hex:\($0)" }
print("\(newColors)")
复制代码

协议式编程

Swift 被设计成单继承,如果希望是多继承就需要使用协议。协议还有个比较重要的作用就是通过 associatedtype 要求使用者遵守指定的泛型约束。

下面先看看传统编程的开发模式:

class Dragon {
    
}
class BlackDragon: Dragon{
    func fire() {
        print("fire!!!")
    }
}

class WhiteDragon: Dragon {
    func fire() {
        print("fire!!!")
    }
}

BlackDragon().fire()
WhiteDragon().fire()
复制代码

这个例子可以看出 fire() 就是重复代码,那么首先想到的方法就是通过直接在基类里添加这个方法或者通过 extension 来对他们基类进行扩展:

extension Dragon {
    func fire() {
        print("fire!!!")
    }
}
复制代码

这时我们希望加个方法让 Dragon 能够 fly:

extension Dragon {
    func fire() {
        print("fire!!!")
    }
    func fly() {
        print("fly~~~")
    }
}
复制代码

这样 BlackDragon 和 WhiteDragon 就都有这两个能力了,如果我们设计出一个新的龙 YellowDragon 或者更多 Dragon 都没有 fly 的能力,这时该如何。因为没法多继承,那么没法拆成两个基类,这样必然就会出现重复代码。但是有了协议这个问题就好解决了。具体实现如下:

protocol DragonFire {}
protocol DragonFly {}

extension DragonFire {
    func fire() {
        print("fire!!!")
    }
}
extension DragonFly {
    func fly() {
        print("fly~~~")
    }
}

class BlackDragon: DragonFire, DragonFly {}
class WhiteDragon: DragonFire, DragonFly {}
class YellowDragon: DragonFire {}
class PurpleDragon: DragonFire {}

BlackDragon().fire()
WhiteDragon().fire()
BlackDragon().fly()
YellowDragon().fire()
复制代码

可以看到一来没有了重复代码,二来结构也清晰了很多而且更容易扩展,Dragon 的种类和能力的组合也更加方便和清晰。extension 使得协议有了实现默认方法的能力。

关于多继承 Swift 是采用 Trait 的方式,其它语言 C++ 是直接支持多继承的,方式是这个类会持有多个父类的实例。Java 的多继承只继承能做什么,怎么做还是要自己来。和 Trait 类似的解决方案是 Mixin,Ruby 就是用的这种元编程思想。

协议还可以继承,还可以通过 & 来聚合,判断一个类是否遵循了一个协议可以使用 is 关键字。

当然协议还可以作为类型,比如一个数组泛型元素指定为一个协议,那么这个数组里的元素只要遵循这个协议就可以了。

Swift 内存管理

内存分配

Heap

在 Heap 上内存分配的时候需要锁定 Heap 上能够容纳存放对象的空闲块,主要是为了线程安全,我们需要对这些进行锁定和同步。

Heap 是完全二叉树,即除最底层节点外都是填满的,最底层节点填充是从左到右。Swift 的 Heap 是通过双向链表实现。由于 Heap 是可以 retain 和 release 所以很容易分配空间就不连续了。采用链表的目的是希望能够将内存块连起来,在 release 时通过调整链表指针来整合空间。

在 retain 时不可避免需要遍历 Heap,找到合适大小的内存块,能优化的也只是记录以前遍历的情况减少一些遍历。但是 Heap 是很大的,这样每次遍历还是很耗时,而且 release 为了能够整合空间还需要判断当前内存块的前一块和后面那块是否为空闲等,如果空闲还需要遍历链表查询,所以最终的解决方式是双向链表。只把空闲内存块用指针连起来形成链表,这样 retain 时可以减少遍历,效率理论上可以提高一倍,在 release 时将多余空间插入到 Heap 开始的位置和先前移到前面的空间进行整合。

即使效率高了但是还是比不过 Stack,所以苹果也将以前 OC 里的一些放在 Heap 里的类型改造成了值类型。

Stack

Stack 的结构很简单,push 和 pop 就完事了,内存上只需要维护 Stack 末端的指针即可。由于它的简单所以处理一些时效性不高,临时的事情是非常合适的,所以可以把 Stack 看成是一个交换临时数据的内存区域。在多线程上,由于 Stack 是线程独有的,所以也不需要考虑线程安全相关问题。

内存对齐

Swift 也有内存对齐的概念

struct DragonFirePosition {
    var x:Int64 //8 Bytes
    var y:Int32 //4 Bytes
    //8 + 4
}
struct DragonHomePosition {
    var y:Int32 //4 Bytes + 对齐内存(4 Bytes)
    var x:Int64 //8 Bytes
    //4 + 4 + 8
}
let firePositionSize = MemoryLayout<DragonFirePosition>.size //12
let homePositionSize = MemoryLayout<DragonHomePosition>.size //16
复制代码

Swift 派发机制

派发目的是让 CPU 知道被调用的函数在哪里。Swift 语言是支持编译型语言的直接派发,函数表派发和消息机制派发三种派发方式的,下面分别对这三种派发方式说明下。

直接派发

C++ 默认使用的是直接派发,加上 virtual 修饰符可以改成函数表派发。直接派发是最快的,原因是调用指令会少,还可以通过编译器进行比如内联等方式的优化。缺点是由于缺少动态性而不支持继承。

struct DragonFirePosition {
    var x:Int64
    var y:Int32
    func land() {}
}

func DragonWillFire(_ position:DragonFirePosition) {
    position.land()
}
let position = DragonFirePosition(x: 342, y: 213)
DragonWillFire(position)
复制代码

编译 inline 后 DragonWillFire(DragonFirePosition(x: 342, y: 213)) 会直接跳到方法实现的地方,结果就变成 position.land()。

函数表派发

Java 默认就是使用的函数表派发,通过 final 修饰符改成直接派发。函数表派发是有动态性的,在 Swift 里函数表叫 witness table,大部分语言叫 virtual table。一个类里会用数组来存储里面的函数指针,override 父类的函数会替代以前的函数,子类添加的函数会被加到这个数组里。举个例子:

class Fish {
    func swim() {}
    func eat() {
        //normal eat
    }
}

class FlyingFish: Fish {
    override func eat() {
        //flying fish eat
    }
    func fly() {}
}
复制代码

编译器会给 Fish 类和 FlyingFish 类分别创建 witness table。在 Fish 的函数表里有 swim 和 eat 函数,在 FlyingFish 函数表里有父类 Fish 的 swim,覆盖了父类的 eat 和新增加的函数 fly。

一个函数被调用时会先去读取对象的函数表,再根据类的地址加上该的函数的偏移量得到函数地址,然后跳到那个地址上去。从编译后的字节码这方面来看就是两次读取一次跳转,比直接派发还是慢了些。

消息机制派发

这种机制是在运行时可以改变函数的行为,KVO 和 CoreData 都是这种机制的运用。OC 默认就是使用的消息机制派发,使用 C 来直接派发获取高性能。Swift 可以通过 dynamic 修饰来支持消息机制派发。

当一个消息被派发,运行时就会按照继承关系向上查找被调用的函数。但是这样效率不高,所以需要通过缓存来提高效率,这样查找性能就能和函数派发差不多了。

具体派发

声明

值类型都会采用直接派发。无论是 class 还是协议 的 extension 也都是直接派发。class 和协议是函数表派发。

指定派发方式

  • final:让类里的函数使用直接派发,这样该函数将会没有动态性,运行时也没法取到这个函数。
  • dynamic:可以让类里的函数使用消息机制派发,可以让 extension 里的函数被 override。

派发优化

Swift 会在这上面做优化,比如一个函数没有 override,Swift 就可能会使用直接派发的方式,所以如果属性绑定了 KVO 它的 getter 和 setter 方法可能会被优化成直接派发而导致 KVO 的失效,所以记得加上 dynamic 的修饰来保证有效。后面 Swift 应该会在这个优化上去做更多的处理。

基本数据类型内存管理

通过 MemoryLayout 来看看基本数据类型的内存是占用多大

MemoryLayout<Int>.size      //8
MemoryLayout<Int16>.size    //2
MemoryLayout<Bool>.size     //1
MemoryLayout<Float>.size    //4
MemoryLayout<Double>.size   //8
复制代码

Struct 内存管理

对于 Struct 在编译中就能够确定空间,也就不需要额外的空间给运行时用,运行过程调用时就是直接传地址。

下面我们再看看 Struct 的 MemoryLayout

struct DragonFirePosition {
    var x:Int64 //8 Bytes
    var y:Int32 //4 Bytes
    //8 + 4
    func land() {}
}

MemoryLayout<DragonFirePosition>.size       //12
MemoryLayout<DragonFirePosition>.alignment  //8
MemoryLayout<DragonFirePosition>.stride     //16
复制代码

alignment 可以看出是按照 8 Bytes 来对齐的,所以这里 struct 是用到了字节对齐,实际占用大小通过 stride 可以看出就是 8 * 2 为16。

如果把 var x:Int64 改成可选类型会增加 4个 Bytes,不过就这个 case 来说实际大小还是16,这个也是因为内存对齐的原因。

Class 内存管理

Class 本身是在 Stack 上分配的,在 Heap 上还需要保存 Class 的 Type 信息,这个 Type 信息里有函数表,在函数派发时就可以按照这个函数表进行派发了。继承的话子类只要在自己的 Type 信息里记录自己的信息即可。

协议类型内存管理

协议类型的内存模型是 Existential Container。先看下面例子

protocol DragonFire {}
extension DragonFire {
    func fire() {
        print("fire!!!")
    }
}

struct YellowDragon: DragonFire {
    let eyes = "blue"
    let teeth = 48
}

let classSize = MemoryLayout<YellowDragon>.size  //32
let protocolSize = MemoryLayout<DragonFire>.size //40
复制代码

可以看出来协议要比类大。这个是由于 Existential Container 的前三个 word 是叫 Value buffer 用来存储 inline 的值。第四个 word 是 Value Witness Table,存储值的各种操作比如 allocate,copy,destruct 和 deallocate 等。第五个 word 是 Protocol Witness Table 是存储协议的函数。

泛型的内存管理

泛型采用的和 Existential Container 原理类似。Value Witness Table 和 Protocol Witness Table 会作为隐形的参数传递到泛型方法里。不过经过编译器的层层 inline 优化,最终类型都会被推导出来也就不再需要 Existential Container 这一套了。

2018-09-13 10:36:53 Caoyang_He 阅读数 1224

Swift 简介

  • 苹果公司2014年WWDC 推出 开源 语言

  • 适用于iOS7+ 及 OS X Mavericks+的应用开发

  • Swift 语言与之前的Objectiv-C可共存(可以调用OC写的代码库,嵌入OC代码之中)

  • Swift 语言不向下兼容,3.0以上逐渐稳定,相互兼容 Swift3.0 与 2.x语法不一致,不能兼容与混用

  • Swift从Python和Javascript中学了设计优点 Swift是语法类似脚本语言的编译型语言

playground - 游乐园

  • Playground是苹果公司在2014年WWDC(苹果开发者大会)

  • 随Swift一起推出 Playground支持一边写代码,一边预览效果

  • 可以看到输出的常量变量的值

  • 实时预览代码的效果

  • 优点:实时、不需要运行模拟器

Swift特点

  • 强类型安全

  • 大小写敏感

  • 结尾没有分号

  • 注释语法和 C 一样

常量、变量

  • 使用 let 来声明常量

  • 使用 var 来声明变量

  • 可以不明确声明类型,赋值时编译器会自动类型推断

var myVariable = 42
myVariable = 50
let myConstant = 42.5
  • 如果初始值没有提供足够的信息(或者没有初始值),需要在变量后面声明类型,用冒号分割
let explicitDouble : Double = 70
let stringLabel : String
  • 一个值不能隐式转换为其他类型
    如果把一个值转换成其他类型,需要显式转换
let label = "The width is"
let width = 94
let widthLabel = label + String(width)

字符串插值(String Interpolation)

使用字符串插值符号 ( ) 可以更简单的把数据值转换成字符串

let apples = 3
let oranges = 5

let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
print( "She has \(oranges) oranges.")

基本数据类型
这里写图片描述

基本运算符
这里写图片描述

数组和字典

  • 使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素,元素下标从 0 开始

  • 最后一个元素后面允许有个逗号

数组

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 = String: Float

  • 如果类型信息在添加值时可以被推断出来,则可以用[]和[:]来创建空数组和空字典

数组: let shoppingList = []
字典: let occupations = [:]

元组

  • 概念类似于python中的元组

  • 元组是多个值组合而成的复合值,用 ( ) 表示

  • 元组中的值可以是任意类型,而且每一个元素的类型可以是不同的

  • 当函数需要返回若干值时,可以利用元组一次性返回多个值

定义:let amout = (100, “EUR”)

  • 元组可以分解,可以使用下标方式来访问元组中的每个值:

let currency = amout.1 // " EUR"

  • 可以给元组每个元素命名:

let money = (amount: 100, currency: " EUR ")

  • 通过名字访问元素:

let currency = money.currency // "EUR "

  • 元组在分解时,可以只分解部分值,而忽略其它值,其它值可以用下划线_来代替:

let (currency , _) = money

元组、数组、字典特点对比

特点 元组 数组 字典
特点 综合了数组、字典、甚至 struct 的一些优点
对数据类型的一个强力补充
C 的数组只能存储多个相同类型的数据
OC,Swift 不再限定存储数据的类型
字典是 key:value 键值对,它适合将不同种类、不同用途的数据组合在一起,通过key值进行索引
优点 元组可同时存储 多种类型元素,且元素类型固定,以保证数据安全,除非定义数据类型为Any,编译器会对赋值参数类型进行检查
元组的元素个数固定,不允许增加、删除,编译器会严格校验赋值参数个数
无需定义key,但是必要时可以为数据命名,方便数据访问,适合同时遍历多元数据,例如官方文档的例子:
for (index, value) in shoppingList.enumerate() {…}
数据存储顺序固定,增删改也通过index来进行
集成了遍历方法,适合对大量同类数据的处理
不需要定义key,写法相对简单
通过key值进行索引,查找效率高
通过key值进行数据标注,可读性高,易于区分多种数据key
值唯一,增删改可以保证数据唯一性
缺点 不适合存储大量数据,因为元组不支持append、remove等方法
考虑到工程实际情况,后端使用的语言可能不支持元组,需要转换为其他格式
元组适合应用于组合少量的多元的数据
访问特定数据时,查找效率不高
处理特定数据时,需要牢记数据的index,可读性不好,容易产生错位处理
一个value必须对应一个key,尽管有时不需要key值
顺序不定,字典对key值表进行了hash,所以不方便存储对顺序敏感的数据

控制流

  • 使用 if 和 switch 来进行条件操作

  • 使用for-in、for、while和repeat-while进行循环

  • 包裹条件和循环变量括号可以省略,但是语句体必须有大括号

  • 去掉了 ++,–及for(i=0;i<x;i++)的形式,一律用for…in 代替

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

可选绑定(optional binding)

  • 使用可选绑定(optional binding)来判断可选类型是否包含值

  • 如果有值就赋值给临时常量或变量

var optionalName: String? 
var greeting = "Hello! "
optionalName = "Hillary Clinton"

if let name = optionalName  {	
	greeting = "Hello, \(name)"
else {
   print(" optionalName  is nil")
}

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.")}

while

var n = 2
while n < 100 {
    n = n * 2
}
print(n)

repeat … while
类似do…while

var m = 2
repeat {
    m = m * 2
} while m < 100
print(m)

使用 …< 创建的范围不包含上界,包含上界需要使用 …

var firstForLoop = 0
for i in 0..<4 {
	firstForLoop += i
	}

var myLoop = 0
for _ in 05 {
	 myLoop += 10
	   }

函数

  • 使用func来声明一个函数,使用名字和参数来调用函数

  • 使用->来指定函数返回值的类型

func greet(person : String, day: String) -> String { return “Hello (name), today is (day).”}

  • 调用:第一个参数名(或参数描述)必须带上

greet(person:“Bob”, day: “Tuesday”)

  • 函数默认使用参数名作标签,也可以指定参数标签,同时用 _ 表示没有参数标签

func greet( _ person : String, on day: String) -> String { return “Hello (person), today is (day).”}

  • 调用时,空标签可以省略

greet(“John”, on:“Wednesday”)

  • 使用元组返回多个值
    该元组的元素可以用名称或数字来表示
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int) 
{	
	var min = scores[0]	
	var max = scores[0]	
	for score in scores {
			if score > max {			
			max = score
							} 
			else if score < min {			
			min = score
								}
						}	
	return (min, max)
}
let statistics = calculateStatistics(scores:[5, 12, 100, 3, 9])
print(statistics. min); print(statistics.1)
  • 函数可以带有可变个数的参数
    这些参数在函数内表现为数组,可以循环遍历
func sumOf(numbers: Int...) -> Int {	
	var sum = 0
	for number in numbers {
			sum += number
						}
	return sum
	}
sumOf()
sumOf(numbers: 42, 597, 12)

Optional

Optional理解
Swift语言使用 var 定义变量,但Swift不会自动给变量赋初始值
。因此变量不会有默认值,所以使用变量之前必须对其初始化。使用变量之前不进行初始化就会编译报错。可以避免发生因为使用了未初始化的值而引发的潜在的运行时错误
可以使用optional类型来处理,即在后面跟一个?。?表示变量可能有值,也可能是 nil (空对象) – 类似NULL(空指针)。声明?之后,变量就成为了optional types(可选类型)。即对变量打包,提取值时需要拆包操作

var strValue: String? 

//判断optional是否有值,不会报错 
if strValue != nil { 
    // do what you need to do here 
} else {  //strValue is nil  
}

可选绑定

var strValue: String?if let hashValue = strValue?.hashValue  { 
    // do what you need to do here 
} else { 
    //strValue is nil
}

如果strValue == nil, 那么结果就是nil,不会调用String的hasValue方法
如果strValue != nil, 就返回strValue对应的hashValue值并赋值给常量hashValue

使用!来强制拆包(提取值),在保证有值的情况下才会这么用

var strValue: String? 
strValue = "1234" 
let integer = Int(strValue!)

更安全的写法是:
先判断是否有值(去掉定义的?再执行试试)

if strValue != nil { 
let integer = Int(strValue!)
}

Optional 本质
Optional其实是个enum(枚举),里面有None和Some两种类型
其实所谓的 nil 就是Optional.None, 非nil就是Optional.Some
通过Some(T)包装(wrap)原始值
在使用Optional的时候要拆包(从enum里取出来原始值)

闭包

  • 闭包就是在函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量
  • 从定义上看,所有的函数都可以是闭包
  • 当一个函数调用时,引用了不是自己作用域内定义的变量(通常称其为自由变量),则形成了闭包
  • 闭包是代码块和创建该代码块的上下文中数据的结合
  • 闭包形式1:函数的嵌套
    特点:闭包可以在其定义的上下文中捕获常量或变量
    用途:可以重构一个太长或者太复杂的函数
func returnFifteen() -> Int {
	var y = 10
	func add() {
		y += 5	
			}	
	add()	
	return y
	}
returnFifteen()
  • 函数是第一等类型
    可以作为另一个函数的返回值
func makeIncrementer() -> ((Int) -> Int) {
      var number = 0	
      func addOne(number: Int) -> Int {		
	      return 1 + number	
	      }	
	  return addOne}
var increment = makeIncrementer()
increment(7)

  • 闭包形式2:函数也可以当做参数传入另一个函数
    这里写图片描述
  • 函数实际上是一种特殊的闭包:它是一段能之后被调取的代码
  • 闭包是功能性自包含模块,可以在代码中被传递和使用
  • Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似
  • 闭包常用形式:
    使用 { } 来创建一个匿名闭包 – 没有 func 函数名称
    使用 in 将参数和返回值类型声明与闭包函数体进行分离
numbers.map ( {
	(number: Int) -> Int  in
			let result = 3 * number
			return result} )
  • 可以在闭包中定义参数,返回值
  • 闭包后用括号执行,并在括号中可以传参
  • Swift 自动为内联函数提供了参数名称简写功能,可以直接通过 $0,$1,$2等名字来引用的闭包的参数的值
    这里写图片描述

尾随闭包

  • 如果需要将一个很长的闭包表达式作为最后一个参数传递给函数可以使用尾随闭包来增强函数的可读性
  • 尾随闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用
func calculate(opr: String, funN:(Int, Int) -> Int) {
 // 函数体部分
} 
  • 最后一个参数funN是(Int,Int)-> Int函数类型
  • funN可以接收闭包表达式
  • 普通调用,闭包体在()内
calculate("+", funN: {(a: Int, b: Int) -> Int in return a + b })
  • 使用尾随闭包进行函数调用 1 ,闭包体在()外
calculate("+"){
(a: Int, b: Int) -> Int in 
    return a + b 
}
  • 使用尾随闭包进行函数调用 2 ,简写方式
calculate("+") { $0 + $1 }

例子

func makeIncrementor(forIncrement amount: Int) -> (() -> Int) {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}
var incrementByTen = makeIncrementor(forIncrement: 10)
print(incrementByTen())
print(incrementByTen())
print(incrementByTen())

  • makeIncrementor 返回类型为 () -> Int, 说明其返回的是函数,不是类型值
    incrementor 函数并没有带参数,但是在函数体内引用外层函数的临时变量: runningTotal 和 amount
  • 只要目标对象(incrementor
    函数)在生存期内,就能始终能保持其方法,也能间接保持外层函数体定义的临时变量值
    尽管外层函数调用已经结束,临时变量的名称也消失,但在目标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问
  • 即使再次调用相同的外层函数,但只会生成新对象和方法,新的临时变量只是对应新的值,和上次那次调用的是各自独立的
  • Swift 会决定捕获引用还是拷贝值
  • 如果一个值没有改变或者在闭包的外面,Swift 可能会使用这个值的拷贝而不是捕获
  • Swift 同时也处理 runingTotal 变量的内存管理操作,如果不再被 incrementor 函数使用,临时变量会被清除
  • 如果创建了另一个 incrementor,其会有一个属于自己的独立的 runningTotal 变量的引用。

let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
incrementByTen()

上课时的代码

变量,常量

// ---------------------------------------
// 课堂演示2.2 Swift基础(1):创建命令行工程。
// 内容:创建一个OS X命令行项目,生成并运行。
print("Hello, My First Swift Project!")



// ---------------------------------------
// 课堂练习2.2 Swift基础(2):语句与输出。
// 内容:输入字符串“我是xxxx,我喜欢iOS开发”。
print("我是李老师,我喜欢iOS开发")



// ---------------------------------------
// 课堂演示2.2:Swift基础(3):变量与常量。
// 内容:创建一个常量,常量名为PI,初始值为3.14。创建一个变量,变量名为r,显式指定类型为Double并指定初始值为10。
let PI = 3.14           // let定义常量,自动推断类型为Double
var radius:Double = 10  // var定义变量,显式指定数据类型为Double



// ---------------------------------------
// 课堂演示2.2:Swift基础(4):元组。
// 内容:使用元组存储你的信息,并输出。
// 1) 定义元组
let myInfo1 = ("liwei", 35)                     // 定义元组
let myInfo2: (String, Int) = ("liwei", 35)
var myInfo3 = (name:"liwei", age:35)            // 定义元组:指定元素名称

// 2)  修改元组
myInfo3.name = "Yu"
myInfo3.age = 60

// 3) 使用元组
print("My name is \(myInfo1.0) and my age is \(myInfo1.1)")
print("My name is \(myInfo2.0) and my age is \(myInfo2.1)")
print("My name is \(myInfo3.name) and my age is \(myInfo3.age)")



// ------------------------------------------
// 课堂演示2.2:Swift基础(5):可选。
// 内容:编写可选代码,理解Swift可选机制。
var possibleInt: Int? = 0           // 定义可选变量

var myString = "7"
possibleInt = Int(myString)
print(possibleInt)

myString = "banana"
possibleInt = Int(myString)
print(possibleInt)



// ----------------------------------------------
// 课堂演示2.2:Swift基础(6):运算符。
// 内容:给定半径,计算圆形面积和球形体积。
var r = 10.0
var area = PI*r*r
var volume = 4/3*PI*r*r*r
print("The area is \(area), and the volume is \(volume)")

控制流

import Foundation

// 课堂演示2.3:控制流。
// 内容(1):计算1-100之和
// 内容(2):计算13的阶乘
// 内容(3):计算100以内的斐波那契数列

/////////////////////////////////////
// 1. 计算1-100之和
var sum = 0
var index = 1

while index <= 100 {
    sum += index
    index += 1
}

print("0-100之和是:\(sum)")




/////////////////////////////////////
// 2. 计算13的阶乘
var result = 1
var n = 13

while n > 0 {
    result *= n
    n -= 1
}

print("13的阶乘是:\(result)")




//////////////////////////////////////
// 2. 计算100以内的斐波那契数列
var value1 = 1
var value2 = 1
var value3 = 0

var str = "1 1 "

while true {
    value3 = value1 + value2
    
    if value3 < 100 {
        str += "\(value3) "
        value1 = value2
        value2 = value3
    } else {
        break
    }
}

print("100以内斐波那契数列是:\(str)")

复杂数据运算

import Foundation

// 课堂演示2.4 复杂数据类型(1):数组。
// 内容:创建字符串数组,存入一些动物名称,进行数组常用操作练习

// 创建数组
var array = [String]()          // 常用
var array2 = Array<String>()
var array3 = ["小猫", "小狗", "小鸟"]

//判断数组是否为空
print(array.isEmpty)

//添加数组元素
array.append("小猫")
array.append("小狗")
array += ["小鸟"]
print(array)

//判断数组是否为空
print(array.isEmpty)

//在指定位置添加元素
array.insert("小鱼", at: 1)
print(array)

//数组元素个数
print(array.count)

//数组遍历
for item in array {
    print(item)
}

//删除最后一个
array.removeLast()
print(array)

//删除指定位置元素
array.remove(at: 1)
print(array)

//删除所有元素
array.removeAll()
print(array.isEmpty)
print(array)



// -----------------------------------------------------
// 课堂演示2.4 复杂数据类型(1):数组。
// 内容(1):使用字典存储水果名称及其价格
// 内容(2):使用字典存储我们主要城市的电话区号
///////////////////////////////////////////
// 1. 使用字典存储水果名称及其价格

//创建字典
var dic = ["苹果":6.5, "葡萄":5.0, "桔子":3.0]
print(dic)


// 增加元素
dic["哈密瓜"] = 7.5
print(dic)

// 删除元素
dic.removeValue(forKey: "苹果")
print(dic)

// 遍历所有key
for key in dic.keys {
    print(key)
}

// 遍历字典
for (key, value) in dic {
    print("\(key)的价格是\(value)元")
}

函数

import Foundation

//////////////////////////////////////////////
// 1. 函数练习:编写函数,计算两个整数之和
func add(number1 n1:Int, number2 n2:Int)->Int {
    return n1+n2
}

print(add(number1: 1, number2: 2))


//////////////////////////////////////////////
// 2. 函数练习:计算数组中所有大于10的数的平均值
func average(arr:[Double])->Double{
    var count = 0
    var sum:Double = 0.0
    
    for item in arr {
        if item > 10 {
            sum += item
            count += 1
        }
    }
    
    return sum / Double(count)
}

var arr:[Double] = [5, 10, 20]

print(average(arr: arr))





//////////////////////////////////////////////
// 3. 函数练习:练习函数嵌套
func evenNumber(threshold: Int) {
    var number = 0
    func add2() {
        number += 2
    }
    
    while number < threshold {
        print(number)
        add2()
    }
}

evenNumber(threshold: 15)



//////////////////////////////////////////////
// 4. 函数练习:函数数据类型
func getSmaller(number1: Int, number2: Int)->Int {
    return ((number1 < number2) ? number1:number2)
}
func getBigger(number1: Int, number2: Int)->Int {
    return ((number1 > number2) ? number1:number2)
}
func printMathResult(mathFunction: (Int, Int)->Int, num1: Int, num2: Int) {
    print("The result is: \(mathFunction(num1, num2))")
}

printMathResult(mathFunction: getSmaller, num1: 1, num2: 2)
printMathResult(mathFunction: getBigger, num1: 1, num2: 2)


/////////////////////////////////////////////
// 5. 函数作为返回值
func makeIncrementer()->((Int)->Int){
    func addOne(number:Int)->Int{
        return number+1
    }
    return addOne
}

var increment = makeIncrementer()
print(increment(7))

自己的代码

import Foundation
///////////////////////////////////////////////////

func calcMaxMin(values: [Int])->(max:Int,min:Int)
{
   var max = values[0]
   var min = values[1]

   for v in values
   {
       if v > max
       {
           max = v
       }
       if v < min
       {
           min = v
       }
   }
   return (max,min)

}

let values=[1,2,3,4,5,6]
let maxmin  = calcMaxMin(values: values)
print("max value is \(maxmin.max),min value is \(maxmin.min)")

///////////////////////////////////////////////////

func oushu(threshold: Int)
{
   var number = 0
   func add2()
   {
       number += 2
   }

   while number < threshold
   {
       print(number)
       add2()
   }
}

oushu(threshold: 100)

///////////////////////////////////////////////////

func mathResult(mathFunc:(Int, Int)->Int, num1: Int, num2: Int) -> Int {
   return mathFunc(num1 , num2)
}

func getSmaller(num1: Int ,num2: Int) -> Int {
   return(num1 < num2) ? num1 : num2
}

func addFunction(num1: Int,num2: Int) -> Int {
   return num1+num2
}

var rst = mathResult(mathFunc:getSmaller,num1:1, num2:2)
print(rst)
var rst1 = mathResult(mathFunc:addFunction,num1:1,num2:2)
print(rst1)

///////////////////////////////////////////////////

func makeIncrementer()->((Int)->Int)
{
   func add1(num: Int)->Int
   {
       return num+1
   }
   return add1
}

var incrementer = makeIncrementer()
var rst = incrementer(7)
print(rst)

///////////////////////////////////////////////////

var b1 = {
   print("这是闭包")
}
b1()

var b2 = {
   (param: String) in
   print("闭包参数:\(param)")
}
b2("Thrinity")

var b3 = {
   (param: String) -> String in
   return "闭包参数:\(param)"
}
print(b3("Thrinity"))

var b4 = {
   (p1: String, p2:String)->String in
   return p1+p2
}("Holle","World")
print(b4)

var s1 = {
   (p1, p2) in
   return p1+p2
}("Holle","World")
print(s1)

var s2 = {
   (p1, p2) in
    p1+p2
}("Holle","World")
print(s2)

var s3 = {
   $0+$1
}("Holle","World")
print(s3)

///////////////////////////////////////////////////

func myOperation(num1: Int,num2: Int, operation:(Int,Int)->int)->int{
   let rst = operation(num1,num2)
   return rst
}

let multipleClosure = {
   (a:Int ,b:Int)->Int in
   return a*b
}

let multipleClosure:(Int , Int)->Int = {$0*$1}

var rst = myOperation(num:1,num:2,operation:multipleClosure)

print(rst)

var rst1 = myOperation(num1:3,num2:2,operation:{(a,b)->Int in return a*b} )

var rst2 = myOperation(num1:3,num2:2){$0*$1}

///////////////////////////////////////////////////

let names=["Thrinity","Jackma","Bob","ZXC","W"]
let rst = names.sorted()
print(rst)

let rst1 = names.sorted{
   $0.count > $1.count
}
print(rst1)

///////////////////////////////////////////////////

enum WeekDay{
   case Mon
   case Tue
   case Wen
   case Thu
   case Fri

   func description(){
       switch self {
       case .Mon:
           print("星期一")
       case .Fri:
           print("星期五")
       default:
           print("I don't know")
       }
   }
}
var day = WeekDay.Mon
print(day)
day.description()

///////////////////////////////////////////////////



2016-08-16 18:26:00 weixin_33676492 阅读数 0

前言

    public struct Array<Element> : CollectionType, MutableCollectionType, _DestructorSafeContainer
    public class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration
  • 在 Swift 语言中,数据在被存储进某个数组之前,类型必须明确,且与数组其他元素类型相同。在 Swift 语言中的数组和 C 语言中的不同,Swift 语言中的数组是值类型的,不管是赋值还是当作参数传递,都只是维护他的拷贝。你可以放心的操作他的拷贝,因为你做的任何操作都不会反馈回它本身,这是值类型自身的规则。

1、Array 与 NSArray 的相互转换

  • Array 与 NSArray 之间可以相互转换。Array 转成 NSArray 时,数组中的各项元素被转换为 AnyObject 类型。NSArray 转换成 Array 时,数组中的各项元素也被转换为 AnyObject 类型。
    // Array 转 NSArray
    
        // Array 转换成 NSArray<AnyObject> 型
        let nsArray1:NSArray = array
    
        // Array 转换成 NSArray<AnyObject> 型
        let nsArray2:NSArray = array as NSArray
        
    // NSArray 转 Array
    
        // NSArray 转换成 Array<AnyObject> 型
        let swiftArray1:Array = nsArray1 as Array
    
        // NSArray 转换成 Array<AnyObject> 型
        let swiftArray2:Array = nsArray1 as [AnyObject]
    
        // NSArray 转换成 Array<Int> 型
        let swiftArray3:Array = nsArray1 as! [Int]

2、数组的 创建

    // Array 型数组
            
        // 不赋初值,指定数组内数据为 String 型
        let array1 = [String]()
        
        // 不赋初值,Array<String> 等价于 [String]
        let array2 = Array<String>()
        
        // 不指定数组内数据类型,自动推断
        let array3:Array = ["bei", "jing"]
        
        // 指定数组内数据类型为 String 型
        let array4:Array<String> = ["huan", "ying", "ni"]
        
        // 不指定数组内数据类型,自动推断
        let array5 = ["bei", "jing", "huan", "ying", "ni"]
        
        // 指定数组内数据类型为 Int 型
        let array6:[Int] = [1, 2, 3, 4, 5, 6]
        
        // 指定数组内数据类型为 AnyObject 型
        let array7:[AnyObject] = [1, 2, 3, 4, 5, 6, "happy"]
        
        // 创建二维数组
        let array8:Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
        
    // NSArray 型数组
            
        let swiftArray:Array = ["bei", "jing", "nin", "hao"]
            
        let nsArray1:NSArray = swiftArray
        let nsArray2:NSArray = swiftArray as NSArray
            
        let nsArray3 = NSArray()
            
        // array: [AnyObject]
        let nsArray4 = NSArray(array: array7)
        
        // array: [AnyObject]
        let nsArray6 = NSArray(array: ["bei", "jing", "huan", "ying", "ni"])
        
        // array: NSArray
        let nsArray5 = NSArray(array: nsArray2)
        
        // object: AnyObject
        let nsArray7 = NSArray(object: "qian")
        
        // objects: AnyObject...
        let nsArray8 = NSArray(objects: "bei", "jing", "huan", "ying", "ni")
        
        // 从 文件 创建字符串
        let nsArray9:NSArray? = NSArray(contentsOfFile: NSHomeDirectory() + "/Desktop/test.txt")
        
        // 从 Url 创建字符串
        let nsArray10:NSArray? = NSArray(contentsOfURL: NSURL(fileURLWithPath: NSHomeDirectory() + "/Desktop/test.txt"))

3、数组的 长度计算

    // Array 或 NSArray 型数组
    
        let array = ["bei", "jing", "huan", "ying", "ni"]
            
        let num:Int = array.count

4、数组位置的 获取

    // Array 型字符串
            
        let array = ["bei", "jing", "huan", "ying", "ni"]
            
        // 获取起始位置,即 0
        let startIndex = array.startIndex
        
        // 获取结束位置,指 数组最后一个元素的位置
        let endIndex = array.endIndex
        
        // 获取指定位置下标值
        let index = array.startIndex.advancedBy(2)
        
        // 获取下标区间值
        let range = array.startIndex.advancedBy(4)...array.startIndex.advancedBy(6)
        
        // 获取指定位置下标的 前一个值
        let predecessorIndex = array.startIndex.advancedBy(2).predecessor()
        
        // 获取指定位置下标的 后一个值
        let successorIndex = array.startIndex.advancedBy(2).successor()

5、数组元素的 获取

    // Array 型数组
            
        let array:Array = ["bei", "jing", "huan", "ying", "ni"]
            
        // 获取数组指定下标的元素
        let obj1:String = array[1]
        
        // 获取数组的第一个元素
        let obj2:String? = array.first
        
        // 获取数组的最后一个元素
        let obj3:String? = array.last
        
        // 获取数组指定范围内的元素
        let obj4 = array[2...4]

    // NSArray 型数组
            
        let nsArray:NSArray = ["bei", "jing", "huan", "ying", "ni"]
            
        // 获取指定下标元素,NSArray 中数据类型为 AnyObject
        let obj5:String = nsArray[2] as! String
        
        // 获取数组指定下标的元素
        let obj6:String = nsArray.objectAtIndex(2) as! String
        
        // 获取数组的第一个元素
        let obj7:String = nsArray.firstObject as! String
        
        // 获取数组的最后一个元素
        let obj8:String = nsArray.lastObject as! String
        
        // 获取数组指定范围内的元素
        let obj9 = nsArray.objectsAtIndexes(NSIndexSet(indexesInRange: NSMakeRange(2, 2)))
        
        // 获取数组指定范围内的元素
        let obj10 = nsArray.subarrayWithRange(NSMakeRange(2, 3))

6、数组下标的 获取

    // Array 型数组
            
        let array:Array = ["bei", "jing", "huan", "ying", "ni"]
            
        // 获取指定元素的下标,若元素不存在,返回 nil
        let indexNum1:Int? = array.indexOf("huan")
            
    // NSArray 型数组
            
        let nsArray:NSArray = ["bei", "jing", "huan", "ying", "ni"]
            
        // 获取指定元素的下标,若元素不存在,返回 Int.max
        let indexNum2:Int = nsArray.indexOfObject("hun")

7、数组的 判断

    // Array 型数组
            
        let arr1:Array = [1, 3, 5, 8]
        let arr2:Array = [1, 3, 7, 8]
            
        // 判断两个数组是否相等
        let bl1:Bool = arr1 == arr2
        
        // 判断数组是否为空
        let bl2:Bool = arr1.isEmpty
        
        // 判断数组中是否存在指定的元素
        let bl3:Bool = arr1.contains(55)
            
    // NSArray 型数组
            
        let nsArr1:NSArray = [2, 3, 5, 8]
        let nsArr2:NSArray = [1, 4, 7, 9]
            
        // 判断两个数组是否相等
        let bl4:Bool = nsArr1.isEqualToArray(nsArr2 as [AnyObject])
        
        // 判断数组中是否存在指定的元素
        let bl5:Bool = nsArr1.containsObject(7)
        
        // 返回两个数组中第一个相同的元素,没有时返回 nil
        let firstObj:AnyObject? = nsArr1.firstObjectCommonWithArray(nsArr2 as [AnyObject])

8、数组元素的组合

    // NSArray 型数组
            
        let arr1:NSArray = ["bei", "jing", "huan", "ying", "nin"]
        let arr2:NSArray = ["Users", "JHQ0228", "Desktop"]
            
        // 按指定字符组合
        let str1:String = arr1.componentsJoinedByString(" ")
        
        // 按路径组合
        let str2:String = NSString.pathWithComponents(arr2 as! [String])

9、数组元素的 追加

    // Array 型数组
            
        var arr1:Array = ["bei", "jing"]
            
        // 使用 "+" 号连接两个数组
        let arr2 = arr1 + ["huan", "ying", "ni"]
        
        // 在数组末尾追加一个元素
        arr1.append("hao")
        
        // 在指定位置插入一个元素
        arr1.insert("ni", atIndex: 2)
            
    // NSArray 型数组
            
        let nsArray:NSMutableArray = ["jing", "huan"]
            
        // 向数组添加一个元素,返回一个新的数组
        let nsArray1 = nsArray.arrayByAddingObject("ma")
        
        // 在数组末尾追加一个元素
        nsArray.addObject("ni")
        
        // 在数组末尾追加一个数组
        nsArray.addObjectsFromArray(["ni", "hao"])
        
        // 在指定位置追加一个元素
        nsArray.insertObject("bei", atIndex: 0)

10、数组元素的 删除

    // Array 型数组
            
        var arr1:Array = ["bei", "jing", "huan", "ying", "ni"]
            
        // 删除指定位置的数组元素
        arr1.removeAtIndex(3)
        
        // 删除数组中的最后一个元素
        arr1.removeLast()
        
        // 删除指定范围内的数组元素
        arr1.removeRange(0...1)
        
        // 删除所有数组元素
        arr1.removeAll(keepCapacity: true)
            
    // NSArray 型数组
            
        let nsArr1:NSMutableArray = ["nan", "jing", "huan", "ying", "nin", "bei", "jing", "ni", "hao"]
            
        // 删除指定下标的元素
        nsArr1.removeObjectAtIndex(2)
        
        // 删除指定元素,删除所有指定元素
        nsArr1.removeObject("ying")
        
        // 删除指定元素
        nsArr1.removeObjectIdenticalTo("nan")
        
        // 删除最后一个元素
        nsArr1.removeLastObject()
        
        // 删除指定范围的元素
        nsArr1.removeObjectsInRange(NSMakeRange(2, 1))
        
        // 删除所有元素
        nsArr1.removeAllObjects()

11、数组元素的 替换

    // NSArray 型数组
            
        let nsArr:NSMutableArray = ["nan", "jing", "huan", "ying", "nin"]
            
        // 替换指定下标元素
        nsArr.replaceObjectAtIndex(0, withObject: "bei")
        
        // 替换的元素个数与被替换的元素个数需相等
        nsArr.replaceObjectsAtIndexes(NSIndexSet(indexesInRange: NSMakeRange(2, 3)), 
                          withObjects: ["ni", "hao", "ma"])
        
        // 用数组替换
        nsArr.replaceObjectsInRange(NSMakeRange(2, 3), withObjectsFromArray: ["huan", "ying"])
            
        nsArr.replaceObjectsInRange(NSMakeRange(0, 2), 
               withObjectsFromArray: ["huan", "ying", "nin", "nan", "jing", "huan"], 
                              range: NSMakeRange(3, 2))

12、数组元素的 交换

    // NSArray 型数组
    let nsArr:NSMutableArray = ["bei", "jing", "huan", "ying", "nin"]
        
    nsArr.exchangeObjectAtIndex(2, withObjectAtIndex: 4)

13、数组元素的 修改

    // Array 型数组
        
        var arr:Array = ["bei", "jing", "huan", "ying", "ni"]
            
        arr[0] = "nan"
            
        // NSArray 型数组
        let nsArr:NSMutableArray = ["bei", "jing", "huan", "ying", "nin"]
            
        // 修改数组中的某个元素
        nsArr[4] = "ni"
        
        // 修改整个数组(覆盖重写)
        nsArr.setArray(["ni", "hao"])

14、数组元素的 过滤

    // Array 型数组
            
        let arr:Array = [1, 9, 2, 8, 45]
            
        let array:[Int] = arr.filter { (obj:Int) -> Bool in
            
            // 过滤掉不能被 3 整除的数
            return obj % 3 == 0
        }
            
    // NSArray 型数组
            
        let nsArr:NSMutableArray = [1, 9, 2, 8, 45]
            
        let nsArray:[AnyObject] = nsArr.filter { (obj:AnyObject) -> Bool in
            
            // 过滤掉不能被 3 整除的数
            return (obj as! Int) % 3 == 0
        }

15、数组的遍历

  • 用 for...in 循环遍历

        // Array 或 NSArray 型数组
    
            let arr = ["bei", "jing", "huan", "ying", "ni"]
    
            // tmp 无需定义,在 Swift 2.2 中 C 语言样式的 for 循环语句被废弃
            for tmp in arr {
    
                print(tmp)
            }
  • 用闭包循环遍历

        // NSArray 型数组
    
            let arr:NSArray = ["bei", "jing", "huan", "ying", "ni"]
    
            arr.enumerateObjectsUsingBlock { (obj:AnyObject, idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in
    
                print(obj)
    
                if obj.isEqualTo("huan") {
    
                    // 停止继续遍历
                    stop.initialize(true)
                }
            }
  • 用迭代器遍历

        // NSArray 型数组
    
            let arr:NSArray = ["bei", "jing", "huan", "ying", "ni"]
    
            // 正序遍历
            let enu1:NSEnumerator = arr.objectEnumerator()
    
            // 反序遍历
            let enu2:NSEnumerator = arr.reverseObjectEnumerator()
    
            // AnyObject 后不要加 ?,否则会导致一直循环
            while let obj:AnyObject = enu1.nextObject() {
    
                print(obj)
            }
  • 条件遍历

        // NSArray 型数组
    
            let arr:NSArray = ["bei", "jing", "huan", "ying", "huan", "ni"]
    
            // 1. indexesOfObjectsPassingTest 找出数组中所有满足条件的值
    
                let indexSet:NSIndexSet = arr.indexesOfObjectsPassingTest { (obj:AnyObject, 
                                                                             idx:Int, 
                                                                            stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
    
                    // 条件满足时返回满足的数组元素的下标
                    return obj.isEqualTo("huan") ? true : false
                }
    
                indexSet.enumerateIndexesUsingBlock { (idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in
    
                    print(idx)
                }
    
            // 2. indexOfObjectPassingTest 找出数组中第一个满足条件的值
    
                let index:Int = arr.indexOfObjectPassingTest { (obj:AnyObject, 
                                                                idx:Int, 
                                                               stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
    
                    // 条件满足时返回满足的数组元素的下标
                    return obj.isEqualTo("huan") ? true : false
                }
    
                print(index)

16、数组的排序

  • 冒泡排序

        // NSArray 型数组
    
            let array:NSMutableArray = ["bei", "jing", "huan", "ying", "ni"]
    
            for i in 0 ..< array.count - 1 {
                for j in 0 ..< array.count - 1 - i {
    
                    // 大小判断 升序
                    if (array[j] as! String).compare(array[j + 1] as! String) == NSComparisonResult.OrderedAscending {
    
                        // 位置交换
                        array.exchangeObjectAtIndex(j, withObjectAtIndex: j + 1)
                    }
                }
            }
            print(array)
  • 用闭包排序

        // Array 型数组
    
            // 1. 利用闭包进行排序,可变数组排序
    
                var varArray:Array = ["sunday", "sunny", "summer", "sun"];
    
                varArray.sortInPlace { (obj1:String, obj2:String) -> Bool in
    
                    return obj1 < obj2
                }
    
                print(varArray)
    
            // 2. 利用闭包进行排序,不可变数组排序
    
                let letArray:Array = ["sunday", "sunny", "summer", "sun"];
    
                let array = letArray.sort { (obj1:String, obj2:String) -> Bool in
    
                    return obj1 < obj2
                }
    
                print(array)
    
        // NSArray 型数组
    
            // 1. 利用闭包进行排序,可变数组排序
    
                let nsMArray1:NSMutableArray = ["sunday", "sunny", "summer", "sun"];
    
                nsMArray1.sortUsingComparator { (obj1:AnyObject, obj2:AnyObject) -> NSComparisonResult in
    
                    let result:NSComparisonResult = (obj1 as! String).compare(obj2 as! String)
    
                    return result
                }
    
                print(nsMArray1)
    
            // 2. 利用闭包进行排序,不可变数组排序
    
                let nsArray1:NSArray = ["sunday", "sunny", "summer", "sun"];
    
                let array1:NSArray = nsArray1.sortedArrayUsingComparator { (obj1:AnyObject, 
                                                                            obj2:AnyObject) -> NSComparisonResult in
    
                    let result:NSComparisonResult = (obj1 as! String).compare(obj2 as! String)
    
                    return result
                }
    
                print(array1)
  • 用指定的方法排序

        // Array 型数组
    
            // 1. 用指定的方法排序,可变数组排序
    
                var varArray:Array = ["sunday", "sunny", "summer", "sun"];
    
                // 直接使用 Swift 数组类型中定义的小于号 (<) 实现,public func <(lhs: String, rhs: String) -> Bool
                varArray.sortInPlace( < )
    
                print(varArray)
    
            // 2. 用指定的方法排序,不可变数组排序
    
                let letArray:Array = ["sunday", "sunny", "summer", "sun"];
    
                // 直接使用 Swift 数组类型中定义的小于号 (<) 实现,public func <(lhs: String, rhs: String) -> Bool
                let array = letArray.sort( < )
    
                print(array)
    
        // NSArray 型数组
    
            // 1. 用指定的方法排序,可变数组排序
    
                let nsMArray1:NSMutableArray = ["sunday", "sunny", "summer", "sun"];
    
                // 原数组的顺序改变,指定元素的比较方法:compare:,默认为升序排列,
                // #selector(NSNumber.compare(_:)) 代替 Selector("compare:")
                nsMArray1.sortUsingSelector(#selector(NSNumber.compare(_:)))
    
                print(nsMArray1)
    
                // 使排序结果 降序 排列
                let enu:NSEnumerator = nsMArray1.reverseObjectEnumerator()
    
                while let obj:AnyObject = enu.nextObject() {
                    print(obj)
                }
    
            // 2. 用指定的方法排序,不可变数组排序
    
                let nsArray1:NSArray = ["sunday", "sunny", "summer", "sun"];
    
                // 返回一个排好的数组,原数组的顺序不变,指定元素的比较方法:compare:
                let array1:NSArray = nsArray1.sortedArrayUsingSelector(#selector(NSNumber.compare(_:)))
    
                print(array1)
    
            // 3. 用指定的方法排序,自定义类
    
                // Student.swift
    
                    class Student: NSObject {
    
                        var firstName:String
                        var lastName:String
    
                        init(firstName:String, lastName:String ) {
    
                            self.firstName = firstName
                            self.lastName = lastName
                        }
    
                        func compareStudent(stu:Student) -> NSComparisonResult {
    
                            // 先按照姓排序
                            var result:NSComparisonResult = self.lastName.compare(stu.lastName)
    
                            if result == NSComparisonResult.OrderedSame {
    
                                // 如果有相同的姓,就比较名字
                                result = self.firstName.compare(stu.firstName)
                            }
                            return result;
                        }
    
                        // 需遵守协议 Printable
                        override var description: String{
    
                            return String(format: "%@, %@", self.lastName, self.firstName)
                        }
                    }
    
                // main.swift
    
                    let stu1:Student = Student(firstName: "MingJie", lastName: "Li")
                    let stu2:Student = Student(firstName: "LongHu", lastName: "Huang")
                    let stu3:Student = Student(firstName: "LianJie", lastName: "Li")
                    let stu4:Student = Student(firstName: "Jian", lastName: "Xiao")
    
                    // 用指定的方法排序,可变数组排序
    
                        let nsMArray2:NSMutableArray = NSMutableArray(objects: stu1, stu2, stu3, stu4)
    
                        // 代替 Selector("compareStudent:")
                        nsMArray2.sortUsingSelector(#selector(Student.compareStudent(_:)))
    
                        print(nsMArray2)
    
                    // 用指定的方法排序,不可变数组排序
    
                        let nsArray2:NSArray = NSArray(objects: stu1, stu2, stu3, stu4)
    
                        let array2:NSArray = nsArray2.sortedArrayUsingSelector(#selector(Student.compareStudent(_:)))
    
                        print(array2)
  • 按描述器排序

        // Student.swift
    
            class Student: NSObject, Printable {
    
                var firstName:String
                var lastName:String
    
                init(firstName:String, lastName:String ) {
    
                    self.firstName = firstName
                    self.lastName = lastName
                }
    
                override var description: String{
    
                    return String(format: "%@, %@", self.lastName, self.firstName)
                }
            }
    
        // main.swift
    
            let stu1:Student = Student(firstName: "MingJie", lastName: "Li")
            let stu2:Student = Student(firstName: "LongHu", lastName: "Huang")
            let stu3:Student = Student(firstName: "LianJie", lastName: "Li")
            let stu4:Student = Student(firstName: "Jian", lastName: "Xiao")
    
            // 先按照姓进行排序
            let lastnNmeDesc:NSSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true)
    
            // 再按照名进行排序
            let firstNameDesc:NSSortDescriptor = NSSortDescriptor(key: "firstName", ascending: true)
    
            // 1.按描述器排序,可变数组排序
    
                let array1 = NSMutableArray(objects: stu1, stu2, stu3, stu4)
    
                // 按顺序添加排序描
                let descs1 = NSArray(objects: lastnNmeDesc, firstNameDesc)
    
                array1.sortUsingDescriptors(descs1 as! [NSSortDescriptor])
    
                print(array1)
    
            // 2.按描述器排序,不可变数组排序
    
                let array2 = NSArray(objects: stu1, stu2, stu3, stu4)
    
                // 按顺序添加排序描
                let descs2 = NSArray(objects: lastnNmeDesc, firstNameDesc)
    
                let array3 = array2.sortedArrayUsingDescriptors(descs2 as! [NSSortDescriptor])
    
                print(array3)
2016-09-23 16:30:19 rigel_xu 阅读数 0

使用SystemConfiguration Api 可以获取当前连接的 Wifi SSID、BSSID信息,swift3.0的写法如下:

首先要导入SystemConfiguration.framework,然后导入头文件:

import SystemConfiguration
import SystemConfiguration.CaptiveNetwork

方法:

func getUsedSSID() -> String {
    let interfaces = CNCopySupportedInterfaces()
    var ssid = ""
    if interfaces != nil {
        let interfacesArray = CFBridgingRetain(interfaces) as! Array<AnyObject>
        if interfacesArray.count > 0 {
            let interfaceName = interfacesArray[0] as! CFString
            let ussafeInterfaceData = CNCopyCurrentNetworkInfo(interfaceName)
            if (ussafeInterfaceData != nil) {
                let interfaceData = ussafeInterfaceData as! Dictionary<String, Any>
                ssid = interfaceData["SSID"]! as! String
            }
        }
    }
    return ssid
}

如果没有连接 wifi 返回 nil。能获取到的信息有:

▿ 3 elements
▿ 0 : 2 elements
    - .0 : "SSID"
    - .1 : PA_WLAN_MA
▿ 1 : 2 elements
    - .0 : "BSSID"
    - .1 : 24:de:c6:cf:8a:fd
▿ 2 : 2 elements
    - .0 : "SSIDDATA"
    - .1 : <50415f57 4c414e5f 4d41>

可能有项目需要获取 Wifi 列表,这在 iOS9 以前是需要使用私有 Api 的,iOS9以后,有更好的方式来处理,参考iOS 9 NetworkExtension使用

2019-01-28 15:16:19 Bolted_snail 阅读数 3385

1.print(str)

  • print函数能够将表达式的结果输出到控制台,类似C的printf函数和OC的NSLog函数。
  • print函数定义:
public func print(_ items: Any..., //任意个数,任何类型的参数它们将输出到控制台
 separator: String = default,//输出多个参数之间的分隔符
  terminator: String = default)//输出字符串之后的结束符号

参数separatorterminator都可以省略,separator只有字输出数据大于1时才有意义。

  • 示例:
print("a","b","c","d","e", separator: "|", terminator: "-")

打印结果为a|b|c|d|e-,用|将打印的字母分开,用-来结束打印。

  • 自定义打印日志:
//自定义log p28
func hjLog(mes:Any){
    print("file:\(#file) column:\(#column)  line:\(#line) \(mes)")
}

由于Swift中不能定义宏,只能定义一个打印日志的方法,上面方法打印文件路径,行数,及要打印的内容,可以方便的定位到打印的位置。

2.浮点型取余

  • Swift中允许浮点型取余,但是在Swift3之后取余运算符%不能应用于浮点数运算,需要使用public func truncatingRemainder(dividingBy other: Double) -> Double方法来计算浮点型取余。
var c = 19.22
//浮点型取余 
let d = c.truncatingRemainder(dividingBy: 6.1)

3.Swift中的数据类型

  • 根据这些类型在赋值或给函数传递时的方式,可分为值类型引用类型值类型就是创建一个副本,把副本赋值或传递过去,这样在函数的调用过程中不会影响原始数据;引用类型就是把数据本身的引用(即指针)赋值或传递过去在函数的调用过程中会影响原始数据。
  • Swift中整型、浮点型、布尔型、字符型、字符串、元组、集合、枚举。结构体值类型,而类属于引用类型。如Swift中String是值类型、NSString是引用类型。
  • 整型、浮点型、布尔型、字符型、字符串、元组、集合等类型本质上都是结构体类型,结构体有构造函数,通过构造函数创建并初始化实例。
  • 用于判断的几种方式:
    1. ==比较基本类型的值是否相等
    2. ===比较对象类型,是不是同一个对象
    3. is判断某个实例是否为某种类型
    4. as强制类型转换

4.Swift中的for循环

  • Swift3之后C语言风格的for语句不再使用, Swift3之后for语句只能与in关键字结合使用。
  • 示例1:只需要知道连续区间的值(只需要循环变量)
//打印1到9平方的值
for i in 1..<10{
    print("\(i)x\(i)=\(i*i)")
}
  • 示例2:只需要连续获取集合元素的值(不需要循环变量)
//打印集合元素
let nums = [1,2,3,4,5,6,7,8,9]
for item in nums {
    print(item)
}
  • 示例3:既需要集合元素的值,又需要脚标(需要循环变量),可以使用结合的enumerated()方法
let nums = [1,2,3,4,5,6,7,8,9]
for(index,item) in nums.enumerated(){
    print("\(index):\(item)")
}
  • Swift中的区间分为两种,全闭合区间...和左闭右开区间..<

5. break语句

  • break语句可用于循环语句结构,(Swift中的循环语句有while、repeat-while、for ),作用是强制退出循环结构,不执行循环结构中的剩余语句。由于Swift的选择switch默认会添加break,所以一般不需要收到添加break,但添加上也不会出问题。
for i in 1...10{
    print("\(i)x\(i)=\(i*i)")
    if i == 5{
        break;
    }
}
  • break可以配合标签使用
label1: for x in 0..<5 {
    label2: for y in (1...5).reversed(){
        if x == y {
            break label1
        }
        print("(x,y)=(\(x),\(y))")
    }
}

默认情况下break只会跳出最近的内循环,而示例中条件成立时直接跳出循环,给外循环添加了一个标签label1,然后break后面指定这个标签,当条件成立就跳出该标签的外循环。reversed()是反向变量区间。

6.Set集合

  • Swift集合有数组、字典、Set集合数组是一组有序的由相同类型元素构成的集合;字典由两部分组成,一个键集合,一个值集合,键集合不能有重复元素,而值集合可以重复,键值成对出现;Set集合是由一串无序的,不能重复的相同类型元素构成的集合。
  • 与OC任何对象类型不同,三种集合都强调是元素的类型一致,但这里的类型指的是泛型。如果想包含任何类型可指定为Any
//正规声明
var arr : Array<Any> = [1,2,"3",1.22]
print(arr[1])

//简写声明
let dic:[AnyHashable : Any] = [1:2,2:3,"3":4,4:"5"]
//字典的key和value都为任何类型,有可能是可选类型,获取元素值时要解包
print(dic[1]!)

let set:Set<String> = ["1","12","123"]
print(set.first!)
  • Array和Dictionary有简写的声明,而Set集合没有简写的声明。因为Set和Array唯一的区别是无序且不重复(当不考虑叙述而且没有重复的元素时,二者可互相替换),如果简写了就无法区分是Set还是Array了。
  • 三种集合强调的点不同, Array强调有序;Set强调不重复(无序);字典强调key唯一(不能重复),通过key取值,也是无序的。
  • Set的一般操作:
let set:Set<String> = ["1","123","12","12","1234"]
print("第一个元素\(set.first!)")
print("元素个数\(set.count)")
var set2:Set<String> = ["1234","1","123","12"]
if set == set2{
    print("set等于set2")
}
//插入一个元素
set2.insert("插入")
//删除某个元素
let item = "1"
set2.remove(item)
print(set2)
//删除一个元素,这里并不是第一个元素,而是随机的
set2.removeFirst()
print(set2)
//判断是否包含某个元素
if set.contains("1234"){
    print("set有该元素")
}

Set一般操作
结果可以看出Set的first方法获取的并不一定是第一个元素,而是随机的。多个重复的元素在Set中只算一个,从count可以看出。

  • Set集合遍历
for item in set{
    print(item)
}

for (index,item) in set.enumerated(){
    print("\(index+1):\(item)")
}

Set遍历
注意:Set的enumerated()方法可以取出Set的索引和元素, (index,item)是元组类型。这里的index是循环遍量,可以表示循环次数,而不是元素的序号脚标。

  • Set集合间的运算,首先了解一下几个概念(A,B是两个Set集合):
  1. 交集:属于A且属于B的元素集合
  2. 并集:属于A或属于B的元素集合
  3. 异或集合:A与B的并集元素集合去掉A与B的交集集合中的元素后剩下元素的集合
  4. 差集:属于A而不属于B的元素集合称A与B的差集。(A与B的并集去掉B中所有元素后的集合)
  5. 子集:B中所有元素都A的元素,那么久称B是A的子集。但是Set集合运算过程中不涉及Set子集概念。
let A:Set<String> = ["a","b","c","d"]
let B:Set<String> = ["c","d","e","f"]
print("A与B的交集 = \(A.intersection(B))")
print("A与B的并集 = \(A.union(B))")
print("A与B异或集合 = \(A.symmetricDifference(B))")
let C = A.subtracting(B)
print("A与B差集 = \(C)")
if C.isSubset(of: A){
   print("C是A的子集")
}

Set集合间的操作

7.函数

7.1 .Swift 函数参数
  • Swift中的函数参数很灵活,具体体现在传递参数有多种形式。
  • 我们可以为每个参数提供标签,为调用者说明参数的含义,这些变迁命名应该唯一,并且有意义:
   func rectangleArea(W width:Double,H height:Double) ->Double{
   return width*height
}
print(rectangleArea(W: 10.0, H: 10.0))

W,H就是参数标签,外部调用时会提示使用。

  • 如果定义函数没有声明标签,原则上也是可以的
func rectangleArea2(width:Double,height:Double) ->Double{
  return width*height
}

print(rectangleArea2(width: 10.0, height: 10.0))
  • 省略参数标签,在Swift3以后,调用函数时要求指定所有参数的标签,除非函数定义是使用下划线_关键字声明的标签。
func rectangleArea3(_ width:Double,_ height:Double) ->Double{
  return width*height
}
print(rectangleArea3(10,10))
  • 给参数设默认值,当调用时可以忽略该参数,调用时如果没传值就取默认值,如果赋值了就会覆盖掉默认值
func test(a:Int = 10,b:Int) -> Int{
  return a+b
}
print(test(b: 20)) //30
print(test(a: 1, b: 2)) //3
  • 可变参数:参数个数可以变化,调用时可以接受不确定数量的参数,这些参数具有相同的类型,有点像传入了一个数组
func sum(nums:Int...)->Int{
  var total = 0
  for num in nums{
   total += num
  }
  return total
}
print(sum(nums: 1,2,3))
print(sum(nums: 1,2,3,4,5,6,8))

sum函数是用来求多个整型的函数,参数nums:Int…是Int类型的可变参数,在函数体重nums被认为是一个Double类型的数组

  • 类型参数的引用传递:除了类是引用类型,其他如整型…都是值类型,但有时候我们想在函数内部改变函数外面参数的值,这样就需要将值类型参数以引用类型方式传递。
func increment(value: inout Double,auto:Double = 1.0){
   value += auto
}
var value:Double = 10.0
print(value)
increment(value: &value)
print(value) //11.0

参数value是需要增长的数值,它被设计为inout类型,inout修饰的参数称为输入输出参数,value必须是变量不能是let修饰的常量。

7.2. Swift函数返回值
  • 函数返回值分为无返回值和有返回值,无返回值其类型是void,可以省略不写;有返回值又分为单个返回值,和多个返回值,单个返回值就是返回一种类型,多个返回值可以返回多个类型,将这些不同类型的返回值放到元组中返回就可以了。
7.3. Swift函数类型
  • 每个函数都有一个类型,使用函数类型与使用其他数据类型一样,可以声明变量或常量,也可以作为其他函数参数或返回值使用。
  • 用函数类型声明常量或变量
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
let rectangleArea1: (Double,Double)->Double = rectangleArea(width:height:)
print(rectangleArea1(10,10)) //100.0

var rectangleArea2: (Double,Double)->Double = rectangleArea(width:height:)
rectangleArea2(20,20)
rectangleArea2 = rectangleArea(width:height:)
print(rectangleArea2(30,30)) //900.0
  • 用函数类型作为函数返回类型使用
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}

//作为函数返回类型使用
func getArea(type:String) -> (Double,Double)->Double {
    var returnFunc: (Double,Double)->Double
    switch type {
    case "矩形":
     returnFunc = rectangleArea
    default://三角形
      returnFunc = triangleArea
    }
    return returnFunc
}
let rectangleFunc = getArea(type: "矩形")
print("矩形面积:\(rectangleFunc(20,20))")  //矩形面积:400.0
let triangleFunc = getArea(type: "三角形")
print("三角形面积:\(triangleFunc(20,20))") //三角形面积:200.0

getArea返回值类型为函数类型,常量rectangleFunctriangleFunc只是接收了getArea函数的返回值,是将计算矩形面积和三角形面积的函数真正声明了,rectangleFunc(20,20)和triangleFunc(20,20)才是对计算面积的函数的真正调用。总之就是getArea函数调用是为了声明rectangleAreatriangleArea,返回的函数常量或变量的调用才是真正用于计算面积的,

  • 用函数类型做为函数参数类型使用
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}
//作为参数类型使用
func getAreabByFunc(funcName:(Double,Double)->Double,a:Double,b:Double)->Double {
    let area = funcName(a,b) //对传进来函数参数的真正调用
    return area;
}
print("矩形面积:\(getAreabByFunc(funcName: rectangleArea, a: 10, b: 10))")  //矩形面积:100.0
print("三角形面积:\(getAreabByFunc(funcName: triangleFunc, a: 10, b: 10))") //三角形面积:50.0

getAreabByFunc调用值只需要将函数名传进来就可以,传进来的函数参数直接在里面调用。
7.4. Swift嵌套函数

  • 将函数定义在另外的函数体重,称为嵌套函数
//嵌套函数
func calculate(opr:String) -> (Int,Int) -> Int {
    //定义加函数
    func add(a:Int,b:Int) -> Int{
        return a + b
    }
    //定义减函数
    func sub(a:Int,b:Int) -> Int{
        return a - b
    }
    var result: (Int,Int) -> Int
    switch opr {
    case "+":
        result = add
    case "-":
        result = sub
    default:
        result = add
    }
    return result
}
let addfunc = calculate(opr: "+") // 声明加函数
print("5+5 = \(addfunc(5,5))") //5+5 = 10
let subfunc = calculate(opr: "-")// 声明减函数
print("5-5 = \(subfunc(5,5))")//5-5 = 0

嵌套函数的作用域在外函数体内,但我们可以定义外函数的返回值类型为嵌套函数类型,从而将嵌套函数出啊递给外函数,被其调用者调用。

8.运算符重载

  • Swift中除了class类型是引用类型,其他整型,浮点型,数组,结构体等都是值类型,对于引用类型通常用===!===来判断是不是同一个对象;对于值类型通常用==!=来判断两个值是否相等。
  • 示例:自定义一个机构体,判断两个实例是否相等。
struct Student {
    var name = ""
    var no = 0
    var age = 0
}

var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一个学生")
}else{
    print("不是同一个学生")
}

在这里插入图片描述
可以发现在运行时报错了,报错说明为==不能用于两个Student结构体实例操作。说了stu1和stu2不能用于比较。我们需要在这些类型中重载==!=运算符号。即定义相等规则。

struct Student {
    var name = ""
    var no = 0
    var age = 0
}
//定义重载==号运算符符
func == (lsh:Student,rhs:Student) -> Bool {
    return lsh.name == rhs.name && lsh.no == rhs.no && lsh.age == rhs.age
}
//定义重载!=号运算符符
func != (lsh:Student,rhs:Student) -> Bool {
    return (lsh.name != rhs.name || lsh.no != rhs.no || lsh.age != rhs.age)
}

var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一个学生")
}else{
    print("不是同一个学生")
}

在这里插入图片描述

9.类型嵌套

  • Swift中的类、结构体和枚举可以进行嵌套。优点是支持访问它外部的成员(包括方法、属性和其他嵌套类型),嵌套可以有多个层次。
class Employee {
    var name = ""
    var no = 0
    var job = ""
    var day = WeekDays.Friday
    var dept = Department()
    
    struct Department {
        var no = 10
        var name = "Sales"
    }
    enum WeekDays{
        case Monday,Tuesday,Wednesday,Thursday,Friday
        
         struct Day {
            static var mes = "Today is ..."
        }
    }
    
    
}
let emplo = Employee()
print(emplo.day)
print(emplo.dept.name)
print(Employee.WeekDays.Day.mes)

10.类和结构体的异同。

  • 相同点:
    1. 定义存储属性;
    2. 定义方法;
    3. 定义下标;
    4. 定义构造函数;
    5. 定义扩展;
    6. 实现协议
  • 不同点:(只有类才有的功能)
    1. 能够继承另外一个类;
    2. 能够核对运行时对象的类型;
    3. 析构对象释放资源;
    4. 引用计数允许一个实例有多个引用。
  • 选择的原则:结构体是值类型,每一个实例没有独一无二的标识,而类是引用类型,每一个实例都是独一无二的标识。
class Employee{ //员工类
  var no = ""
  var name = ""
  var dept: Department?
  
}
struct Department{//部门结构体
  var no = ""
  var name = ""
}

上面示例可以看出,由于员工的编号都是独一无二,每个员工是独立的个体,所以员工可以声明成类Employee;如果具有相同部门标号和部门名称,我们就认为是它们是相同的部门,所以就可以把部门设计为机构体Department。

11.属性与下标。

  • Swift中的属性分为存储属性计算属性。存储属性就是oc中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据。
class Employee{
  let no = 0
  var firstName = "Tony"
  var lastName = "Guan"
  lazy var dept: Department = Department() //延迟存储属性
  var fullName: String{  //计算属性
      get{
       return firstName + "." + lastName
          
      }
      set(newFullName){
          let names = newFullName.components(separatedBy: ".")
          firstName = names.first!
          lastName = names.last!
      }
//        set{
//            let names = newValue.components(separatedBy: ".")
//            firstName = names.first!
//            lastName = names.last!
//        }
  }
  
}
struct Department{
  var no = ""
  var name = ""
}
let emp = Employee()
//emp.no = 10;编译会报错 ,常量属性不允许被修改。
print(emp.fullName)
emp.fullName = "Jack.Ma"
print(emp.fullName)
let dept = Department()

上面类Employee中的no、firstName、lastName、dept都是存储属性,fullName是计算属性,其中dept是延迟存储属性。

  • 延迟存储属性:存储属性前面加上lazy关键字声明,就是延迟存储属性,只在第一次访问时加载,如果不访问就不会创建,这样可以减少内存占用,注意存储属性没有延迟加载一说,添加到存储属性前面会报错的。
  • 计算属性:计算属性本身不存储数据,而是从其他属性计算得到数据。计算属性提供一个Getter(取值访问器)来获取值,以及一个可选的(可以不实现set方法)Settter(设置访问器)来间接设置其他属性或变量的值,语法如下。
面向对象的类型(classstructenum) 类型名{
存储属性
var 计算属性名: 属性数据类型{
 get{
 return 计算后的语句值
 }
 set(新属性值){
 语句组
}
}
}

其中新属性值是要赋值给属性值的,当然也可以不写,Swift提供了一个默认的变量newValue去接收新传入的值。上面set(newFullName)可以省略如下:

  set{
           let names = newValue.components(separatedBy: ".")
           firstName = names.first!
           lastName = names.last!
       }
  • 只读计算属性:计算属性只有Getter访问器,没有Setter访问器,只能取值而不能赋值。(只读存储属性就是let修饰的存储属性)
class Employee{
   let no = 0
   var firstName = "Tony"
   var lastName = "Guan"
   lazy var dept: Department = Department()
   var fullName: String{
       get{
        return firstName + "." + lastName
           
       }
   }
}
let emp = Employee()
//emp.no = 10;
print(emp.fullName)
// emp.fullName = "Jack.Ma" //不能赋值

fullName就是只读计算属性,不能给其赋值,结构体,枚举的计算属性也是类似,这里不再赘述。只读属性可以简化去掉get关键字和括号,上面只读属性可以简化为:

   var fullName: String {
        return firstName + "." + lastName   
       }
  • 存储属性观察者:Swift的属性观察者有两个,willSet:观察者在修改之前调用,didSet观察者在修改之后调用 语法格式如下。
面向对象类型(class/struct) 类型名{
   ...
   var 存储属性值: 属性数据类型 = 初始值{
       willSet(新值){
           ...
       }
       didSet(旧值){
           ...
       }
   }
}

示例

class Employee1{
    let no = 0
    var name = "Tony"{
        willSet(newName){
            print("员工新名字:\(newName)")
        }
        didSet(oldName){
            print("员工旧名字:\(oldName)")
        }
    }
}
struct Department1{
    var no = 1{
        willSet{
            print("部门新编号:\(newValue)")
        }
        didSet{
            print("部门旧编号:\(oldValue)")
        }
    }
    var name = "移动开发部"
}

let emp1 = Employee1()
emp1.name = "Jack"
//结构体是值类型,必须用声明为变量才能修改其属性
var dept1 = Department1()
dept1.no = 10

Employee1类中给name存储属性添加了观察者, willSet(newName)中的newName是要传进来的新值,didSet(oldName)中的oldName是新值传进来之前的旧值;参数的声明可以省略。Department1结构体中的no就省略了观察者参数,Swift提供了对应默认参数,新值默认是newValue,旧值默认是oldValue。这里需要说明下枚举只有计算属性没有存储属性,所以枚举不支持属性观察者

  • 静态属性:在属性前面加static关键字,类中也可以加class关键字,这样的属性称为静态属性。类,结构体,枚举都可以定义静态属性(也包括静态存储属性和静态计算属性)。这里需要注意的是枚举中没有实例存储属性但是可以有静态存储属性
struct Account {
    var amount = 0.0 //账户金额
    var ower = "" //账户名
    static let monthRate = 0.00688 //月利率
    static var yearRate : Double{  //计算存储属性不能用let修饰,及不能是常量
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)


class Account {
    var amount = 0.0 //账户金额
    var ower = "" //账户名
    static let monthRate = 0.00688 //月利率
    class var yearRate : Double{  //class换成static子类就不能重写该属性
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
   static var test:Int = 10{ //静态属性也支持添加属性监听者
        willSet{
            print(newValue)
        }
        didSet{
            print(oldValue)
        }
    }
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)

class Account2:Account{
    override class var yearRate : Double{  //class换成static子类就不能重写该属性
        return monthRate * 12 * 1.1
    }
}

这里枚举与结构体类似不再举例,类中static修饰的属性为类的静态属性,class修饰的属性称为类的类属性,区别是类的类属性可以被子类重写,但是class不能修饰存储属性,static却可以

  • 实例属性和静态属性总结
    1. 类、结构体,枚举都支持静态存储属性、静态计算属性,实例计算属性;但是只有类和结构体支持实例存储属性,枚举不支持实例存储属性。
    2. 基于第一条可知只有类和结构体支持添加属性观察者(因为只有存储属性才能添加属性观察者)
    3. 延迟属性只能是延迟存储属性
    4. 计算属性必须是var声明的
    5. let修饰的存储属性,前可以加static称为静态存储属性,不能加class
    6. class修饰的属性可以被重写,但static修饰的属性不允许被重写。
    7. class只能修饰计算属性。
  • 下标:Swift中,我们可以定义一些集合类型,,它们可以回有一些集合类型的存储属性,这些属性可通过下标访问,其如法格式如下。
面向对象类型(class/struct/enum) 类型名{
   ...
   subscript (参数:参数数据类型)->返回值类型{
       get{
           return 返回值
       }
       set(新属性值){
           ...
       }
   }
}

Swift中没有提供二维数组,但是我们可以通过下标自定义一个二维数组。

struct DoubleDimensionalArray {
   let rows:Int,colums:Int
   var grid: [Int]
   init(rows:Int,colums:Int) {
       self.rows = rows //行数
       self.colums = colums //列数
       grid = Array(repeating: 0, count: rows * colums) //初始化数组都为0
   }
   subscript(row:Int,col:Int) -> Int{
       get{
           return grid[row * colums + col] //返回对应的脚标取出二维数组的值
       }
       set{
           grid[row * colums + col] = newValue //通过脚标给二维数组赋值
       }
   }
}
//初始化一个10行10列的二维数组
var arr = DoubleDimensionalArray(rows: 10, colums: 10)
for i in 0..<10 {
   for j in 0..<10{
       arr[i,j] = i*j //通过脚标给二维数组赋值为脚标之和
   }
}

for i in 0..<10 {
   for j in 0..<10{
       print("\t \(arr[i,j])",terminator: " ") //通过脚标获取二维数组中的值
   }
   print("\n")
}

Swift自定义二维数组

11.方法。

  • Swift中,方法是在类,结构体,枚举中定义的函数,分为实例方法和静态方法.
  • 方法和函数的区别:方法是在在类,结构体,枚举内部定义的.方法调用前面要有主体,而函数就不需要.
  • 我们在枚举和结构体方法掐面添加关键字mutatting,将方法声明为可以变方法,可变方法能够修改值类型变量属性,但不能修改值类型常量属性.也就说不可变方法值类型属性是都不能访问的,但引用类型的属性是可以访问的。
  • static修饰的方法为静态方法,当然类中class修饰的方法类方法.与计算属性类似,实例方法中既可以访问实例属性和方法又可以访问静态属性和方法,但是静态方法不能访问实例属性和实例方法,只能访问静态属性和方法.
  • class修饰的方法能被重写,static修饰的方法不能被重写.

12.重写

  • 一个类继承另一个类的属性,方法,下标等特征后,子类可以重写(override)这些特征。
  • 重写实例属性:实例属性重写一方面可以Getter和Setter访问器,另一方面可以重写属性观察者。
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
}
class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue<8?8:newValue
        }
    }
}

从属性重写来看,子类本身并不存储数据,数据存储在父类的存储属性中,子类将其变成了计算属性并重写。
注意:一个属性重写了Getter和Setter访问器后就不能重写观察者。另外常量属性和只读计算属性也都不能重写属性观察者。

  • 重写静态属性:
class Account{
    class var staticProp:Double{
        return 0.0668 * 1000000
    }
}
class TermAccount: Account {
    override static var staticProp:Double{
        return 0.0700*1000000
    }
}

Account的静态属性staticProp只能用class修饰,因为要在子类TermAccount重写该静态属性,所以该属性能被继承才行,而TermAccount可以用class也可以用static修饰,因为没有子类继承TermAccount的staticProp属性。

  • 重写实例静态方法:
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
    func description() -> String {
        return "\(name)的年龄为:\(age)"
    }
}

class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue
        }
    }
    override init(name:String,age:Int) {
        self.school = "清华大学"
        super.init(name: name, age: age)
        
    }
    override func description() -> String {
        return "\(name)的年龄为:\(age),所在学校为\(school)"
    }
}

静态方法重写与实例方法重写类似,在方法名钱加上override关键字即可,但是只有class修饰的静态方法才能被被继承被重写,static修饰的静态方法不能被重写。

  • 下标重写:对下标重写也是重写Getter和Setter访问器,类似于属性,这里不再举例。
  • final关键字:final关键字可以声明类、属性、方法、和下标。final关键字可以声明类不能被继承,声明的属性、方法和下标不能被重写。

13.类检查与转换(is、as、as!、as?、AnyObject、Any)

  • 使用is进行类型检查:is操作符可以判断一个实例是否是某个类的类型。(只要是该类型以及该类型的父类以及父类的父类类型,返回都为true,类似于oc的isKindOfClass方法)
//Student继承于Person
let  stu =  Student(name: "马云", age: 18)
if stu is Student{
    print("马云18岁是一个学生")
}
if stu is Person{
    print("马云18岁是一个人")
}

在这里插入图片描述

  • 使用as、as!、as?进行类型转换:
    1. as操作符:用于向上转型(子类转换成父类),因为向上转型很少进行,所以代码中很少能够看到使用as操作符(通常都是向下转型:父类转出子类)。
    2. as!操作符: 在类型转换过程中对可选值进行拆包,转换结果是费可选类型。将费可选类型转换为非可选类型,将可选类型转换为非可选类型。
    3. as?操作符: 在类型转换过程中不进行裁包,转换结果是可选类型。将非可选类型转换为可选类型,将可选类型转换为可选类型。
//Student,Worker类都继承于People
let stu1 = Student(name: "李彦宏", age: 35, school: "北京大学")
let stu2 = Student(name: "马化腾", age: 45, school: "深圳大学")
let stu3 = Student(name: "马云", age: 55, school: "杭州师范大学")
let wk1  = Worker(name: "李开复", age: 56, factory: "微软")
let wk2  = Worker(name: "张小龙", age: 46, factory: "腾讯")
let people = [stu1,stu2,stu3,wk1,wk2]
for p in people{
    if let stu = p as? Student{ //这里在是先转换为可选类型,并且在转换成功后进行了可选绑定(因为都有值就解包了)
        print("Student \(stu.name),年龄:\(stu.age),毕业于:\(stu.school)")
    }else if let wk = p as? Worker{
        print("Worker \(wk.name),年龄:\(wk.age),工作于:\(wk.factory)")
    }
}
let stu4 = people[0]as? Student //这里是直接赋值为可选类型
print("--------")
print(stu4 as Any)
print(stu4?.name as Any) //可选类型安全访问其属性的写法
print(stu4!.name)

在这里插入图片描述

  • 使用AnyObjectAny类型:Swift提供了两种类型来表示不确定类型(任意类型),AnyObject表示任何类(class)的类型;Any则表示任何类型,包括Int、Double、Array、struct等基本数据类型,也包括AnyObject类型。也就是说AnyObjectAny的子集。
  • 在OC与Swift混合编程时,OC的id类型和Swift的AnyObject类型可以互换,但是两者有本质区别。id类型是泛性,可以代表任何指针类型,编译时编译器不检查id类型,是动态的。AnyObject是实实在在表示类的类型,编译时会检查AnyObject类型。
  • 原则上若能使用集体的数据类型,则尽量不要使用AnyObject类型,更要少考虑使用Any类型。从集合中取出这些实例时也要尽可能的将AnyObject类型和Any类型转换为特定类型,然后再进行接下来的操作。

14.扩展(extension)

  • Swift中可以使用一种扩展机制,在原始类型(类、机构体、枚举)的基础上添加新功能。扩展是一种轻量级的继承机制。
  • Swift中扩展机制可以在原始类型中添加新功能包括:
    1. 实例计算属性和静态计算属性
    2. 实例方法和静态方法
    3. 构造函数(结构体中可以扩展构造函数;类中只能扩展便利构造函数,不能扩展指定构造函数和析构函数,指定构造函数和析构函数只能在原始类中提供)
    4. 下标

swift cell报错

阅读数 923

IOS开发之SWIFT

阅读数 0