Swift 优点 (相对 OC)

  • Swift 愈加安全,是类型安全的语言
  • 代码少,语法简练,能够省去大量冗余代码
  • Swift 速度更快,运算功用更高,(Apple 专门对编译器进行了优化)

Swift 中 类(class) 和 结构体(struct) 的差异,以及各自优缺点?

  • 类:
    • 引证类型
      • 在进行变量赋值时,是经过指针copy,归于浅仿制(shallow copy)
      • 数据的存储是在堆空间
    • 能够被承继(条件是类没有被final关键字润饰),子类能够运用父类的特点和办法
    • (当class承继自 Object,具有runtime机制)类型转化能够在运转时查看和解释一个实例目标
    • 用 deinit(析构函数)来开释资源 相似OC(dealloc)
    • 类的办法地址 是不承认的,只要在详细运转时,才干承认调用的详细值
  • 结构体
    • 值类型
      • 在进行变量赋值是,是深仿制(deep copy),发生了新的副本
      • 数据的存储时在栈空间(大部分状况下,不需求考虑内存走漏问题,栈空间的特点是用完即开释)
    • 结构体调用办法,在编译完结就能够承认办法详细的地址值,以便直接调用

综上,在满意程序要求的状况下 优先运用 结构体


Swift中strong 、weak和unowned是什么意思?二者有什么不同?何时运用unowned?

Swift 的内存管理机制与 Objective-C相同为 ARC(Automatic Reference Counting)。它的根本原理是,一个目标在没有任何强引证指向它时,其占用的内存会被收回。反之,只需有任何一个强引证指向该目标,它就会一直存在于内存中。

  • strong代表着强引证,是默许特点。当一个目标被声明为strong时,就表明父层级对该目标有一个强引证的指向。此刻该目标的引证计数会添加1。

  • weak代表着弱引证。当目标被声明为weak时,父层级对此目标没有指向,该目标的引证计数不会添加1。它在目标开释后弱引证也随即消失。继续拜访该目标,程序会得到 nil,不亏崩溃

  • unowned与弱引证实质上相同。不同的是,unowned 无主引证 实例毁掉后依然存储着实例的内存地址(相似于OC中的unsafe_unretained), 企图在实例毁掉后拜访无主引证,会发生运转时错误(野指针)

  • weakunowned只能用在 类实例上面

  • weakunowned都能处理 循环引证,unowned要比weak功用 稍高

    • 在生命周期中可能会 变成nil的运用weak
    • 初始化赋值今后再也不会变成nil运用unowned

Swift 中什么是可选类型?

  • Swift中可选类型为了表明 一个变量 答应为 空(nil)的状况
  • 类型称号后 加?界说 可选项
  • 选项的实质是 枚举类型

Swift 中什么 是 泛型?

  • 跟JS和Dart 相似,泛型 能够将类型 参数化,进步代码复用率,削减代码量
  • Swift泛型函数 并不会 在底层 生成 若干个 (匹配类型)函数 ,发生函数重载,而是: 在函数调用时,会将参数的类型 传递给 目标函数
  • Swift泛型应用在协议上时,需求运用相关类型(associatedtype)

怎样了解 Swift中的泛型约束

泛型约束 能够 更精确的知道 参数 需求 遵从什么规范

//someT遵从的是某个class,someU遵从的是某个协议,这样在传参的时分清晰参数类型
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是泛型函数的函数体部分
}

Swift 中 static 和 class 关键字的差异

在 Swift 中staticclass都是表明「类型规模效果域」的关键字。

在一切类型(class[类]、struct、enum )中运用

  • static润饰都能够表明类办法类与特点(包括存储特点和核算特点)。
  • class是专门用在calss 类型中润饰类办法和类的核算特点(留意:无法运用 class 润饰存储特点)。

结构体只能用 static 润饰 类办法或特点

class类型 static class 的差异

