我正在参与「启航方案」

或许是最全的Swift Tips

1. 关于Swift

1.1 Swift的优点

  1. Swft愈加安全, 它是类型安全的言语.
  2. Swift容易阅读, 语法和文件结构简易化.
  3. Swift易于保护, 文件分离后结构更清晰.
  4. Swift代码更少, 简洁的语法, 能够省去许多冗余代码.
  5. Swift速度更快, 运算功用更高.

1.2 Swift和OC怎么彼此调用?

  • Swift调用OC代码, 需求创立一个Target-Bridging-Header.h的桥接文件, 在桥接文件导入需求调用的OC代码头文件即可.
  • OC调用Swift代码, 直接导入项目名-Swift.h文件即可, Swift如果需求被OC调用, 需求运用@objc对办法或许特点进行润饰.

1.3 Swift是面向目标编程(Object Oriented Programing)仍是函数式编程(Functional programming)?

  • Swift是一种混合编程言语, 它包括着两种编程形式.
  • 它完结了面向目标的三个根本原则: 封装、承继、多态.
  • 函数式编程言语是指: 它是一种编程范式, 它将电脑运算视为函数核算, 并且防止运用程序状态以及易变目标. 很难说Swift是一个老练的函数式编程言语, 可是它现已具备了函数式编程言语的根底.

2. 基操知识点

2.1 Swift中struct和class的差异, struct能承继吗(不能)

  • 在Swift中, class是引证类型(指针类型), struct是值类型.

值类型

  1. 值类型在传递和赋值时将进行仿制; 赋值给var、let或许给函数传参, 是直接将一切内容仿制一份, 相似于对文件进行copy、paste操作, 发生了全新的文件副本. 归于深仿制.
  2. 值类型: 比方结构体, 枚举, 是在栈空间上存储和操作的.

引证类型

  1. 引证类型只会运用引证目标的一个”指向”; 赋值给var、let或许给函数传参, 是将内存地址仿制一份, 相似于制造一个文件的替身(快捷办法、链接), 指向的是同一个文件. 归于浅仿制.
  2. 引证类型: 比方Class, 是在堆空间上存储和操作的.

class和struct比较, 优缺点

class有以下功用, struct是没有的:

  1. class能够承继, 子类能够运用父类的特性和办法
  2. 类型转化能够在运转时查看和解释一个实例目标
  3. class能够用deinit来开释资源
  4. 一个类能够被屡次引证
  • 类中的每一个成员变量都有必要被初始化, 不然编译器会报错, 而结构体不写.., 编译器会主动帮咱们生成init函数, 给一个变量赋一个默许值

struct优势:

  1. 结构较小, 适用于仿制操作, 相比较一个class实例被屡次引证, struct更安全
  2. 无须担心内存走漏问题

2.1.1 在Swift中, 什么时分用struct, 什么时分用class?

  • 函数式编程倾向于struct, 面向目标编程更倾向于class. 在Swift中, 类和结构体有许多不同的特性如下:
  1. 类支撑承继, 结构体不支撑.
  2. 类是引证类型, 结构体是值类型.
  • 没有通用的规矩决议结构体和类哪一个更好用, 一般的主张是运用最小的东西来完结你的方针.
  • 可是有一个好的经验是多运用struct, 除非你用了承继和引证语义.
  • 在运转时, 结构体在功用方面更优于类, 原因是结构体的办法调用是静态绑定的, 而类的办法调用是动态完结的. 这便是尽或许运用结构体替代类的一个重要原因之一.

2.1.2 Swift为什么将String、Array、Dictionary设计为值类型?

  • 值类型和引证类型相比, 最大的优势是能够高效的运用内存.
  • 值类型在栈上操作, 引证类型在堆上操作, 栈上操作仅仅是单个指针的移动.
  • 堆上操作涉及到内存的兼并、位移、重链接.
  • Swfit这样设计减少了堆上内存分配和回收次数, 运用写时仿制(Copy-On-Write)将值传递与仿制开支降到最低.

2.2 Swift中Class的内部完结和内存管理

2.3 文件拜访权限关键字 private public

