注册

iOS 面试题 八股文 1.6

如何自定义下标获取


实现 subscript 即可, 如

extension AnyList {
subscript(index: Int) -> T{
return self.list[index]
}
subscript(indexString: String) -> T?{
guard let index = Int(indexString) else {
return nil
}
return self.list[index]
}
}


索引除了数字之外, 其他类型也是可以的


?? 的作用


可选值的默认值, 当可选值为nil 的时候, 会返回后面的值. 如

let someValue = optional1 ?? 0


lazy 的作用


懒加载, 当属性要使用的时候, 才去完成初始化

class LazyClass {
lazy var someLazyValue: Int = {
print("lazy init value")
return 1
}()
var someNormalValue: Int = {
print("normal init value")
return 2
}()
}
let lazyInstance = LazyClass()
print(lazyInstance.someNormalValue)
print(lazyInstance.someLazyValue)
// 打印输出
// normal init value
// 2
// lazy init value
// 1


一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示


需要实现自 OptionSet, 一般使用 struct 实现. 由于 OptionSet 要求有一个不可失败的init(rawValue:) 构造器, 而 枚举无法做到这一点(枚举的原始值构造器是可失败的, 而且有些组合值, 是没办法用一个枚举值表示的)

struct SomeOption: OptionSet {
let rawValue: Int
static let option1 = SomeOption(rawValue: 1 << 0)
static let option2 = SomeOption(rawValue:1 << 1)
static let option3 = SomeOption(rawValue:1 << 2)
}
let options: SomeOption = [.option1, .option2]


inout 的作用


输入输出参数, 如:

func swap( a: inout Int, b: inout Int) {
let temp = a
a = b
b = temp
}
var a = 1
var b = 2
print(a, b)// 1 2
swap(a: &a, b: &b)
print(a, b)// 2 1


Error 如果要兼容 NSError 需要做什么操作


其实直接转换就可以, 例如 SomeError.someError as NSError 但是这样没有错误码, 描述等等, 如果想和 NSError 一样有这些东西, 只需要实现 LocalizedErrorCustomNSError 协议, 有些方法有默认实现, 可以略过, 如:

enum SomeError: Error, LocalizedError, CustomNSError {
case error1, error2
public var errorDescription: String? {
switch self {
case .error1:
return "error description error1"
case .error2:
return "error description error2"
}
}
var errorCode: Int {
switch self {
case .error1:
return 1
case .error2:
return 2
}
}
public static var errorDomain: String {
return "error domain SomeError"
}
public var errorUserInfo: [String : Any] {
switch self {
case .error1:
return ["info": "error1"]
case .error2:
return ["info": "error2"]
}
}
}
print(SomeError.error1 as NSError)
// Error Domain=error domain SomeError Code=1 "error description error1" UserInfo={info=error1}


下面的代码都用了哪些语法糖


[1, 2, 3].map{ $0 * 2 }

[1, 2, 3] 使用了, Array 实现的ExpressibleByArrayLiteral 协议, 用于接收数组的字面值

map{xxx} 使用了闭包作为作为最后一个参数时, 可以直接写在调用后面, 而且, 如果是唯一参数的话, 圆括号也可以省略

闭包没有声明函数参数, 返回值类型, 数量, 依靠的是闭包类型的自动推断

闭包中语句只有一句时, 自动将这一句的结果作为返回值

0 在没有声明参数列表的时候, 第一个参数名称为0, 后续参数以此类推


什么是高阶函数


一个函数如果可以以某一个函数作为参数, 或者是返回值, 那么这个函数就称之为高阶函数, 如 map, reduce, filter


如何解决引用循环



  1. 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的,
  2. delegate 使用 weak 属性.
  3. 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned, 修饰

下面的代码会不会崩溃,说出原因

var mutableArray = [1,2,3]
for _ in mutableArray {
mutableArray.removeLast()
}