class 类型staticclass都能够表明类型规模效果域,那差异是

  1. class无法润饰存储特点,而 static 能够。
  2. 运用 static 润饰的类办法和类特点无法在子类中重载。也便是说 static 润饰的类办法和类特点包括了 final 关键字的特性。相当于 final class 。

一般在 protocol界说一个类办法或许类核算特点引荐运用static关键字来润饰。运用 protocol 时,在 struct 和 enum 中依然运用 static,在 class 类型中 class 和 static 关键字都能够运用。


Swift 中的模式匹配?

模式 是用于 匹配的规矩,比方 switch 的 case 、捕捉错误的 catch 、 if guard while for 句子条件等


Swift 中的拜访操控?

Swift 供给了 5个 不同的拜访级别,从高到低摆放如下:

  • open
    • 答应在恣意模块中拜访、承继、重写
    • open 只能用在 类 、 类成员上
  • public
    • 答应在恣意模块中拜访
    • 润饰类时不答应其他模块进行承继、重写
  • internal– 默许
    • 只答应在界说实体的模块中拜访,不答应在其他模块中拜访
  • fileprivate
    • 只答应在界说实体的源文件中拜访
  • private:
    • 只答应在界说实体的关闭声明中(效果域)拜访

怎样了解 copy – on – write? 或许 了解Swift中的写时仿制

值类型在仿制时,仿制目标 与 原目标 实践上在内存中指向同一个目标,**当且仅当** 修改仿制的目标时,才会在内存中创立一个新的目标,

  • 为了提高功用,Struct, String、Array、Dictionary、Set采取了Copy On Write的技能
  • 比方仅当有“写”操作时,才会真实履行仿制操作
  • 关于规范库值类型的赋值操作,Swift 能确保最佳功用,一切没必要为了确保最佳功用来避免赋值

原理

在结构体内部用一个引证类型来存储实践的数据,

  • 在不进行写入操作的普通传递进程中,都是将内部的reference的应用计数+1,
  • 当进行写入操作时,对内部的 reference 做一次 copy 操效果来存储新的数据;防止和之前的reference发生意外的数据同享。

swift中供给[isKnownUniquelyReferenced]函数,他能查看一个类的实例是不是唯一的引证,假如是,咱们就不需求对结构体实例进行仿制,假如不是,阐明目标被不同的结构体同享,这时对它进行更改就需求进行仿制。

Swift 为什么将 Array,String,Dictionary,Set,规划为值类型?

值类型 比较 引证类型的优点

  • 值类型和引证类型比较,最大优势能够高效的运用内存;
  • 值类型在栈上操作,引证类型在堆上操作;
  • 栈上操作仅仅是单个指针的移动,
  • 堆上操作牵涉到合并,位移,重链接

Swift 这样规划削减了堆上内存分配和收回次数,运用 copy-on-write将值传递与仿制开销降到最低

String,Array,Dictionary规划成值类型,也是为了线程安全考虑。经过Swift的let设置,使得这些数据达到了真实意义上的“不变”,它也从根本上处理了多线程中内存拜访和操作次序的问题


什么是特点调查器?

特点调查是指在当时类型内对特性特点进行监测,并作出呼应,特点调查是 swift 中的特性,具有2种,willsetdidset

  • willSet 传递新值 newValue

  • didSet 传递旧值 oldValue

  • 在初始化器中对特点初始化时,不会触发调查器

  • 特点调查器 只能用在 存储特点 ,不行用在 核算特点

  • 能够 为 lazy(即推迟存储特点)的var存储特点设置 特点调查器

  • willSet会传递新值,默许叫 newValue
  • didSet会传递旧值,默许叫 oldValue

留意:

  • 在初始化器中设置特点值不会触发 特点调查器
    • 特点界说时设置初始值也不会出发 特点调查,原因是
      • 特点界说时设置初始值,实质跟在 初始化器中设置值是相同的
  • 特点调查器 只能用在 存储特点,不行用在 核算特点