2.3.1 拜访等级

  1. open
  • open的权限是最大的, 能够在答应的实体模块、其它模块中拜访, 并且答应其它模块进行承继和重写.
  • 例如: TargetA中有classA, 权限是open, TargetB中的classB即能够承继classA, classA的办法, 成员变量等也能够被拜访.
    iOS老司机万字整理, 可能是最全的Swift Tips
  1. public
  • public和open是差不多的, 也是答应在实体模块, 其它模块中拜访, 有一点差异是, 并不答应其它模块进行承继和重写.
  • 例如: TargetA中有classA, 办法是testA, 权限是public, TargetB中有classB, 那么在classB中testB办法, 就能够初始化var a = classA(), 并且调用a.testA.
  1. internal
  • internal只答应在界说的实体模块进行拜访, 不答应在其它模块中拜访. 这个也是许多实体默许的权限.
  1. fileprivate
  • fileprivate翻译过来便是文件私隐, 它只答应在界说的文件中拜访.
  • 例如: 在一个Target中, 有classA和classB两个类分别在两个文件, classA当时权限是fileprivate, 那么classB是不能拜访classA的. 如果classA和classB是在同一个文件下, 就能够拜访.
  1. private
  • private只答应在当时界说实体中拜访.
  • 例如: classA和classB都在同一个文件, classA的权限是private, 那么classB原则上是不能拜访classA的. 要拜访的话, 需求一些状况.

2.3.2 拜访等级的运用准则

  • 一个实体不能够被更低的拜访等级的实体界说.
  1. 变量类型的拜访等级 >= 变量的拜访等级
  • 例如: 界说一个类fileprivate class ClassA{}, 如果界说为internal var classA: ClassA就会报错, 权限ClassA的试题类型需求大于变量classA.

iOS老司机万字整理, 可能是最全的Swift Tips

  1. 参数类型, 回来值类型 >= 函数
  • 例如: func testA(_ num: Int) -> Double{}, 函数的拜访等级默许是internal, 参数的num是public, 回来值Double也是public

iOS老司机万字整理, 可能是最全的Swift Tips

  1. 父类 >= 子类
  • 相当于说我能拜访子类, 那么父类也应该要能够拜访才对.
  • 例如: class SupClassA{}, 子类class ClassA: SupClassA{}, 父类的默许全显现internal, 那么子类就不能为publicopen
    iOS老司机万字整理, 可能是最全的Swift Tips

2.3.3 成员嵌套类型

  1. 类型为private, fileprivate
  • 当时类型为private, fileprivate, 那么成员的默许类型也是privatefileprivate
  • 例如: fileprivate class ClassA { var a = 0, var b = 0}, a和b默许都是fileprivate
  1. 类型为internal, public
  • 当类型为internal, public, 成员的默许类型为internal
public class ClassA {
    internal var a = 0
}

2.3.4 直接在大局效果域下界说的private等价于fileprivate

  1. 能够编译经过
    iOS老司机万字整理, 可能是最全的Swift Tips
  2. 不能够编译经过
    iOS老司机万字整理, 可能是最全的Swift Tips

3.4 getter, setter权限

  • 关于读写方面, 许多时分咱们希望他人读咱们的值, 而不答应修正咱们的值, 咱们能够这么界说如下:
class ClassA {
    private(set) var age: Int = 0
}

iOS老司机万字整理, 可能是最全的Swift Tips

2.4 Swift的module的默许拜访权限, module内部的拜访权限

2.5 写时仿制机制, OC中相似的机制是什么?

值类型(比方struct), 在仿制时, 仿制目标与原目标实践上在内存中指向同一个目标, 当且仅当修正仿制的目标时, 才会在内存中创立一个新的目标.

  1. 为了提高功用, 值类型:struct, enum, Int, Double, Float, String, Array, Dictionary、Set采取了Copy On Write的技能
  2. 比方仅当有”写”操作时, 才会真正履行仿制操作
  3. 关于标准库值类型的赋值操作, Swift能确保最佳功用, 所以没必要为了确保功用来防止赋值
