Swift面试题

267 阅读8分钟

Swift和OC的区别

语言层面

  1. swift是静态语言,有类型推断,OC是动态语言。静态语言和动态语言

  2. swift是一门支持多编程范式的语言,既支持面向对象编程,也支持面向协议编程,同时还支持函数式编程,OC面向对象编程。

  3. swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,OC支持动态派发(消息派发)方式。Swift派发机制

    image.png

  4. swift单文件、oc双文件。

语法层面

  1. swift注重值类型,OC注重引用类型。
  2. swift支持泛型,OC只支持轻量泛型。
  3. swift的协议不仅可以被类实现,也可以被struct和enum实现。
  4. swift有元组类型、支持运算符重载。
  5. swift支持命名空间,NSClassFromString需要加上命名空间(命名空间.类名)。
  6. swift支持默认参数。
  7. swift比oc代码更加简洁。
  8. oc的类都是集成于NSObject,swift可以定义不继承于任何的类。
  9. swift的switch可以判断类型比较多,oc只能判断基本数据类型。
  10. swift支持运算符重载
  11. swift有可选类型
  12. 枚举

Swift比Objective-C优势

  1. swift容易阅读,语法和文件结构简易化。
  2. swift更易于维护,文件分离后结构更清晰。
  3. swift更加安全,它是类型安全的语言:
  4. swift代码更少,简洁的语法,可以省去大量冗余代码。
  5. swift速度更快,运算性能更高。

便利构造函数 convenience

便利构造函数通常用在对系统的类进行构造函数的扩充时使用

便利构造函数的特点:

//1.能够提供条件检测
//2.能够允许返回 nil,默认(指定)的构造函数,必须要创建对象
//3.便利构造函数,必须在条件检测完成之后,以 self. 的方式调用其他的构造函数,创建对象
//4.能够简化对象的创建方法
convenience init?(name: String, age: Int){
    if age < 0 || age > 100 {
        //不能创建对象
        return nil
    }

    //调用其他的构造函数初始化属性 在一个构造函数中调用了另外一个构造函数
    self.init(dict: ["name":name, "age": age])
}

Class和Struct的区别

struct SRectangle {
    var width = 200
}

class CRectangle {
    var width = 200
}

虽然两者写法看起来很像,其实区别还是很大的:

  1. struct不可以继承,class可以继承。
  2. Struct无法修改自身属性值,函数需要添加mutating关键字
  3. 类中的每一个成员变量都必须被初始化,否则编译器会报错,而结构体不需要,编译器会自动帮我们生成init函数,给变量赋一个默认值
  4. 类属于引用类型,结构体属于值类型
    1. 值类型变量直接包含数据,赋值时也是值拷贝,或者叫深拷贝,所以多个变量的操作不会相互影响。
    2. 引用类型变量存储的是对数据的引用地址,后者称为对象,赋值时,是将对象的引用地址复制过去,也叫浅拷贝,因此若多个变量指向同一个对象时,操作会相互影响。
    3. 值类型数据没有引用计数,也就不会因为循环引用导致内存泄漏,而引用类型存在引用计数,需要小心循环引用导致的内存泄漏
    4. 拷贝时,struct是深拷贝,拷贝的是内容,class则需要选用正确的深浅拷贝类型。
    5. 因为值类型数据是深拷贝,所以是线程安全的,而引用类型数据则不是

inout

swift中需要对参数只进行修改,需要用到inout 关键字,调用函数时加&

通俗的讲,就是使用inout关键字修饰的值,在接下来的方法中可以修改

final

Swift中,final关键字可以在class、func和var前修饰,表示不允许对其修饰的内容进行继承或者重新操作

final:防止方法、属性、下标被重写。

final class Person{}

class Programmer: Person{} //编译错误

is:类型检查运算符,用于确定实例是否为某个子类类型。

dynamic

dynamic的作用就是让swift代码也能有oc中的动态机制,常用的就是KVO。

使用dynamic关键字标记属性,使属性启用Objc的动态转发功能。

dynamic只用于类,不能用于结构体和枚举,因为它们没有继承机制,而Objc的dynamic:指明编译器不会对类成员或者函数的方法进行内联或虚拟化。这意味着对这个成员的访问是使用 Objective-C 运行时进行动态派发的(代替静态调用)。

class Person {
    //比如 KVO、KVC、Swizzling
    dynamic var name: String?
}

defer