struct Cicle {
    /// 存储特点
    var radius :Double {
        willSet {
            print("willSet -- ",newValue,"radius == ",radius)
        }
        didSet {
            print("didSet ++ ",oldValue,"radius == ",radius)
        }
    }
    /*
     上述代码 跟下面等价,不引荐
     var radius :Double {
         willSet(jk_newValue) {
             print("willSet -- ",jk_newValue,"radius == ",radius)
         }
         didSet(jk_oldValue) {
             print("didSet ++ ",jk_oldValue,"radius == ",radius)
         }
     }
     */
}
var circle = Cicle.init(radius: 10.0)
circle.radius = 20.0
/*
 willSet --  20.0 radius ==  10.0
 didSet ++  10.0 radius ==  20.0
 */
print("result == ",circle.radius)
//result ==  20.0

拓宽:
●特点调查器,核算特点这两个功用,相同能够应用在全局变量/局部变量
●特点调查器,核算特点 不能够一起 应用在同一个类(不包括承继)的特点中

Swift 反常捕获

do – try – catch 机制

  • Swift中能够经过Error协议自界说运转时的错误信息
  • 函数内部经过throw抛出 自界说Error, 抛出 Error 的函数有必要加上throws声明(逻辑经过不会抛出,反之可能抛出)
  • 需求运用try调用 可能会 抛出 的Error函数
  • 经过 try 测验调用 函数 抛出的反常 必需求 处理反常;否在会编译报错; 反之 运转时 在 top level (main) 报错,闪退

defer 的用法

  • 运用defer代码块来表明在函数回来前,函数中最终履行的代码。无论函数是否会抛出错误,这段代码都将履行。


怎么将Swift中协议 部分办法 规划成可选?

  • 方案一(不引荐,除非需求露出给OC用)
    • 在协议和办法前面添加@objc,然后在办法前面添加optional关键字,改办法实践上是将协议转为了OC的办法
@objc protocol someProtocol {
  @objc  optional func test()
}

协议 能够 用来界说特点 办法 下标声明,协议 能够被类 结构体 枚举遵守(多个协议之间用,离隔)

protocol Drawable {
    func draw()
    var x:Int { get set }
    var y:Int { get }
    subscript(index:Int) -> Int {get set}
}
protocol Test1 {}
protocol Test2 {}
protocol Test3 {}
class TestClass: Test1,Test2,Test3 {
}

留意:

  • 协议中界说的办法,不能有默许参数
  • 默许状况下,协议中界说的内容需求悉数完结

Swift和OC中的 protocol 有什么不同?

  • 相同点,两者都是用作代理
  • 不同点
    • Swift
      • Swift中的 protocol 还能够对接口进行抽象,能够完结面向协议,然后大大进步编程功率
      • Swift中的 protocol 能够用于值类型,结构体,枚举;

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

swift 的初始化办法,由于引入了两段式初始化和安全查看因而愈加严格和精确,

swift初始化办法需求确保一切的非optional的成员变量都完结初始化,

一起 swfit 新增了convenience和 required两个润饰初始化器的关键字

  • convenience只供给一种便利的初始化器,有必要经过一个指定初始化器来完结初始化
  • required是强制子类重写父类中所润饰的初始化办法

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

  • OC
    • 自省在OC中便是判别某一目标是否归于某一个类的操作,有以下2中办法
      • [obj iskinOfClass:[SomeClass class]]: obj 有必要是 SomeClass 的 目标或 其子类目标;return YES;
      • [obj isMemberOfClass:[SomeClass class]]: obj 有必要是 SomeClass 的 目标;return YES;
  • Swift
    • Swift 中由于许多 class 并非承继自 NSObject, 故而 Swift 运用is来判别是否归于某一类型, is 不只能够效果于class, 还能够效果于enum和struct

Swift 与 OC 怎么相互调用

  • Swift -> OC
    • 需求创立一个Target-BriBridging-Header.h(默许在OC项目中,会提示主动创立)的桥文件,在该文件中,导入需求调用的OC代码的头文件即可
  • OC -> Swift
    • 直接导入Target-Swift.h(该文件是Xcode主动创立) Swift怎么需求被OC调用,需求运用@objc对办法或特点进行润饰