let array = [1, 2, 3]
var array1 = array
// 断点1, 此刻array和array2内存地址共同
array1 = array
// 断点2, 此刻array和array2内存地址不共同

iOS老司机万字整理, 可能是最全的Swift Tips

  • 写时仿制答应同享同一个内存地址, 直到其中之一发生改动. 这样的设计使得值类型能够被屡次仿制而无需耗费多余的内存, 只需在改动的时分才会添加开支, 躲藏内存的运用愈加高效.

  • 在OC言语中, 想要获取多个完全共同、互不干扰的目标, 能够运用mutableCopy.

NSMutableArray *array = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];
NSMutableArray *array1 = [array mutableCopy];

2.6 什么是optional类型, 它是用来处理什么问题的?

  • optional类型被用来表明任何类型的变量的短少值. 在OC中, 引证类型的变量是能够缺失值, 并且运用nil作为短少值. 根本数据类型没有这种功用.
  • Swift用optional扩展了在根本数据类型和引证类型中短少值的概念, 一个optional类型的变量, 在任何时分都能够保存一个值或许为nil.

2.7 什么是泛型? 泛型是用来处理什么问题的?

  • 泛型是让你能依据自界说的需求, 编写出适用于恣意类型的、灵活可复用的函数及类型. 你能够防止编写重复的代码, 而是用一种清晰笼统的办法来表达代码的意图.

2.7.1 Swift中泛型的高档运用

  • Swift包括泛型类泛型结构体, 泛型能够在类、结构体、枚举、大局函数或许办法中运用.
  • 泛型协议是经过typealias部分完结的, typealias不是一个泛型类型, 他仅仅一个占位符的姓名. 它通常是作为相关类型被引证, 只需协议被一个类型引证的时分它才被界说.

2.8 哪些状况下运用隐式解包?

  • optional变量运用隐式解包最常见的原因如下:
  1. 目标的特点在初始化的时分不能为nil, 不然不能初始化成功. 典型的比如是Interface Builder outlet类型的特点, 它总是在它的具有者初始化之后再初始化. 在这种特定的状况下, 假定他在Interface Builder中被正确的装备–outlet被运用之前, 确保它不为nil.
  2. 处理强引证的循环问题, 当两个实例目标彼此引证, 并且对引证的实例目标的值要求不能为nil时分. 在这种状况下, 引证的一方能够符号为unowned, 另一方运用隐式解包.
  3. 除非必要, 不然尽量不要对optional类型运用隐式解包. 运用不当会添加运转时crash的或许性.在某些状况下, crash或许是有意的行为, 但这种状况更推荐fatalError()函数.

2.8.1 对一个optional变量解包有哪些办法?

  1. 强制解包, !操作符, 不安全,容易引起运转时崩溃.
  2. 隐式解包, 在变量声明时, 大多数状况也不安全, 也有或许引起运转时崩溃.
  3. 可选绑定if letguard let.
  4. 自判别连接optional chaining.
  5. 兼并空值运算符??.
  6. guard语句.
  7. 可选形式optional pattern.

2.9 Swift中的常量界说和OC的差异

// 在OC中能够这样界说常量:
const int number = 0;
// 相似的Swift是这样界说的:
let number = 0
  • const常量是一个在编译时或许编译解析时被初始化的变量.
  • 经过let创立的是一个运转时常量, 是不可变的. 它能够运用static或许dynamic关键字类初始化. 它的值只能被分配一次.

2.10 Swift中的static或许class润饰符的效果

  • 声明一个静态特点或许函数, 咱们常常运用值类型的static润饰符. 下面便是一个结构体的比如:
struct Sun {
    static fun illuminate() {}
}
  • 对类来说, 运用static或许class润饰符, 都是能够的. 他们运用后的效果是一样的, 可是实质上是不同的.
  • 实质不同原因是: static润饰的特点或许润饰的函数都不能够重写. 可是运用class润饰符, 你能够重写特点或许函数.
  • static在类中运用的时分, static就成为class final的一个别号.
  • 例如下面代码中, 当你尝试重写illuminate()函数的时分, 编译器就会报错:
