iOS面试题-Swift篇(二)

swift 中的闭包结构是什么样子的

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

什么是尾随闭包

将一个很长的闭包表达式作为函数的最后一个实参
使用尾随闭包可以增强函数的可读性
尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式

// fn 就是一个尾随闭包参数
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}

// 调用
exec(v1: 10, v2: 20) {
    $0 + $1
}

什么是逃逸闭包
当闭包作为一个实际参数传递给一个函数或者变量的时候,我们就说这个闭包逃逸了,可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。

  • 非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
  • 非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
  • 逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明
// 定义一个数组用于存储闭包类型
var completionHandlers: [() -> Void] = []

//  在方法中将闭包当做实际参数,存储到外部变量中
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

如果你不标记函数的形式参数为 @escaping ,你就会遇到编译时错误。

什么是自动闭包
自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号

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)

  • 类似于成员变量这个概念
  • 存储在实例对象的内存中
  • 结构体、类可以定义存储属性
  • 枚举不可以定义存储属性

计算属性(Computed Property)

  • 本质就是方法(函数)
  • 不占用实例对象的内存
  • 枚举、结构体、类都可以定义计算属性
struct Circle {
    // 存储属性
    var radius: Double
    // 计算属性
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            return radius * 2
        }
    }
}

什么是延迟存储属性(Lazy Stored Property)
使用lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化(类似OC中的懒加载)

  • lazy属性必须是var,不能是let
  • let必须在实例对象的初始化方法完成之前就拥有值
  • 如果多条线程同时第一次访问lazy属性
  • 无法保证属性只被初始化1次
class PhotoView {
    // 延迟存储属性
    lazy var image: Image = {
        let url = "https://...x.png"
        let data = Data(url: url)
        return Image(data: data)
    }()
}

什么是属性观察器
可以为非lazy的var存储属性设置属性观察器,通过关键字willSet和didSet来监听属性变化

struct Circle {
    var radius: Double {
        willSet {
            print("willSet", newValue)
        }
        didSet {
            print("didSet", oldValue, radius)
        }
    }
    init() {
        self.radius = 1.0
        print("Circle init!")
    }
}

swift中什么类型属性(Type Property)
严格来说,属性可以分为

  • 实例属性(Instance Property): 只能通过实例对象去访问

  • 存储实例属性(Stored Instance Property):存储在实例对象的内存中,每个实例对象都有1份

  • 计算实例属性(Computed Instance Property)

  • 类型属性(Type Property):只能通过类型去访问

  • 存储类型属性(Stored Type Property):整个程序运行过程中,就只有1份内存(类似于全局变量)

  • 计算类型属性(Computed Type Property)

可以通过static定义类型属性 p如果是类,也可以用关键字class

struct Car {
    static var count: Int = 0
    init() {
        Car.count += 1
    }
}

不同于存储实例属性,你必须给存储类型属性设定初始值

因为类型没有像实例对象那样的init初始化器来初始化存储属性

存储类型属性默认就是lazy,会在第一次使用的时候才初始化

就算被多个线程同时访问,保证只会初始化一次
存储类型属性可以是let

枚举类型也可以定义类型属性(存储类型属性、计算类型属性)

swift 中如何使用单例模式
可以通过类型属性+let+private 来写单例; 代码如下如下

 public class FileManager {
    public static let shared = {
        // ....
        // ....
        return FileManager()
}()
    private init() { }
}

swift 中的下标是什么
使用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

简要说明Swift中的初始化器

  • 类、结构体、枚举都可以定义初始化器
  • 类有2种初始化器: 指定初始化器(designated initializer)、便捷初始化器(convenience initializer)
 // 指定初始化器
init(parameters) {
    statements
}
// 便捷初始化器
convenience init(parameters) {
    statements
}

规则:

  • 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
  • 默认初始化器总是类的指定初始化器
  • 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器

初始化器的相互调用规则

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

什么可选链
可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil 。如果可选项包含值,属性、方法或者下标的调用成功;如果可选项是 nil ,属性、方法或者下标的调用会返回 nil 。多个查询可以链接在一起,如果链中任何一个节点是 nil ,那么整个链就会得体地失败

多个?可以链接在一起
如果链中任何一个节点是nil,那么整个链就会调用失败

什么是运算符重载(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

以上就是全文内容,希望对大家有所帮助
收录:原文地址

你可能感兴趣的:(ios,swift)