Swift 中特别的标记

iOS Swift开发面试题总结

Swift调用OC
●新建一个桥接文件,文件格局默许为:{targetName}-Bridging-Header.h;(一般在OC项目中,创立Swift文件,Xcode会主动提示生成该文件,仅需点击承认即可)

iOS Swift开发面试题总结

●在{targetName}-Bridging-Header.h 文件中 #import OC 需求 露出 给 Swift的内容

OC 调用 Swift
●Xcode 现已默许 生成 一个 用于 OC 调用 Swift的头文件,文件名格局是: {targetName}-Swift.h

iOS Swift开发面试题总结

●Swift 露出给 OC的 类 一定要承继 NSObject

●运用 @objc 润饰 需求露出 给 OC的成员

●运用@objcMembers 润饰类
○代表 默许一切的 成员 都会 露出给 OC(包括扩展中界说的成员)
○终究 是否成功 露出,还需求考虑 成员自身的 拜访级别

拓宽
●为什么Swift 露出给 OC 的类 要终究 承继 NSObject?
由于 OC 中的办法调用 是经过 Runtime 机制,需求经过 isa 指针 去完结 一些列音讯的发送等, 而 只要承继自 NSObject 的类 才具有 isa 指针,才具有 Runtime 音讯 发送的才干
●p.run() 底层是怎么调用的? 反过来,OC调用Swift 又是怎么调用?
○JKPerson 是 OC 的类,以及OC 中界说的初始化 和 run 办法
○在Swift中 调用 JKPerson 目标的 run 办法 ,底层是怎么调用的?

Swift仿制代码

var p = JKPerson(age: 10,name:"Jack")
p.run()

答:走 Runtime 运转时机制, 反过来 OC 调用 Swift中的类 跟 问题一 相同,也是经过 Runtime 机制
●car.run() 底层是怎么调用的?

iOS Swift开发面试题总结

答 : (虽然 Car 类 被露出给 OC运用)在Swift中 car.run(),底层依然是 经过 相似 C++ 的虚表 机制 来调用的;

拓宽:
假如想要 Swift中的办法 调用 也运用 Runtime 机制,需求在办法称号前面 加上 dynamic关键字


Swift界说常量 和 OC界说常量的差异?

//OC:
const int price = 0;
//Swift:
let price = 0
  • OC中 const 常量类型和数值是在编译时承认的
  • Swift 中 let 常量(只能赋值一次),其类型和值既能够是静态的,也能够是一个动态的核算办法,它们在运转时承认的。

Swift 中的函数重载

构成函数重载的规矩

  • 函数名相同
  • 参数个数不同 || 参数类型不同 || 参数标签不同

留意: 回来值类型 与函数重载无关;回来值类型不一起,函数重载会报错:

func overloadsum(v1 : Int,v2:Int) -> Int {
    v2 + v1
}
// 参数个数不同
func overloadsum(v1 : Int,v2:Int,v3:Int) -> Int {
    v2 + v1 + v3
}
// 参数类型不同
func overloadsum(v1 : Int,v2:Double) -> Double {
    v2 + Double(v1)
}
// 参数标签不同
func overloadsum(_ v1 : Int,_ v2:Int) -> Int {
    v2 + v1
}
func overloadsum(a : Int,_ b:Int) -> Int {
    a + b
}
/**
 回来值类型不一起,在函数重载时,会报错:
 Ambiguous use of 'overloadsum(v1:v2:)'
 func overloadsum(v1 : Int,v2:Int) {
 }
 */
public func overloadtest() {
    let result1 = overloadsum(v1: 10, v2: 20)
    let result2 = overloadsum(v1: 10, v2: 20, v3: 30)
    let result3 = overloadsum(v1: 10, v2: 20)
    let result4 = overloadsum(10, 20)
    let resutt4_1 = overloadsum(a: 10, 20)
    print(result1,result2,result3,result4,resutt4_1)
    //30 60 30 30 30
}

