【iOS面试题二】Swift编程语言
APP(iOS烂笔头---您的iOS面试备考神器!)https://i-blog.csdnimg.cn/direct/4ad72e550c874e1296ee241c9f14a98e.png
该APP除了包含完整的内容,还有AI知识扩展。利用便捷、接待下载。
题库(一):Objective-C高级编程
题库(二):Swift编程语言
题库(三):SwiftUI编程思想
题库(四):SwiftUI与Combine编程
题库(五):函数响应式编程
题库(六):知识要点
一、基础语法
1、什么是强制解析?
用!来访问一个可选变量,如:
let someValue: Int? = 3
print(someValue!) 2、什么是隐式解析?
用!来声明一个可选变量(人为保证其不为空值),如:
let someValue: Int! = 3
print(someValue) 3、什么是可选绑定?
用if let来将可选变量有值环境下的值赋给另外一个变量,如:
let someValue: Int? = 3
if let value = someValue {
print(value)
} 4、条件检查函数有哪些?
(1) assert(断言):只在调试环境有效
(2) precondition(先决条件):同时在调试和生产环境有效,但在unchecked编译模式下不举行检查
(3) fatalError(强制检查):不受编译环境和编译模式控制
5、区间运算符有哪些?
闭区间、半开区间和单侧区间
6、闭包表达式的参数有什么要求?
不能拥有默认值(可以是in-out、可变的)
7、什么是尾随闭包?
写在函数圆括号背面的闭包表达式,函数支持将其作为最后一个参数调用
8、什么是闭包的值捕获?
值捕获是指闭包可以在其被定义的上下文中捕获常量或变量,纵然定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包的函数体内引用和修改这些值。
9、什么是逃逸闭包?
逃逸闭包通过@escaping修饰,是指当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
10、什么是自动闭包?
自动闭包(@autoclosure)是一种自动创建的闭包,用于包装转达给函数作为参数的表达式。这种闭包不担当任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你可以或许省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
11、恒等运算符和等价运算符的区别是什么?
恒等运算符(===),用于引用范例,判定两个常量大概变量是否引用同一个类实例。等价利用符(==),用于值范例或引用范例,判定哈希值是否相称。
12、常量的利用规则是什么?
值范例的常量,所有属性也成为常量,不可修改;引用范例的常量,仍然可以修改它的可变属性。
13、输入输出参数(in-out)的内存工作方式是怎样的?
输入输出参数接纳拷入拷出内存模式,在函数内部利用的是参数的copy,函数竣事后,再对参数重新赋值。因此,假如将带有观察器的属性转达给输入输出参数,它的willSet和didSet都会调用。
14、什么是异变方法(mutating)?
异变方法表示可以在方法中修改结构体的属性大概枚举的值,该方法竣事时会将改变写回原值中,也可以直接在方法中给隐含的self属性赋予一个全新的实例(即,用于改变实例的属性值或实例自己)
15、下标(subscript)的参数有什么要求?
不能是输入输出的和默认的(可以是可变的)
二、枚举、结构体和类
1、Swift和OC的枚举有什么差别?
(1) 枚举默认环境下没有原始值。可以将原始值范例声明为int,它就会像OC一样为rawValue赋予默认值。除了可以声明为int范例,也可以声明为其他范例,原始值的范例可以没有,但有的时候必须是唯一的。
(2) 枚举还可以有关联值,关联值可以存储任意范例的值,假如必要的话,每个枚举成员的关联值范例可以各不相同。
(3) 原始值和关联值是差别的。原始值是在定义枚举时被预先填充的值。对于一个特定的枚举成员,它的原始值始终稳定。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。
2、结构体比类少了哪些特性?
析构器、继承、范例转换和引用计数。
3、枚举中是否可以有存储属性?
不可以。存储属性只能用于类和结构体,盘算属性可以用于所有范例(类、结构体和枚举)。
4、延时加载属性(lazy)是盘算属性还是存储属性?为什么它只能声明为变量(var)?
延时加载属性是存储属性,由于它的初始值可能在实例构造完成之后才会得到,而常量必须在构造过程完成之前有初始值,所以只能声明为变量。
5、延时加载属性(lazy,普通的和闭包的)是线程安全的吗?
不是的,当在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
6、什么是盘算属性?
盘算属性不直接存储值,而是提供一个getter和一个可选的setter,来间接获取和设置其他属性或变量的值。它可用于枚举、结构体和类。
7、为什么盘算属性(包罗只读的)只能用var来声明?
由于盘算属性的值不是固定的,所以只能用var来声明。而let关键字只用来声明常量属性,表示初始化后再也无法修改的值。
8、属性观察器(willSet/didSet)有哪些限制?
属性观察器不能用于延长加载存储属性和非重写的盘算属性(可以直接通过它的setter来监控),也可以在子类中通过重写属性的方式为继承的属性(常量存储属性和只读盘算属性不可以)添加属性观察器。
9、在父类初始化方法调用之前,给子类的属性赋值时会调用子类属性的观察器吗?
不会
10、在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的观察器吗?
会
11、继承对属性访问性的变化规则
可放大不可缩小,即只读可重写为读写(只必要在重写版本的属性里同时提供getter和setter即可),读写不可重写为只读。
12、类的构造器有哪几类?
(1) 指定构造器:每一个类都必须至少拥有一个,大多数类通过继承了父类中的指定构造器而满足了这个条件。
(2) 便利构造器
13、类构造器的代理规则
(1) 指定构造器必须调用其直接父类的的指定构造器。
(2) 便利构造器必须调用同类中定义的别的构造器。
(3) 便利构造器最后必须调用指定构造器。
14、类实例的构造过程
构造过程分为两段:
(1) 阶段一
① 类的某个指定构造器或便利构造器被调用。
② 完成类的新实例内存的分配,但此时内存还没有被初始化。
③ 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
④ 指定构造器切换到父类的构造器,对其存储属性完成相同的使命。
⑤ 这个过程沿着类的继承链一直往上执行,直到到达继承链的最顶部。
⑥ 当到达了继承链最顶部,而且继承链的最后一个类已确保所有的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段一完成。
(2) 阶段二
① 从继承链顶部往下,继承链中每个类的指定构造器都有机会进一步自定义实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
② 最终,继承链中任意的便利构造器有机会自定义实例和利用self。
(3) 安全检测
① 指定构造器必须保证它所在类的所有属性都必须先初始化完成,之后才能将别的构造使命向上代理给父类中的构造器。
② 指定构造器必须在为继承的属性设置新值之前向上代理调用父类构造器。假如没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
③ 便利构造器必须为任意属性(包罗所有同类中定义的)赋新值之前代理调用别的构造器。假如没这么做,便利构造器赋予的新值将被该类的指定构造器所覆盖。
④ 构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。
15、OC和Swift的子类默认环境下会继承父类的构造器吗?
OC会,Swift不会
16、在子类中写一个与父类便利构造器相匹配的构造器必要加override吗?
不必要,由于Swift默认不继承父类的构造器,它们没有重写关系
17、构造器的继承规则是什么?
(1) 默认环境不会继承
(2) 假如子类引入的新属性都提供了默认值,且没有定义任何指定构造器,则自动继承父类的所有指定构造器。
(3) 假如子类提供了父类的所有指定构造器实现(无论是自动继承的,还是手动定义的),自动继承父类的所有便利构造器。
18、可失败构造器和非可失败构造器的参数署名(参数名+参数范例)可以相同吗?
不可以
19、可以把可失败构造器重写为非可失败的吗?
可以,反之不可。当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值举行强制解包。
20、什么是须要构造器(required)?
用于表明所有该类的子类都必须实现该构造器(不必要加override),它不但可以修饰指定构造器,还可以修饰便利构造器。
21、如何利用闭包或函数设置属性的默认值
let someProperty: SomeType = {
return someValue
}() (1) 闭包结尾的花括号背面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。假如你忽略了这对括号,相称于将闭包自己作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
(2) 假如你利用闭包来初始化属性,请记住在闭包执行时,实例的别的部分都还没有初始化。这意味着你不能在闭包里访问别的属性,纵然这些属性有默认值。同样,你也不能利用隐式的self属性,大概调用任何实例方法。
22、什么是析构器(deinit)
用于类实例在释放前举行清理工作(析构器会被立即调用),只实用于类范例
23、任何范例(Any)和任意对象(AnyObject)有什么区别
AnyObject是class的对象,Any是任意范例的对象,前者是后者的子集
24、Swift五大访问控制级别
(1) open(开放):模块外(只能用于类和类成员,可被模块外继承和重写)
(2) public(公开):模块外
(3) internal(内部):模块内
(4) fileprivate(文件):文件作用域
(5) private(私有):地区作用域
25、可测试特性(@testable)可以访问哪些访问控制级别的实体?
所有的,便于举行单位测试
26、范例成员的默认访问级别规则
(1) 一个public范例的所有成员的访问级别默认为internal级别,而不是public级别。
(2) 但假如你将范例指定为private大概fileprivate级别,那么该范例的所有成员的默认访问级别也会变成private大概fileprivate级别。
27、元组的访问控制级别规则
元组的访问级别将由元组中访问级别最严酷的范例来决定。例如,假如你构建了一个包含两种差别范例的元组,其中一个范例为internal,另一个范例为private,那么这个元组的访问级别为private。
28、函数的访问控制级别规则
由访问级别最严酷的参数范例或返回范例的访问级别来决定。但是,假如这种访问级别不符合函数定义所在环境的默认访问级别,那么就必要明确地指定该函数的访问级别。
29、枚举及其成员的访问控制级别规则
(1) 枚举定义中的任何原始值或关联值的范例的访问级别至少不能低于枚举范例的访问级别
(2) 枚举成员的访问级别和该枚举范例相同
30、嵌套范例的访问控制级别规则
嵌套范例的访问级别和包含它的范例的访问级别相同,嵌套范例是public的环境除外。在一个public的范例中定义嵌套范例,那么嵌套范例自动拥有internal的访问级别。假如你想让嵌套范例拥有public访问级别,那么必须显式指定该嵌套范例的访问级别为public。
31、子类的访问控制级别规则
一个子类的访问级别不得高于父类的访问级别,可以通过重写给所继承类的成员提供更高的访问级别。
32、Setter的访问级别可以低于对应的Getter的访问级别吗?
可以,接纳该方式可以控制变量、属性或下标的读写权限。
33、结构体默认的成员逐一构造器的默认访问控制级别是什么?
假如结构体中任意存储型属性的访问级别为private,那么该结构体默认的成员逐一构造器的访问级别就是private。否则,这种构造器的访问级别依然是internal。
34、什么环境下,自定义范例会为等价运算符提供默认实现?
(1) 假如是引用范例必要显示实现Equatable协议
(2) 假如是值范例:结构体->只拥有存储属性,并且它们全都遵照Equatable协议的结构体;枚举->没有关联范例;只拥有关联范例,并且它们全都遵照Equatable协议的枚举
三、扩展、协议和泛型
1、扩展(extension)的利用规则
不可以添加存储属性、不能为现有属性添加观察器、不可以重写方法、不可以添加指定构造器
2、协议规定的属性应该用盘算属性还是存储属性实现?
都可以,协议只规定属性为可读写性{ get set }的变量(var关键字修饰),不要求是存储的还是盘算的,由实现该协议的具体范例去定义(变量、常量或盘算属性)。
3、协议规定的类方法用什么修饰?
static
4、协议规定的方法可以提供默认值吗?
不可以
5、协议规定的异变方法(mutating)如何实现?
由类实现不必要mutating修饰;由结构体或枚举实现必要mutating修饰
6、实现协议规定的构造器时,为什么通常将其定义为须要的(required)?
以确保其所有的子类都提供此构造器实现,从而也能遵照该协议。(final类除外)
7、什么是类专属协议?
继承至AnyObject的协议为类专属协议,限制协议只能被类范例采取
8、什么是OC专属协议?
标记@objc特性的协议为OC专属协议,限制只能被继承自Objective-C类的类大概@objc类遵照
9、如作甚协议提供默认实现?
可以通过协议扩展来为协议要求的方法、盘算属性提供默认的实现。假如遵照协议的范例为这些要求提供了自己的实现,那么这些自定义实现将会替换扩展中的默认实现被利用。
四、并发
1、简单形貌一下Swift语言级的并发支持
Swift语言级的并发支持接纳async/await语法,它是构建在线程上的,但并不会直接跟线程举行交互。
2、什么是异步函数?
异步函数是指在返回箭头前利用了async关键字的函数,表示该函数能在执行一部分代码时被挂起而暂停执行(线程让步)。
3、Task.yield函数的作用?
用于显式地插入挂出发点,从而让其他使命有机会执行。
4、如何处置惩罚异步的可失败函数(async throws)?
(1) do-catch代码块
(2) 转换为异步的Result函数
5、什么是异步序列?
遵照AsyncSequence协议的范例叫异步序列,可通过for-await-in举行遍历。
6、如何实现多个异步方法并行执行?
接纳async-let来定义异步常量,然后将它们放在数组中举行await。如:
async let firstPhoto = downloadPhoto(named: photoNames)
async let secondPhoto = downloadPhoto(named: photoNames)
async let thirdPhoto = downloadPhoto(named: photoNames)
let photos = await show(photos) 7、什么是Swift语言级中的使命?
(1) 异步方法大概async-let定义的常量被称为一个使命
(2) 使命具有继承特性,好比放在同一个使命组中的使命拥有相同的父使命,而每个使命也可以拥有自己的子使命。
(3) 父子使命之间的关系:
① 父使命必要等候所有子使命的完成
② 当设置子使命更高的优先级时,父使命的优先级会随之提拔
③ 取消父使命时,其所有子使命也会随之取消
④ 父使命的当地值会自动且有效地流传到子使命中
8、如何执行Swift语言级中的使命组?
withTaskGroup或withThrowingTaskGroup可以执行使命组
9、正在工作的使命被取消时,可能会出现哪种环境?
(1) 抛出错误,好比CancellationError
(2) 返回nil或空集合
(3) 返回部分完成的工作结果
10、使命取消涉及的接口有哪些?
Task.checkCancellation、Task.isCancelled、TaskGroup.addTaskUnlessCancelled和Task.withTaskCancellationHandler等
11、什么是Swift语言级中的非结构化使命?
它没有父使命,由开辟者举行自行管理,从而拥有完整的灵活性。创建方法:async或asyncDetached。
12、什么是行为体?
行为体(actor)可以看作是一种特别的类范例。它是一种隔离技能,在同一时间,它只允许一个使命访问它的可变属性,因此它是线程安全的。
13、什么是并发域,什么是可发送范例?
并发域是指可线程安全访问的可变数据,如行为体实例中的可变属性。可发送范例(Sendable)是指可以在并发域之间举行共享的范例。
14、可发送范例的要求是什么?
(1) 假如是枚举范例要遵照Sendable协议,它要求其关联值都是可发送的。
(2) 假如是结构体范例要遵照Sendable协议,它要求其存储属性都是可发送的大概其只能包含只读属性。
(3) 假如是类范例要遵照Sendable协议,它要求其必须标注@MainActord大概其在特定线程或队列中只能被串行访问其属性。
五、其他
1、defer语句的作用?
defer语句用于在代码离开当前代码块前执行一个语句集合,通常用于清理工作。
2、闭包、协议方法和下标的参数要求是怎样的?
(1) 闭包和协议方法:不可以有默认值
(2) 下标:不可以有默认值和不能是输入输出的
3、Swift中出现内存访问冲突的主要场景是什么?
输入输出参数(in-out)大概异变方法(mutating)
4、Swift中解决循环引用的方法有哪些?
(1) 弱引用(weak)
弱引用不会对其引用的实例保持强引用,声明属性大概变量时,在前面加上weak关键字表明这是一个弱引用。ARC会在引用的实例被烧毁后自动将其弱引用赋值为nil。并且由于弱引用必要在运行时允许被赋值为nil,所以它们会被定义为可选范例变量,而不是常量。当ARC设置弱引用为nil时,属性观察不会被触发。
(2) 无主引用(unowned)
和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用差别的是,无主引用在其他实例有相同大概更长的生命周期时利用。你可以在声明属性大概变量时,在前面加上关键字unowned表示这是一个无主引用。无主引用通常都被盼望拥有值。不过ARC无法在实例被烧毁后将无主引用设为nil,由于非可选范例的变量不允许被赋值为nil。
5、弱引用和无主引用的利用场景
(1) 两个类相互引用的属性都允许为nil,生命周期短的接纳弱引用。【房子的业主(人)】如一个房子可能拥有一个业主,一个人也可能拥有一个房子,房子的业主属性接纳弱引用(先有人再有房子,业主的生命周期更短)。
(2) 两个类相互引用的属性,一个允许为nil、另一个不允许,不允许的接纳无主引用。【信用卡的客户(人)】如一个人可能拥有一张信用卡,一张信用卡必须拥有一个客户,信用卡的客户属性接纳无主引用。
(3) 两个类相互引用的属性都不允许为nil,必要在构造器中调用另一个类构造器(转达自身self)举行赋值的属性接纳隐式解包可选属性,另一个接纳无主引用。【国家的首都(都会)】如每个国家必须有首都,每个都会必须属于一个国家,国家的首都属性接纳隐式解包可选属性,都会的国家属性接纳无主引用。
6、循环引用产生的两种场景
(1) 两个类的属性相互引用
(2) 在类的闭包属性中利用类实例(闭包捕获列表:闭包“捕获”self,产生强引用)
7、闭包捕获列表如何解决循环引用问题?
(1) 在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个大概多个引用范例的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定利用弱引用还是无主引用。(即,闭包通过定义捕获列表中的参数为弱引用或无主引用来解决循环引用问题)
(2) 在闭包和捕获的实例总是互相引用并且总是同时烧毁时,将闭包内的捕获定义为无主引用。相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选范例,并且当引用的实例被烧毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。原则:假如被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。
8、属性包装器(@propertyWrapper)有什么作用?
属性包装器用于在属性的定义和存储之间添加一个分隔层
9、什么是不透明范例?
(1) 在函数或属性返回一个协议时,在前面加上some关键字。从而要求它们返回一个具体范例,但不对调用者暴露。
(2) 返回不透明范例可以让编译器知道具体的返回范例,从而举行范例检查的同时,也能进步性能。
(3) 相比直接返回协议,实际上就是让这个函数或属性支持多态,编译器并不知道返回的具体范例,必要通过动态派发的方式来对协议调用方法或属性,从而低落性能。
10、什么是包装协议范例?
(1) 用于将协议范例封装为一种具体的范例,便于在步伐中灵活处置惩罚不确定的范例实现。
(2) 包装协议范例有两种实现方式:
① 用Any/AnyObject包装任意范例或任意类范例,必要手动举行范例转换来利用。
② 用Existential范例包装某个协议范例,无需举行范例转换而可直接调用协议方法。
(3) 包装协议范例还可以实现范例擦除,用于包装含有associatedtype或Self要求的协议,从而绕过它作为参数时的语法限制。
11、Swift中的宏分为哪两类?
(1) 独立宏:以#开头,如#function和#warning等,它用于产生一个值。(产生值)
(2) 附加宏:以@开头,如@OptionSet等,它用于向添加该修饰的声明添加代码。(附加代码)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]