class Star {
    class func spin() {}
    static func illuminate() {}
}
class Sun : Star {
    override class func spin() {
        super.spin()
    }
    override static func illuminate() { // error: Cannot override static method
        super.illuminate()
    }
}

iOS老司机万字整理, 可能是最全的Swift Tips

  • sil代码中能够看到class 润饰的类办法存储在VTable中, static润饰的类办法是以静态办法的形式存储的.

2.11 Swift中能经过extension保存一个特点吗?

  • 不能. extension能够给当时类型添加新的行为, 但不能改动自身的类型或许自身的接口.
  • 如果你添加一个新的可存储的特点, 你需求额定的内存来存储新的值. 扩展不能完结这样的使命.

2.12 闭包是引证类型吗?

  1. 闭包是一个引证类型.
  2. 闭包捕获值的实质是在堆区开辟内存, 然后存储其在上下文中捕获到的值.
  3. 修正值也是修正的堆空间的值.
  4. 闭包的底层结构是一个结构体. 首先存储闭包的地址; 加上捕获值的地址.
  5. 在捕获的值中, 会对界说的变量和函数中的参数分开存储.
  6. 存储的时分内部会有一个HeapObject结构, 用于管理内存、引证计数
  7. 函数是特殊的闭包, 只不过函数不捕获值, 所以在闭包结构体中只存储函数地址, 不存储指向捕获值的指针.

2.13 怎么把一个负整数转化成一个无符号的整数?

  • UInt类型是用来存储无符号整型的. 下面的代码完结了一个有符号整型转化的初始化办法: let myNegative = UInt(-1)
  • 咱们知道负数的内部结构是运用二进制补码的正数, 在坚持这个负数内存地址不变的状况下, 怎么把一个负整数转化成一个无符号的整数?
  • 原码: 原码便是符号位加上真值的绝对值, 即用第一个二进制位表明符号(正数该位为0, 负数该位为1), 其余位表明值.
  • 反码: 正数的反码与其原码相同; 负数的反码是对其原码逐位取反, 但符号位在外.
  • 补码: 正数的补码便是其自身; 负数的补码是在其反码的根底上+1

iOS老司机万字整理, 可能是最全的Swift Tips

2.14 描绘一种在Swift中呈现循环引证的状况.

  • 循环引证呈现在两个实例目标彼此具有强引证关系的时分, 这会造成内存走漏, 原因是这两个目标都不会被开释, 只需一个目标被另一个目标强引证, 那么该目标就不能被开释, 因为强引证的存在, 每个目标都会坚持对方存在.
  • 处理办法能够运用weak或许unowned.
  • 转化为值类型, 只需类会存在喧嚷引证, 如果能用把class换成struct, 是能够防止循环引证的.
  • delegate运用weak特点.
  • 闭包中, 对有或许发生循环引证的目标, 运用weak或许unowned润饰.

2.14.1 关键字strong、weak、unowned的差异?

  • Swfit的内存管理机制同OC共同, 都是ARC, strongweak同OC一样.
  • unowned(无主引证), 不会发生强引证, 实例毁掉后仍然存储着实例的内存地址(相似于OC中的unsafe_unretained), 它仍然会坚持对被现已开释了的目标的一个”无效的”引证, 它不是Optional, 也不会被指向nil, 如果试图在实例毁掉后拜访无主引证unowned, 会发生运转时过错(悬垂指针).
  • weak, 当咱们赋值给一个被符号为weak的变量时, 它的引证计数不会被改动. 并且当这个弱引证变量所引证的目标被开释时, 这个变量将被主动设为nil. 这也是弱引证有必要被声明为Optional的原因.
  • 在引证目标的生命周期内, 如果它或许为nil, 那么就用weak引证. 反之, 当你知道引证目标在初始化后永远都不会为nil, 就用unowned.
  • 如果你知道你引证的目标会在正确的时机开释掉, 且它们是彼此依存的, 而你不想写一些多余的代码来状况你的引证指针, 那么你就应该运用unowned引证而不是weak引证.