Swift 中的枚举,相关值 和 原始值的区分?

    • 将 枚举的成员值 跟 其他类型的值 相关存储在一起
    • 存储在枚举变量中,占用枚举变量内存
enum Score {
    case points(Int)
    case grade(Character)
}
    • 枚举成员能够运用相同类型的默许值预先相关,这个默许值叫做:原始值
    • 不会存储在 枚举变量中,不占用枚举变量内存
enum PokerSuit : Character {
    case spade = "♠"
    case heart = "♥"
    case diamond = "♦"
    case club = "♣"
}

闭包是引证类型吗?

闭包和函数都是是引证类型。假如一个闭包被分配给一个变量,这个变量仿制给另一个变量,那么他们引证的是同一个闭包,他们的捕捉列表也会被仿制。

swift 中的闭包结构是什么姿态的?

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

什么是跟随闭包

  • 跟随闭包 是一个被 书写在 函数调用 括号 后边的 闭包表达式

根本界说
●Swift中可经过func界说一个函数,也能够经过闭包表达式界说一个函数

闭包表达式的格局:

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

闭包表达式与界说函数的语法相对比,有差异如下:
1没有func
2没有函数名
3回来值类型添加了关键字in

let fn1 = {
    (v1 : Int,v2 : Int) -> Int in
    return v1 + v2
}
let result1 = fn1(10,5)
let result2 = {
    (v1:Int,v2:Int) -> Int in
    return v2 + v1
}(10,6)
print(result1,result2) // 15 16

闭包表达式的简写

func exec(v1:Int,v2:Int,fn:(Int,Int)->Int) {
    print(fn(v1,v2))
}
闭包表达式的简写
private func test2() {
    // 1: 没有简写
    exec(v1:10, v2:20, fn: {
        (v1:Int,v2:Int) -> Int in
        return v1 + v2
    })
    // 2: 简写1
    exec(v1: 2, v2: 3, fn: {
        v1,v2 in return v1 + v2
    })
    // 3:简写 2
    exec(v1: 3, v2: 4, fn: {
        v1,v2 in v1 + v2
    })
    // 4: 简写3
    exec(v1: 5, v2: 6, fn: {$0 + $1})
    // 5: 简写4
    exec(v1: 7, v2: 8, fn: +)
}

跟随闭包

  • 将一个很长的闭包表达式作为函数的最终一个实参,运用跟随闭包能够增强代码的可读性
    • 跟随闭包 是一个被 书写在 函数调用 括号 后边的 闭包表达式
func test3() {
    exec(v1: 8, v2: 7) { a, b in
        a + b
    }
    // or     { 书写在 函数调用 括号 后边的 闭包表达式}
    exec(v1: 9, v2: 10) {
        $0 + $1
    }
}
  • 假如 闭包表达式 是函数的唯一 实参,且运用了跟随闭包的 语法,则在函数名后边的()可省略
// fn:便是跟随闭包
func exec1(fn:(Int,Int)->Int) {
    print(fn(1,2))
}
exec1(fn: {$0 + $1})
exec1() {$0 + $1}
exec1{$0 + $1}

什么是逃逸闭包

  • 闭包有可能在函数完毕后调用,闭包调用 逃离了函数的效果域,需求经过@escaping声明

留意:逃逸闭包 不能够 捕获 inout 参数

原因是: 逃逸闭包不承认 何时开始履行,有可能 在履行逃逸闭包时,可变参数现已被程序收回,形成野指针拜访

什么是主动闭包

主动闭包是一种主动创立的用来把作为实践参数传递给函数的表达式打包的闭包。

它不承受任何实践参数,而且当它被调用时,它会回来内部打包的表达式的值。

这个语法的优点在于经过写普通表达式代替显式闭包而使你省略围住函数形式参数的括号。