不会, 原理不清楚, 就算是把 removeLast(), 换成 removeAll() ,这个循环也会执行三次, 估计是在一开始, for

in 就对 mutableArray 进行了一次值捕获, 而 Array 是一个值类型 , removeLast() 并不能修改捕获的值.


给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明


使用 where 子句, 限制 Element 为 String

extension Array where Element == String {
var isStringElement:Bool {
return true
}
}
["1", "2"].isStringElement
//[1, 2].isStringElement// error


定义静态方法时关键字 static 和 class 有什么区别


static 定义的方法不可以被子类继承, class 则可以

class AnotherClass {
static func staticMethod(){}
class func classMethod(){}
}
class ChildOfAnotherClass: AnotherClass {
override class func classMethod(){}
//override static func staticMethod(){}// error
}


一个 Sequence 的索引是不是一定从 0 开始?


不一定, 两个 for in 并不能保证都是从 0 开始, 且输出结果一致, 官方文档如下



Repeated Access


The Sequence protocol makes no requirement on conforming types regarding

whether they will be destructively consumed by iteration. As a

consequence, don't assume that multiple for-in loops on a sequence

will either resume iteration or restart from the beginning:

for element in sequence {
if ... some condition { break }
}

for element in sequence {
// No defined behavior
}



有些同学还是不太理解, 我写了一个demo 当作参考

class Countdown: Sequence, IteratorProtocol {
var count: Int
init(count: Int) {
self.count = count
}
func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}

var countDown = Countdown(count: 5)
print("begin for in 1")
for c in countDown {
print(c)
}
print("end for in 1")
print("begin for in 2")
for c in countDown {
print(c)
}
print("end for in 2")


最后输出的结果是

begin for in 1
5
4
3
2
1
end for in 1
begin for in 2
end for in 2


很明显, 第二次没有输出任何结果, 原因就是在第二次for in 的时候, 并没有将count 重置.


数组都实现了哪些协议


MutableCollection, 实现了可修改的数组, 如 a[1] = 2

ExpressibleByArrayLiteral, 实现了数组可以从[1, 2, 3] 这种字面值初始化的能力

...


如何自定义模式匹配


这部分不太懂, 贴个链接吧

http://swifter.tips/pattern-match/


autoclosure 的作用


自动闭包, 会自动将某一个表达式封装为闭包. 如

func autoClosureFunction(_ closure: @autoclosure () -> Int) {
closure()
}
autoClosureFunction(1)


详细可参考http://swifter.tips/autoclosure/


编译选项 whole module optmization 优化了什么


编译器可以跨文件优化编译代码, 不局限于一个文件.

http://www.jianshu.com/p/8dbf2bb05a1c


下面代码中 mutating 的作用是什么

struct Person {
var name: String {
mutating get {
return store
}
}
}


让不可变对象无法访问 name 属性


如何让自定义对象支持字面量初始化


有几个协议, 分别是

ExpressibleByArrayLiteral 可以由数组形式初始化

ExpressibleByDictionaryLiteral 可以由字典形式初始化

ExpressibleByNilLiteral 可以由nil 值初始化

ExpressibleByIntegerLiteral 可以由整数值初始化

ExpressibleByFloatLiteral 可以由浮点数初始化

ExpressibleByBooleanLiteral 可以由布尔值初始化

ExpressibleByUnicodeScalarLiteral

ExpressibleByExtendedGraphemeClusterLiteral

ExpressibleByStringLiteral

这三种都是由字符串初始化, 上面两种包含有 Unicode 字符和特殊字符


dynamic framework 和 static framework 的区别是什么



静态库和动态库, 静态库是每一个程序单独打包一份, 而动态库则是多个程序之间共享



链接: https://www.jianshu.com/p/7c7f4b4e4efe

链接:https://www.jianshu.com/p/cc4a737ddc1d

链接:https://www.jianshu.com/p/23d99f434281

0 个评论

要回复文章请先登录注册