class SwiftViewControllerA: UIViewController {
  var person : Person?
  override func viewDidLoad() {
    super.viewDidLoad()
    person = Person()
    person?.testClosure()
    person = nil
   
  }
}
// 测试unowned和weak
class SomeSigleton {
  static let share = SomeSigleton()
  func closure(closure: (() -> Void)?) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
      closure?()
    }
  }
}
class Person {
  let someSigleton = SomeSigleton.share
  let portrait = UIImage()
  func testClosure() {
    someSigleton.closure { [unowned self] in
      print(self.portrait)
    }
        // 运用weak润饰就不会有问题!
        //    someSigleton.closure { [weak self] in
//      print(self?.portrait)
//    }
  }
  deinit {
    print("Person is deinited")
  }
}

iOS老司机万字整理, 可能是最全的Swift Tips

  • Apple文档运用主张
Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.
Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. This enables you to check for their existence within the closure’s body.
  • 当咱们知道两个目标的生命周期并不相关, 那么咱们有必要运用weak. 相反, 非强引证目标具有和强引证目标同样或许更长的声明周期的话, 则应该运用unowned.
  • 例如, ViewController对它的SubView的引证能够运用unowned. 因为ViewController的生命周期一定比它的SubView长. 而在运用服务时, 则需求看状况运用weak. 因为服务的初始化办法或许是被工厂形式或Service Locator所封装. 这些服务或许在某些时分被重构为单例, 此刻它们的生命周期发生了改动.

2.15 什么关键字能够完结递归枚举?

  • indirect
enum List<T> {
    case end
    indirect case node(T, next: List<T>)
}
indirect enum List<T> {
    case end
    case node(T, next: List<T>)
}

2.16 什么是特点调查?

  • 特点调查是指在当时类型内对特性特点进行监测, 并做出相应. 特点调查是Swift中的特性, 具有两种办法, willsetdidset
var title: String {
    willSet {
        print("willSet", newValue)
    }
    didSet {
        print("didSet", oldValue, title)
    }
}
  • willSet会传递新值, 默许叫newValue
  • didSet会传递旧值, 默许叫oldVlaue
  • 在初始化器中设置特点不会触发willSet和didSet

2.17 比较Swift和OC中的初始化办法init有什么不同?

  • Swift的初始化办法, 愈加严厉和精确, Swift初始化办法需求确保一切的非Optional的成员变量都完结初始化, 一起Swift新增了conveniencerequired两个润饰初始化器的关键字.
  • convenience只供给了一种快捷的初始化器, 有必要经过一个指定初始化器来完结初始化.
  • required是强制子类重写父类中所润饰的初始化办法.

2.18比较Swift和OC中的protocol有什么异同?

  • 相同点: 两者都能够被用作代理.
  • 不同点: Swift中的protocol还能够对接口进行笼统, 能够完结面向协议编程, 然后大大提高编程功率; Swift中的protocol能够用于值类型、结构体、枚举.

2.18.1 怎么将Swift中协议protocol中的部分办法设计为可选Optional?

  • 在协议和办法前面添加@objc, 然后在办法前面添加optional关键字, 该办法实践上是将协议转为了OC的办法.
@objc protocol someProtocol {
    @objc optional func testProtocol()
}
  • 运用扩展extension, 来规定可选办法, 在Swfit中, 协议扩展能够界说部分办法的默许完结
protocol someProtocol {
    func test()
}
extension someProtocol {
    func test() {
        print("test")
    }
}

2.19 Swift和OC中的自省办法有什么差异?

  • OC中的自省办法便是判别某一个目标是否归于某一个类的操作, 有以下2种办法
// 判别obj是否是某个类
[obj isKindOfClass:[SomeClass class]];
// 判别obj是否是某个类或许是该类的子类
[obj isMemberOfClass:[SomeClass class]];
  • 在Swift中因为许多class并非承继自NSObject, 故而Swift运用is来判别是否归于某一类型, is不仅能够效果于class, 还能效果于enumstruct.

2.20 什么是函数重载? Swift支撑函数重载吗?

  • 函数重载: 函数名相同, 函数的参数个数不同, 或许参数类型不同, 或参数标签不同, 回来值类型与函数重载无关.
  • Swift支撑函数重载.