func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
  • 为了避免与希望冲突,运用了@autoclosure的地方最好清晰注释清楚:这个值会被推延履行
  • @autoclosure 会主动将 20 封装成闭包 { 20 }
  • @autoclosure 只支撑 () -> T 格局的参数
  • @autoclosure 并非只支撑最终1个参数
  • 有@autoclosure、无@autoclosure,构成了函数重载

假如你想要主动闭包答应逃逸,就一起运用 @autoclosure 和 @escaping 标志。


Swift 中的存储特点与核算特点

存储特点(Stored Property)

  • 相似于成员变量这个概念
  • 存储在实例目标的内存中
  • 结构体、类能够界说存储特点
  • 枚举不能够界说存储特点

关于 存储特点, Swift 中有个清晰的规则

  • 在创立 类 或许 结构体 的实例时,有必要为一切的存储特点设置一个适宜的初始值
    • 能够在初始化器里为存储特点设置一个初始值
    • 能够分配一个默许的特点值作为特点界说的一部分

核算特点(Computed Property)

  • 实质便是办法(函数)
  • 不占用实例目标的内存
  • 枚举、结构体、类都能够界说核算特点

核算特点(Computed Property)
○实质便是办法(函数)
○不占用实例的内存
○枚举、结构体、类 都能够界说核算特点

了解核算特点与存储特点:

假如两个特点之间存在一定的逻辑关系,运用核算特点,原因如下:
●假如都用存储特点的话,其逻辑对应关系可能有误
●而运用核算特点,则能够精确的描绘 这种逻辑关系
详细事例参阅 下面的Cicle中的 radius(半径) 和 diameter(直径) 的逻辑关系

一起由于核算特点不占用实例的内存,能够有用的节省实例的内存空间

●set 传入的新值 默许叫做newValue,也能够自界说

struct Cicle {
    /// 存储特点
    var radius :Double
    /// 核算特点
    var diameter: Double {
        get {
            radius * 2
        }
        set (jkNewValue){
            radius = jkNewValue / 2.0
        }
    }
}
  • 只读核算特点:只要get, 没有set
struct Cicle {
    /// 存储特点
    var radius :Double
    /// 核算特点
    /*
     var diameter: Double {
         get {
             radius * 2
         }
     }
     */
    // 上述代码与下面的代码等价
    var diameter: Double {radius * 2}
}
var cicle = Cicle.init(radius: 12)
print(cicle.radius)//12.0
print(cicle.diameter)//24.0
// cicle.diameter = 10.0 //Cannot assign to property: 'diameter' is a get-only property
  • 界说核算特点只能用 var,不能够是 let
    • let 表明常量
    • 核算特点的值是可能会发生变化的(包括只读核算特点)

什么是[推迟存储特点](Lazy Stored Property)

  • 运用lazy能够界说一个 推迟存储特点,在第一次用到特点的时分才会进行初始化(相似 OC 中的懒加载)

留意点:

  • lazy 特点 有必要是 var
    • 由于,let 特点 有必要在实例的初始化办法 完结之前 就具有值
  • 假如多条线程一起第一次拜访 lazy 特点,无法确保 特点 只被 初始化 一次 (即线程不是安全的)

  • 运用lazy能够界说一个 推迟存储特点,在第一次用到特点的时分才会进行初始化(相似 OC 中的懒加载)

留意点:

  • lazy 特点 有必要是 var
    • 由于Swift 规则:let 特点 有必要在实例的初始化办法 完结之前 就具有值
  • 假如多条线程一起第一次拜访 lazy 特点,无法确保 特点 只被 初始化 一次 (即线程不是安全的)
class Car {
    init() {
        print("car has init")
    }
    func run() {
        print("car running")
    }
}
class Person {
    lazy var car = Car.init()
    init() {
        print("person has init")
    }
    func go_out() {
        car.run()
    }
}
let p = Person.init() //person has init
print("*******")
p.go_out()//  car has init ---->   car running
  • 当结构体 包括 一个推迟存储特点时,只要 var 才干拜访推迟 存储特点
    • 由于推迟特点 初始化时 要改动 结构体的内存
