美文网首页
swift小问题

swift小问题

作者: 雷霸龙 | 来源:发表于2021-03-18 13:40 被阅读0次

    1、struct和class的区别

    swift中,class是引用类型,struct是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。

    class有这几个功能struct没有的:

    • class可以继承,这样子类可以使用父类的特性和方法
    • 类型转换可以在runtime的时候检查和解释一个实例的类型
    • 可以用deinit来释放资源
    • 一个类可以被多次引用

    struct也有这样几个优势:

    • 结构较小,适用于复制操作,相比于一个class的实例被多次引用更加安全。
    • 无须担心内存memory leak或者多线程冲突问题

    顺便提一下,array在swift中是用struct实现的。Apple重写过一次array,然后复制就是深度拷贝了。猜测复制是类似参照那样,通过栈上指向堆上位置的指针来实现的。而对于它的复制操作,也是在相对空间较为宽裕的堆上来完成的,所以性能上还是不错的。

    2、map、filter、reduce函数

    • map 可以对数组中的每一个元素做一次处理
    • filter:过滤,可以对数组中的元素按照某种规则进行一次过滤
    • reduce:计算,可以对数组的元素进行计算

    3、map 和 flatMap

    • map:是对原对象所有元素进行一对一转换处理,中间不会跳过或遗漏,包括nil元素,map 方法接受一个闭包作为参数, 然后它会遍历整个 numbers 数组,并对数组中每一个元素执行闭包中定义的操作。 相当于对数组中的所有元素做了一个映射。
    • flatMap:更灵活,可变换维度,也能够自动解包,所以当我们对不符合元素,返回nil,最终的结果是过滤掉nil的,从而能够实现过滤。

    4、写入时复制 copy-on-write

    swift的数组是值类型,值类型的一个特点是在传递和赋值时进行复制。swift使用了copy-on-write来避免频繁复制带来的额外开销。只有当多个对象指向相同的资源,其中一个对象尝试修改资源内容时,copy才会触发。

    Swift针对标准库中的集合类型(Array、Dictionary、Set)进行优化。当变量指向的内存空间并没有发生改变,进行拷贝时,只会进行浅拷贝。只有当值发生改变时才会进行深拷贝。

    Array、Dictinary、Set每次进行修改前,都会通过类似isUniquelyReferencedNonObjC函数进行判断,判断是否是唯一的引用(即引用计数器为1)。若不为1,则创建新的类型值并返回。若是唯一的则直接赋值。

    写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

    5、获取当前代码的函数名和行号

    字面量 类型
    #file String 所在的文件名
    #line Int 所在的行数
    #column Int 所在的列数
    #function String 所在的函数的名字
    例如:
    // 函数名
    func logFunctionName(string: String = #function) {
        print(string)
    }
    func myFunction() {
        logFunctionName()
    }
    myFunction() // 打印
    
    // 行数
    func logFunctionName(count: Int = #line) {
        print(count)
    }
    func myFunction() {
        logFunctionName()
    }
    myFunction() // 打印
    
    
    也可以简单如下使用
    func myFunction() {
        print("\(#function)")
    }
    myFunction() // 打印
    
    func myFunction() {
        print(“\(#line)”)
    }
    myFunction() // 打印
    

    6、如何声明一个只能被类 conform(遵循) 的 protocol

    协议的继承列表中,通过添加 class 关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。class 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:

    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
        // 这里是类类型专属协议的定义部分
    }
    

    7、defer 使用场景

    defer 语句块中的代码, 会在当前作用域结束前调用, 常用场景如异常退出后, 关闭数据库连接
    func someQuery() -> ([Result], [Result]){
        let db = DBOpen("xxx")
        defer {
            db.close()
        }
        guard results1 = db.query("query1") else {
            return nil
        }
        guard results2 = db.query("query2") else {
            return nil
        }
        return (results1, results2)
    }
    
    需要注意的是, 如果有多个 defer, 那么后加入的先执行
    func someDeferFunction() {
        defer {
            print("\(#function)-end-1-1")
            print("\(#function)-end-1-2")
        }
        defer {
            print("\(#function)-end-2-1")
            print("\(#function)-end-2-2")
        }
        if true {
            defer {
                print("if defer")
            }
            print("if end")
        }
        print("function end")
    }
    someDeferFunction()
    // 输出
    // if end
    // if defer
    // function end
    // someDeferFunction()-end-2-1
    // someDeferFunction()-end-2-2
    // someDeferFunction()-end-1-1
    // someDeferFunction()-end-1-2
    

    8、什么时候使用 final

    1. 类或者方法的功能确实已经完备了,通常是一些辅助性质的工具类或者方法,比如MD5加密类这种,算法都十分固定,我们基本不会再继承和重写
    2. 避免子类继承和修改造成危险
    3. 为了让父类中某些代码一定会执行

    9、public 和 open 的区别

    open

    • open 修饰的 class 在 Module 内部和外部都可以被访问和继承
    • open 修饰的 func 在 Module 内部和外部都可以被访问和重载(override)

    public

    • public 修饰的 class 在 Module 内部可以访问和继承,在外部只能访问
    • public 修饰的 func 在 Module 内部可以被访问和重载(override),在外部只能访问

    10、dynamic 的作用

    由于 swift 是一个静态语言, 所以没有 Objective-C 中的消息发送这些动态机制, dynamic 的作用就是让 swift 代码也能有 Objective-C 中的动态机制, 常用的地方就是 KVO 了, 如果要监控一个属性, 则必须要标记为 dynamic。
    首先需要知道的是, KVO, KVC 都是Objective-C 运行时的特性, Swift 是不具有的, 想要使用, 必须要继承 NSObject, 自然, 继承都没有的结构体也是没有 KVO, KVC 的, 这是前提.。
    例如, 下面的这段错误代码:

    class SimpleClass {
        var someValue: String = "123"
    }
    //SimpleClass().setValue("456", forKey: "someValue") // 错误, 必须要继承自 NSObject
    
    KVC

    Swift 下的 KVC 用起来很简单, 只要继承 NSObject 就行了.

    class KVCClass :NSObject{
        var someValue: String = "123"
    }
    let kvc = KVCClass()
    kvc.someValue // 123
    kvc.setValue("456", forKey: "someValue")
    kvc.someValue // 456
    
    KVO

    KVO 就稍微麻烦一些了,由于 Swift 为了效率, 默认禁用了动态派发, 因此想用 Swift 来实现 KVO, 我们还需要做额外的工作, 那就是将想要观测的对象标记为 dynamic.

    class KVOClass:NSObject {
        dynamic var someValue: String = "123"
        var someOtherValue: String = "abc"
    }
    
    class ObserverClass: NSObject {
        func observer() {
            let kvo = KVOClass()
            kvo.addObserver(self, forKeyPath: "someValue", options: .new, context: nil)
            kvo.addObserver(self, forKeyPath: "someOtherValue", options: .new, context: nil)
            kvo.someValue = "456"
            kvo.someOtherValue = "def"
            kvo.removeObserver(self, forKeyPath: "someValue")
            kvo.removeObserver(self, forKeyPath: "someOtherValue")
        }
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            print("\(keyPath!) change to \(change![.newKey] as! String)")
        }
    }
    ObserverClass().observer()
    这段代码只会输出someValue change to 456
    

    11、什么时候使用 @objc

    • @objc 用途是为了在 Objective-C 和 Swift 混编的时候, 能够正常调用 Swift 代码. 可以用于修饰类, 协议, 方法, 属性.
    • 常用的地方是在定义 delegate 协议中, 会将协议中的部分方法声明为可选方法, 需要用到@objc
    • 在协议中使用 optional 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上@objc属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议

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

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

    enum Optional<Wrapped> {
      case none
      case some(Wrapped)
    }
    除了使用 let someValue: Int? = nil 之外, 还可以使用let optional1: Optional<Int> = nil 来定义
    

    13、什么是高阶函数

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

    14、如何解决引用循环

    1. 转换为值类型, 只有类会存在引用循环, 所以如果能不用类, 是可以解引用循环的
    2. delegate 使用 weak 属性
    3. 闭包中, 对有可能发生循环引用的对象, 使用 weak 或者 unowned 修饰 (在引用对象的生命周期内,如果它可能为nil,那么就用weak引用。反之,当你知道引用对象在初始化后永远都不会为nil就用unowned)

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

    • static 和 class都是用来指定类方法
    • class关键字指定的类方法 可以被 override
    • static关键字指定的类方法 不能被 override

    相关文章

      网友评论

          本文标题:swift小问题

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