2.21 Swift中枚举的*相关值原始值的差异?

  • 相关值: 有时会将枚举的成员值跟其他类型的变量相关存储在一起, 会十分有用.
// 相关值
enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}
  • 原始值: 枚举成员能够运用相同类型的默许值预先相关, 这个默许值叫做: 原始值.
// 原始值
enum Grade: String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}

2.22 Swift中的闭包Closure相关

2.22.1 Swift中的闭包结构是什么样的?

{
    (参数列表) -> 回来值类型 in 函数体代码
}

2.22.2 什么是跟随闭包?

  • 将一个很长的闭包表达式作为函数的最终一个实参.
  • 运用跟随闭包能够增强函数的可读性.
  • 跟随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式.
// fn便是一个跟随闭包参数
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}
// 调用
exec(v1: 10, v2: 20) {
    $0 + $1
}

iOS老司机万字整理, 可能是最全的Swift Tips

2.22.3 什么是逃逸闭包?

  • 当闭包作为一个实践参数传递给一个函数或许变量的时分, 咱们就说这个闭包逃逸了, 能够在形式参数前写@escaping来清晰闭包是答应逃逸的.
  • 非逃逸闭包、逃逸闭包, 一般都是作为参数传递给函数.
  • 非逃逸闭包: 闭包调用发生在函数完毕前, 闭包调用在函数效果域内.
  • 逃逸闭包: 闭包有或许在函数完毕后调用, 闭包调用逃逸出了函数的效果域, 需求经过 @escaping声明.
// 界说一个数组用于存储闭包类型
var completionHandlers: [() -> Void] = []
// 在办法中将闭包作为实践参数, 存储到外部变量中
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
  • 上面这种状况, 如果不符号函数的形式参数为escaping, 就会编译报错.

2.22.4 什么是主动闭包?

  • 主动闭包是一种主动创立的用来把作为实践参数传递给函数的表达式打包的闭包.
  • 它不接受任何实践参数, 并且当它被调用时, 它会回来内部打包的表达式的值.
  • 这个语法的优点在于经过写一般表达式替代显式闭包而使你省略围住函数形式参数的括号.
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
  • 为了防止与希望冲突, 运用了@autoclosure的当地最好清晰注释清楚: 这个值会被推迟履行.
  • @autoclosure会主动将20封装成闭包{ 20 }
  • @autoclosure只支撑()->T格局的参数
  • @autoclosure并非只支撑最终一个参数
  • @autoclosure、无@autoclosure, 构成了函数重载.

2.23 兼并空值运算符 ??

  • a ?? b
  • a 是可选项, b 能够是可选项也能够不是可选项, b跟a的存储类型有必要相同.
  • 如果a不为nil, 就回来a.
  • 如果a为nil, 就回来b. 如果b不是可选项, 回来a时, 会对a进行主动解包操作.
let a: Int? = 1
let b: = 2
// 此刻c为Int型, 不是可选类型. 并且值为1.
let c = a ?? b // 
  • 所以??回来的类型取决于b.
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
  • 从上面的界说就能够看出来.

2.24 Swfit中, 存储特点和核算特点的差异?

  • Swfit中跟实例目标相关的特点能够分为两大类
  1. 存储特点(Stored Property)
  • 相似于成员变量这个概念
  • 存储在实例目标的内存中
  • 结构体、类能够界说存储特点
  • 枚举不能够界说存储特点
  1. 核算特点(Computed Property)
  • 实质便是办法(函数)
  • 不占用实例目标的内存
  • 枚举、结构体、类都能够界说核算特点
struct Circle {
    // 存储特点
    var radius: Double
    //核算特点
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            return radius * 2
        }
    }
}

2.24.1 什么是推迟存储特点(Lazy Stored Property)?

  • 运用lazy能够界说一个推迟存储特点, 在第一次用到特点的时分才会进行初始化(相似OC中的懒加载).
  • lazy特点有必要是var, 不能是let(let有必要在实例目标的初始化办法完结之前就具有值).
  • 如果多条线程一起第一次拜访lazy特点, 无法确保特点只被初始化一次.