struct Point {
    var x = 0
    var y = 0
    lazy var z = 0
}
let p = Point.init()
print(p.z)//Cannot use mutating getter on immutable value: 'p' is a 'let' constant

什么是 类型 特点?

  • 类型特点(Type Property) :经过类型去拜访
    • 存储类型特点(Stored Type Property):整个程序运转进程中,就只要1份内存(相似于全局变量,底层采用了 gcd_once 操作)
    • 核算类型数据(Computed Type Property):

特点可分为
●实例特点(Instance Property):经过实例去拜访
○存储实例特点(Stored Instance Property):存储在实例的内存中,每个实例都有1分
○核算实例特点(Computed Instance Property):不占用实例的内存,实质是办法

●类型特点(Type Property) :经过类型去拜访
○存储类型特点(Stored Type Property):整个程序运转进程中,就只要1份内存(相似于全局变量,底层采用了 gcd_once 操作,确保只初始化一次)
○核算类型数据(Computed Type Property):

留意:
存储类型特点不会 占用 实例目标 的内存,整个程序运转进程中,就只要1份内存

存储类型特点 实质便是全局变量(该全局变量加了一些类型操控,只能经过类型去拜访),

能够经过 static 界说类型特点
假如 是类, 也能够运用 关键字 class
结构体 就只能运用 关键字 static

struct Car {
    static var count:Int = 0
    init(){
        Car.count += 1
    }
}
let c1 = Car.init()
let c2 = Car.init()
let c3 = Car.init()
print(Car.count)//3

类型特点的细节:
●不同于 存储实例特点 ,有必要给 存储类型特点 设定初始值
○由于类型没有想实例那样的 init 初始化器来初始化 存储特点
●存储类型特点 默许便是 lazy。会在第一次运用的时分 初始化
○就算 被 多个线程 一起拜访,确保只会被 初始化 一次,线程是安全的
○存储类型 特点 也能够是 let
●枚举类型 也能够 界说 类型特点(存储类型特点,核算类型特点)


Swift 中怎么界说单例模式

能够经过类型特点+let+private来写单例; 代码如下如下:

// 办法一
public class SingleManager{
    public static let shared = SingleManager()
    private init(){}
}
// 办法二
public class SingleManager{
    public static let shared = {
        //...
        //...
        return SingleManager()
    }()
    private init(){}
}
// 上述两个办法等价,一般引荐 办法二

Swift 中 下标是什么?

  • 运用subscript能够给恣意类型(枚举,结构体,类) 添加下标的功用

subscript 的语法 相似于 实例办法、核算特点,实质便是办法(函数)

func xiabiaoTest() {
    class Point {
        var x = 0.0
        var 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
            }
        }
    }
    let p = Point()
    p[0] = 11.1 // 调用subscript
    p[1] = 22.2 // 调用subscript
    print(p.x)//11.1 不会调用 subscript
    print(p.y)//22/2 不会调用 subscript
    print(p[0])//11.1 // 调用subscript
    print(p[1])//22.2 // 调用subscript
}

扼要阐明Swift中的初始化器?

一图胜千言针对类

  • 结构领会默许生成 含有参数的初始化器,一旦自界说初始化器,默许的初始化器则不行用
  • 类默许只会生成无参的指定初始化器

iOS Swift开发面试题总结

  • 类、结构体、枚举都能够界说初始化器
  • 类有2种初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer)

什么是可选链?

可选链是一个调用和查询可选特点、办法和下标的进程,它可能为 nil 。

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

可选链(Optional Chaining)

假如 可选项 不会nil ,调用 办法 ,下标,特点成功,成果会被包装成 可选项,反之调用失利,回来nil

class Car { var price = 0}
class Dog { var weight = 0}
class Person {
    var name:String = ""
    var dog :Dog = Dog()
    var car :Car? = Car()
    func age() -> Int { 18 }
    func eat() {print("Person Eat")}
    subscript (index:Int) ->Int {index}
}
var person :Person? = Person()
var age1 = person!.age() //Int
var age2 = person?.age() // Int?
var name = person?.name // String?
var index = person?[6] // Int?
func getName() -> String {"jackie"}
/*
 假如 person 目标 是 nil  ,将不会调用 getName() 办法
 */
