美文网首页面试IOSswift面试
答《卓同学的 Swift 面试题》下

答《卓同学的 Swift 面试题》下

作者: yww | 来源:发表于2017-08-30 13:47 被阅读1970次

    原文链接 卓同学的 Swift 面试题
    上篇回答 http://www.jianshu.com/p/23d99f434281

    dynamic 的作用

    由于 swift 是一个静态语言, 所以没有 Objective-C 中的消息发送这些动态机制, dynamic 的作用就是让 swift 代码也能有 Objective-C 中的动态机制, 常用的地方就是 KVO 了, 如果要监控一个属性, 则必须要标记为 dynamic, 可以参考我的文章http://www.jianshu.com/p/ae26100b9edf

    什么时候使用 @objc

    @objc 用途是为了在 Objective-C 和 Swift 混编的时候, 能够正常调用 Swift 代码. 可以用于修饰类, 协议, 方法, 属性.
    常用的地方是在定义 delegate 协议中, 会将协议中的部分方法声明为可选方法, 需要用到@objc

    @objc protocol OptionalProtocol {
        @objc optional func optionalFunc()
        func normalFunc()
    }
    class OptionProtocolClass: OptionalProtocol {
        func normalFunc() {
        }
    }
    let someOptionalDelegate: OptionalProtocol = OptionProtocolClass()
    someOptionalDelegate.optionalFunc?()
    

    Optional(可选型) 是用什么实现的

    Optional 是一个泛型枚举
    大致定义如下:

    enum Optional<Wrapped> {
      case none
      case some(Wrapped)
    }
    

    除了使用 let someValue: Int? = nil 之外, 还可以使用let optional1: Optional<Int> = nil 来定义

    如何自定义下标获取

    实现 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 的区别是什么

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

    相关文章

      网友评论

        本文标题:答《卓同学的 Swift 面试题》下

        本文链接:https://www.haomeiwen.com/subject/ctfadxtx.html