class PhotoView {
    // 推迟存储特点
    lazy var image: Image = {
        let url = ""
        let data Data(url: url)
        return Image(data: data)
    }()
}

2.24.2 什么是特点调查器?

  • 能够为非lazyvar存储特点设置特点调查器, 经过关键字willsetdidset来监听特点改动.
struct Circle {
    var radius: Double {
        willSet {
            print("willSet", newValue)
        }
        didSet {
            print("didSet", oldValue, radius)
        }
    }
    init() {
        self.radius = 1.0
        print("Circle init!")
    }
}

2.24.3 Swift中什么是类型特点(Type Property)?

  • 严厉的说, 特点能够分为

  • 实例特点(Instance Property): 只能经过实例目标去拜访.

    • 存储实例特点(Stored Instance Property): 存储在实例目标的内存中, 每个实例目标都有一份.
    • 核算实例特点(Computed Instance Property)
  • 类型特点(Type Property): 只能经过类型去拜访

    • 存储类型特点(Stored Type Property): 整个程序运转进程中, 就只需一份内存(相似于大局变量).
    • 核算类型特点(Computed Type Property)
  • 能够经过static界说类型特点p如果是类, 也能够用关键字class.

struct Car {
    static var count: Int = 0
    init() {
        Car.count += 1
    }
}
  • 不同于存储实例特点, 你有必要给存储类型特点设定初始值.
    • 因为类型没有像实例目标那样的init初始化器来初始化存储特点.
  • 存储特点默许便是lazy, 会在第一次运用的时分才初始化.
    • 就算被多个线程一起拜访, 确保只会初始化一次.
    • 存储类型特点能够是let.
  • 枚举类型也能够界说类型特点(存储类型特点、核算类型特点)

2.25 Swfit中怎么运用单例形式?

  • 能够经过类型特点 + let + private来写单例, 代码如下:
public class FileManager {
    public static let shared = {
        ...
        return FileManager()
    }
    private init() {}
}

2.26 Swfit中的下标是什么?

  • 运用subscript能够给恣意类型(枚举、结构体、类)添加下标功用, 有些当地也翻译为: 下标脚本.
  • subscript的语法相似于实例办法、核算特点, 实质便是办法(函数). 运用如下:
class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}
var p = Point()
// 下标值
p[0] = 11.1
p[1] = 22.2
// 下标拜访
print(p.x)// 11.1
print(p.y)// 22.2

2.27 简略说一下Swift中的初始化器

  • 类、结构体、枚举都能够界说初始化器
  • 类有两种初始化器: 指定初始化器designated initializer、快捷初始化器convenience initializer
// 指定初始化器
init(parameters) {
    statements
}
// 快捷初始化器
convenience init(parameters) {
    statements
}

规矩:

  • 每一个类至少有一个指定初始化器, 指定初始化器是类的首要初始化器
  • 默许初始化器总是类的指定初始化器
  • 类倾向少数指定初始化器, 一个类通常只需一个指定初始化器

初始化器的彼此调用规矩

  • 指定初始化器有必要从它的直系父类调用指定初始化器
  • 快捷初始化器有必要从相同的类里调用另一个初始化器
  • 快捷初始化器最终有必要调用一个指定初始化器

2.28 什么是可选链?

  • 可选链是一个调用和查询可选类型、办法和下标的进程, 它或许为nil.
  • 如果可选项包括值, 那么特点、办法或许下标的调用成功;
  • 如果可选项是nil, 特点、办法或许下标的调用会回来nil.
  • 多个查询能够链接在一起, 如果链中任何一个节点是nil, 那么整个链就会得体的失利.

2.29 什么是运算符重载(Operator Overload)?

  • 类、结构体、枚举能够为现有的运算符供给自界说的完结, 这个操作叫: 运算符重载.