person?.name = getName()
  • 假如成果本来便是可选项,不会进行再次包装
if let _ = person?.eat() {
    /*
    Person Eat
	eat success
    */
    print("eat success")
} else {
    print("eat failure")
}
  • 多个能够衔接在一起,其间任何一个节点 假如为 nil,那么整条链就会 调用失利
var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int?
var price = person?.car?.price // Int?

什么是运算符重载?

类、结构体、枚举能够为现有的运算符供给自界说的完结,这个操作叫做:运算符重载

struct Point {
    var x: Int,y: Int
    static func + (p1: Point,p2: Point) -> Point {
        Point(x: p1.x + p2.x ,y: p1.y + p2.y)
    }
}
let p = Point(x:10,y: 20) + Point(x: 30,y: 40)
print(p) //Point(x: 40, y: 60)

Swift中函数的柯里化

将一个 承受 多个参数的 函数,变成 只承受 一个参数的一系列 操作

示例

func add(_ v1: Int,_ v2: Int) -> Int {
    v1 + v2
}
func difference(_ v1: Int,_ v2: Int) -> Int {
    v1 - v2
}
func add2(_ v1: Int,_ v2: Int,_ v3 :Int ,_ v4 :Int) -> Int {
    v1 + v2 - v3 + v4
}
  • 伪柯里化
func currying_add(_ v1:Int) -> (Int) -> Int {
    return {$0 + v1}
}
/*
 将恣意一个 承受两个 参数的函数 柯里化
 */
func curring_fun_tow_params1(_ f1 :@escaping (Int,Int) -> Int, _ v1 :Int) -> (Int) -> Int {
//    return {
//        f1($0,v1)
//    }
    return { (v2) in
        return f1(v1 , v2)
    }
}
print(add(10, 20)) // 30
print(currying_add(10)(20)) // 30 // 30
print(curring_fun_tow_params1(add, 10)(20))
  • 正宗柯里化
func curring_fun_two_params2<A,B,C>(_ f1: @escaping (A,B) -> C) -> (A) -> ((B) -> C) {
    /*
     return {
         (a) in  // a = 3
         return {
             (b) in  // b = 8
             return  f1(a,b)
         }
     }
     */
    { a in { b in f1(a, b)} }
}
let result = curring_fun_two_params2(add)(3)(5)
print("result == ",result) //8
  • 柯里化拓宽

/*
 -> (A) -> (B) -> (C) -> (D) -> E
 实践是 一连串 闭包的 组合  如下所示:
 -> (A) -> (  (B) ->    ((C)  ->   ((D) -> E))  )
 传入 A   >  一个 闭包    (B)   ->   (  (C) -> ((D) -> E)  )
 传入 B   >>  一个 闭包   (C)   ->   (  (D) -> E  )
 传入 C   >>  一个闭包    (D) -> E
 */
//func curring_fun_more_params<A,B,C,D,E>(_ fn: @escaping (A,B,C,D) -> (E)) -> (A) -> ((B) -> ((C) -> ((D) -> E))) {
  func curring_fun_more_params<A,B,C,D,E>(_ fn: @escaping (A,B,C,D) -> (E)) -> (A) -> (B) -> (C) -> (D) -> E {
   /*
    return {
        (a) in
        return {
            (b) in
            return {
                (c) in
                return {
                    d in
                    return fn(a,b,c,d)
                }
            }
        }
    }
    */
    {a in { b in { c in { d in fn(a,b,c,d)}}}}
}
let resutl2 = curring_fun_more_params(add2)(10)(20)(30)(40)
print(resutl2) // 40
let resutl2_func = curring_fun_more_params(add2)
let resutl2_func_value = resutl2_func(10)(20)(30)(40)
print(resutl2_func_value) // 40