defer的字面意思是延时执行,在swift中修饰一个block,该block内的代码会在当前函数的所有代码执行完毕之后才会被执行。

  1. 必须有执行到defer才会触发defer,如果提前return或抛出异常则不会执行。
  2. 多个defer,执行顺序是倒序的。最后定义的defer先执行。

@objc与@objcMembers的区别

在Swift中,继承自NSObject的类如果有比较多的属性或方法都需要加上@objc的话,会多比较多的代码。那么可以利用@objcMembers减少代码。被@objcMembers修饰的类,会默认为类、子类、类扩展和子类扩展的所有属性和方法都加上@objc。当然如果想让某一个扩展关闭@objc,则可以用@nonobjc进行修饰。

  1. selector调用的方法前需要加@objc,目的是允许函数在“运行时”通过oc消息机制调用
override func viewDidLoad() {
    super.viewDidLoad() 
    let btn = UIButton(type:.contactAdd)
    btn.addTarget(self,action:#selector(click),for:.touchUpInside)
}
@objc func click(){
    print("Button clicked")
}
  1. 协议的方法可选时,协议和可选方法前要用@objc声明
@objc protocol OptionalProtocol{
    @objc optional func optionalMethold1()
    @objc optional func optionalMethold2()
}
  1. 用weak修饰协议时,协议前面要用@objc声明
@objc protocol MyDelegate{
    funcmethold1()
}
class MyClass{
    weak var delegate:MyDelegate?
}
  1. 类前加上 @objcMembers,那么它及其子类、扩展里的方法都会隐式的加上 @objc
@objcMembers class Person{}
  1. 扩展前加上 @objc,那么里面的方法都会隐式加上 @objc

class Person{
    funcwork(){}
}
@objc extension Person{
    funceat(){}//包含隐式的 
    @objc func sleep(){}//包含隐式的 @objc
}

copy-on-write

当你有一个占用内存很大的一个值类型,并且不得不将它赋值给另一个变量或者当做函数的参数传递的时候,拷贝它的值是一个非常消耗内存的操作,因为你不得不拷贝它所有的东西放置在另一块内存中。

为了优化这个问题,Swift 对于一些特定的值类型(集合类型:Array、Dictionary、Set)做了一些优化,在对于 Array 进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int 等值类型,在赋值的时候就会发生拷贝。

  • Copy-on-Write 是一种用来优化占用内存大的值类型的拷贝操作的机制。
  • 对于 Int、String 等基本类型的值类型,它们在赋值的时候就会发生拷贝,- - 它们没有 Copy-on-Write 这一特性(因为Copy-on-Write带来的开销往往比直接复制的开销要大)。
  • 对于 Array、Dictionary、Set类型,当它们赋值的时候不会发生拷贝,只有在修改的之后才会发生拷贝,即 Copy-on-Write。
  • 对于自定义的结构体,不支持 Copy-on-Write。

open, public, internal, fileprivate, private

  1. open: 具备最高访问权限,其修饰的类可以和方法,可以在任意 模块中被访问和重写.
  2. public: 权限仅次于 open,和 open 唯一的区别是: 不允许其他模块进行继承、重写
  3. internal: 默认权限, 只允许在当前的模块中访问,可以继承和重写,不允许在其他模块中访问
  4. fileprivate: 修饰的对象只允许在当前的文件中访问;
  5. private: 最低级别访问权限,只允许在定义的作用域内访问

Swift 和OC 如何相互调用

  1. Swift 调用 OC代码

需要创建一个 Target-Bridging-Header.h 的桥接文件,在桥接文件导入需要调用的OC代码头文件即可

  1. OC 调用 Swift代码

直接导入 Target-Swift.h文件即可, Swift如果需要被OC调用,需要使用@objc对方法或者属性进行修饰;如果想要所有成员都暴露给OC,就使用@objcMembers来修饰类;暴露给OC的类需要最终继承自NSObject

typealias和associatedtype的区别

associatedtype:关联类型的关键字,处理 协议 中的范型使用场景。

associatedtype 只能在 protocol 中使用,class 中应该使用typealias进行代替。

protocol Stack {
    associatedtype Element
    
    func push(e: Element) -> Void
    
    func pop() -> Element
}
class Stack<T>: Stackble {
    typealias Element = T
    
    func push(e: T) {
        
    }
    
    func pop() -> T? {
        return nil
    }
}

/// 指定类型 Stack
let strStack = Stack<String>()
strStack.push(e: "")
let result = strStack.pop()