struct Point {
    var x: Int
    var y: Int
    // 运算符重载
    static func + (p1: Point, p2: Point) -> Point {
        return Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(x: 20, y: 20)
var p3 = p1 + p2

3. OC和Swift运转时简介

3.1 Objective-C运转时

  • 动态类型 (dynamic typing)
  • 动态绑定 (dynamic binding)
  • 动态装载 (dynamic loading)

3.1.1 OC目标调用办法的进程

  • [object methodA]

iOS老司机万字整理, 可能是最全的Swift Tips

iOS老司机万字整理, 可能是最全的Swift Tips

3.2 派发办法

3.2.1 直接派发 (Direct Dispatch)

  • 直接派发是最快的, 不止是因为需求调用的指令集会更少, 并且编译器还能够有很大的优化空间, 例如函数内联等, 直接派发也有人称为静态调用.
  • 但是, 关于编程来说直接调用也是最大的限制, 并且因为缺少动态性所以没办法支撑承继多态.

3.2.2 函数表派发 (Table Dispatch)

  • 函数表派发是编译型言语完结动态行为最常见的完结办法. 函数表运用了一个数组来存储类声明的每一个函数的指针. 大部分言语把这个称为”Virtual table(虚函数表)”, Swift里称为 “witness table”. 每一个类都会保护一个函数表, 里边记录着类一切的函数, 如果父类函数被override的话, 表里边只会保存被override之后的函数. 一个子类新添加的函数, 都会被刺进到这个数组的最终. 运转时会依据这一个表去决议实践要被调用的函数.

iOS老司机万字整理, 可能是最全的Swift Tips

  • 查表是一种简略、易完结, 并且功用可预知的办法. 但是, 这种派发办法比起直接派发仍是慢一点. 从字节码角度来看, 多了两次读和一次跳转, 由此带来了功用的损耗. 另一个慢的原因在于编译器或许会因为函数内履行(如果函数带有副效果的话)的使命导致无法优化.
  • 这种基于数组的完结, 缺点在于函数表无法拓展. 子类会在虚数函数表的最终刺进新的函数, 没有位置能够让extension安全地刺进函数.

3.2.3 音讯机制派发 (Message Dispatch)

  • 音讯机制是调用函数最动态的办法. 也是Cocoa的基石, 这样的机制催生了KVO、UIAppearence和CoreData等功用. 这种运作办法的关键在于开发者能够在运转时改动函数的行为. 不止能够经过swizzling来改动, 甚至能够用isa-swizzling修正目标的承继关系, 能够在面向目标的根底上完结自界说派发.

iOS老司机万字整理, 可能是最全的Swift Tips

3.3 Swift运转时

  • 纯Swift类的函数调用现已不再是Objective-C的运转时发音讯, 而是类型C++的vtable, 在编译时就确定了调用哪个函数, 所以没法经过runtime获取办法、特点.
  • 而Swift为了兼容Objective-C, 但凡承继自NSObject的类都会保存其动态性, 所以咱们能经过runtime拿到他的办法. (老版别的Swift(如2.2)是编译期隐式的主动帮你加上了@objc, 而Swift4.0以后编译期去掉了隐式特性, 有必要运用显现添加.)
  • 不管是纯Swift类仍是承继自NSObject的类, 只需在特点和办法前添加了@objc关键字就能够运用runtime.

iOS老司机万字整理, 可能是最全的Swift Tips

  • 值类型总是会运用直接派发, 简略易懂
  • 协议和类的extension都会运用直接派发
  • NSObject的extension会运用音讯机制进行派发
  • NSObject声明效果域里的函数都会运用函数表进行派发
  • 协议里声明的, 并且带有默许完结的函数会运用函数表进行派发

iOS老司机万字整理, 可能是最全的Swift Tips

3.3.1 Swift运转时-final @objc

  • 能够在符号为final的一起, 也运用@objc来让函数能够运用音讯机制派发. 这么做的成果便是, 调用函数的时分会运用直接派发, 但也会在Objective-C的运转时里注册相应的selector. 函数能够响应perform(selector:)以及别的Objective-C特性, 但在直接调用时又能够有直接派发的功用.

发文不易, 喜欢点赞的人更有好运气 :), 定时更新+重视不迷路~

ps:欢迎参加笔者18年建立的研讨iOS审核及前沿技能的三千人扣群:662339934,坑位有限,备注“网友”可被群管经过~