swift经典面试题

一、经典题

1.Swift中的常量和OC中的常量有啥区别?
OC中用 const 是用来表示常量的,而 Swift 中用 let 是用来判断是不是常量。
OC中的常量(const)是编译期决定的,Swift中的常量(let)是运行时确定的。
上面的区别更进一步说,OC中 const 表明的常量类型和数值是在 compilation time 时确定的;而 Swift 中 let 只是表明常量(只能赋值一次),其类型和值既可以是静态的,也可以是一个动态的计算方法,它们在 runtime 时确定的。

Swift中的常量可以是非特定类型的,即它们的类型可以在运行时确定。例如,你可以定义一个常量,其值可以是任何类型,只要在赋值时类型明确即可。

在Objective-C中,常量通常使用#define预处理器指令或const关键字定义。使用#define定义的常量在编译时会被替换为它们的值,而使用const定义的常量在运行时不可修改。Objective-C中的常量类型必须在编译时确定,并且不能用作函数参数。

2.指定构造器和便利构造器有什么区别?

类必须要有一个指定构造器,可以没有便利构造器
便利构造器必须调用本类另一个构造器,最终调用到本类的指定构造器
便利构造器前面需要添加convenience关键字

3.Any和AnyObject的区别?

AnyObject只能表示引用类型的任何实例,相当于Objective-C中的id类型。
Any可以表示类,结构体,枚举的任何实例。
AnyObject是Any的子集。
(延伸 oc中 id的本质是struct objc_object结构体指针,可以指向任何OC对象,id指向基础数据类型会报编译错误。)

4.In-Out(inout关键字)参数了解过吗?

默认情况下,函数参数默认是常量,试图从函数体中去改变一个函数的参数值会报编译错误。如果希望函数修改参数值,并在函数调用结束后仍然保留。这个时候就需要用到inout关键字。
注意事项:
inout关键字只能修饰变量,无法修饰常量,因为常量和字面量不能被修改。
inout参数不能有默认值,可变参数不能标记为inout。
调用函数的时候,应该在变量名前放置&符号表示该变量可以由函数修改。

var variable: Int = 1
func changeNumber(num:inout Int) {
    num = 2
    print(num)
}
changeNumber(num: &variable) // 2

inout关键字修饰的变量传递过程:

  1. 如果实参有物理内存地址,且没有设置属性观察器
    答: 直接将实参的内存地址传入函数 (实参进行引用传递)

2.如果实参是计算属性 或者 设置了属性观察器
答:采取了Copy In Copy Out 的做法

  1. 调用该函数时,先复制实参的值,产生副本 [get]
    2.将副本的内存地址传入函数 (副本进行引用传递),在函数内部可以修改副本的值
    3.函数返回后,再将副本的值覆盖实参的值 [set]
 var man = person(heigth: 1, width: 1)
    {
        willSet(newValue){
            print("即将 \(newValue) ")
        }
        didSet (oldValue){
            print("已经 \(man) \(oldValue) ")
        }
    }

 func addValue( _ value : inout person)->person
    {
        value.heigth = 20
        value.width = 20
//如果将属性观察关掉,崩溃:因为违反了独占原则:一个地址被别的地方引用后,不可以访问。Swift 需要对变量进行独占访问时才能修改该变量。本质上来说,当一个变量作为 inout 参数或者 mutating 方法中的 self 被修改时,不能通过不同的名称被访问的。
        print("新",value,"旧",self.man)

        return value
    }
//执行
  let k = addValue(&man)
  print(k)
//打印  因为有属性观察器所以 copy。 执行方法函数结束后再进入属性观察器,再给返回值。
//        新 person(heigth: 20, width: 20) 旧 person(heigth: 1, width: 1)
//        即将 person(heigth: 20, width: 20)
//        已经 person(heigth: 20, width: 20) person(heigth: 1, width: 1)
//        person(heigth: 20, width: 20)

总结:

  1. inout参数的本质是地址传递 (引用传递),不管什么情况都是传入一个地址。
  2. Swift 值类型中,属性的默认行为是不可变的。mutating关键字,用于在结构体或枚举的方法中修改属性。使用mutating修饰的方法(func)在修改属性后更新原始值,而不是返回一个新的副本。
    (mutating关键字只能用于值类型,mutating关键字本质是包装了inout关键字,加上mutating关键字后参数值会变成地址传递。
    类对象是指针,传递的本身就是地址值,所以 mutating关键字对类是透明的,加不加效果都一样。)

5.什么是自动闭包、逃逸闭包、非逃逸闭包?
非逃逸闭包, 永远不会离开一个函数的局部作用域的闭包就是非逃逸闭包。

func player(complete:(Bool)->()){ 
    complete(true) //执行闭包 传入真
}
 self.player { bool in
            print( bool ? "yes":"no")
        } // yes

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。在形式参数前写@escaping来明确闭包是允许逃逸的。

 var completionHanglers: [() -> Void] = []
        
        func someFunctionWithEscapingClosure(completionHangler: @escaping () -> Void) {
            
        completionHanglers.append(completionHangler)
        }
completionHanglers.first?()

自动闭包:是一种自动创建的闭包,用来把作为实际参数传递给函数的表达式打包的闭包.他不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值.

Autoclosure 是用于延迟执行闭包的一种技术。使用 Autoclosure,我们可以将闭包作为参数传递给函数或方法,但是闭包不会立即执行。相反,它会在需要时才会被执行。

 public   func assert(_ condition:@autoclosure () -> Bool,_ message: @autoclosure () -> String = String(), file:StaticString = #file, line: Unit = #line)

let num = 3
  assert(num>3,"num不能大于3")
var customersInLine = ["李一", "张2", "刘3", "赵四", "王五"]
    
    override func viewDidLoad() {
        super.viewDidLoad()

        print(customersInLine.count)
        // 打印出“5”
        let customerProvider = { self.customersInLine.remove(at: 0) }//自动闭包
        print(customersInLine.count)//没有执行呢还    还是打印出“5”
        print("移除了 \(customerProvider())!") //移除了 李一!
        print(customersInLine)
        
        serve(customer: customersInLine.remove(at: 0))
        // 不用  @autoclosure 修饰
        serve2(customer: { customersInLine.remove(at: 0) } )
    }
    
    func serve(customer customerProvider: @autoclosure () -> String) {
       print("移除了 \(customerProvider())!")
        print(customersInLine)
    }
    
    func serve2(customer customerProvider: () -> String) {
       print("移除了 \(customerProvider())!")
        print(customersInLine)
    }

6.什么是Optional(可选项或者叫可选类型)?
在变量类型后加问号(?)表示该变量可能有值也可能没有值
底层Optional是一个包含None和Some(Wrapped)两种类型的泛枚举类型,Optional.None即nil,Optional.Some非nil。

@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    /// The absence of a value.
    ///
    /// In code, the absence of a value is typically written using the `nil`
    /// literal rather than the explicit `.none` enumeration case.
    case none

    /// The presence of a value, stored as `Wrapped`.值的存在,存储为“Wrapped”
    case some(Wrapped)
}
var optional1: String? = nil
 var optional2: String? = .none

在OC中, nil是一个指向不存在对象的指针(OC非对象类型也可以设置成nil但是会有警告⚠️:指针不兼容)
在Swift中,nil不是指针,而是值缺失的一种特殊类型,任何类型的可选项都可以设置为nil而不仅仅是对象类型,Swift 是一种类型安全的语言,而 Objective-C 不是。这意味着在 Swift 中,每个类型的 nil 都是不同的,例如 Int? 的 nil 和 String? 的 nil 是不同的,它们所代表的空值的类型不同。
非可选项类型,不可设置为nil。Optional及可以包装值类型也可以包装引用类型。

7.swift的派发机制(函数的派发机制:静态派发(直接派发)、函数表派发、消息派发)?

1)swift中所有值类型:struct、enum使用直接派发。
2)swift中协议的extensions(扩展)使用直接派发,初始声明函数使用函数表派发
3)swift中class中extensions使用直接派发,初始化声明函数使用函数表派发,dynamic修饰的函数使用消息派发。
4)swift中NSObject的子类用@nonobjc或final修饰的函数使用直接派发,初始声明函数使用函数表派发,dynamic修饰的extensions使用消息派发

swift(关键字)显示指定派发方式?

1)添加final关键字的函数使用直接派发
2)添加static关键字函数使用直接派发
3)添加dynamic关键字函数使用消息派发
4)添加@objc关键字的函数使用消息派发
5)添加@inline关键字的函数告诉编译器可以使用直接派发

8.try、try?、try
“try”需要用“ do catch”捕捉异常,如果在“try”代码块中出现异常,程序会跳转到相应的“catch”代码块中执行异常处理逻辑,然后继续执行“catch”代码块后面的代码。

enum NormalError: Error {
    case one
    case two
    case three
    case four
}

func someFunction(words: String) throws -> String {
    switch words {
    case "one":
        throw NormalError.one
    case "two":
        throw NormalError.one
    case "three":
        throw NormalError.one
    case "four":
        throw NormalError.one
    default:
        return "ok"
    }
}
//如果在“try”代码块中出现异常,程序会跳转到“catch”代码块中执行异常处理逻辑。
do {  
    try someFunction(words: "five")  
} catch {  
    print("An error occurred: \(error)")  
}

“try?”是返回一个可选值类型,如果“try?”代码块中出现异常,返回值会是“nil”,否则返回可选值。可以在运行时判断是否有异常发生。

// 返回值是一个可选类型,如果执行正常的话就存在返回值,否则如果抛出错误的话返回值为nil
let result = try? say(words: "four")
// 可选绑定
if let res = try? doSomething(words: "four") {
}
else {  print("出现错误")  }
// 提前退出
guard let resu = try? say(words: "four") else { return }

“try!”类似于可选型中的强制解包,它不会对错误进行处理,如果“try!”代码块中出现异常,程序会在异常发生处崩溃。

let result = try! someFunction()  
print("The result is \(result)")
print(try? divide(2,1))//divide 整数除法运算符(/)
// Optional(2.0)
print(try? divide(2,0))
// nil
print(try! divide(2,1))
// 2.0
print(try! divide(2,0))
// 崩溃

9.存储属性、计算属性和类型属性的区别?
存储属性用来进行数据的存储, 需要分配内存空间。子类(无论是let var static修饰)不能直接重写存储属性。重写(Override)通常用于子类覆盖父类的方法、计算属性或观察者。
计算属性:用来定义计算的过程, 不需要分配空间.计算属性必须使用var关键字进行定义。
类型属性:用于定义某个类所有实例共享的数据。oc和swift都可以有类型属性。
oc需要添加属性关键字class定义类型属性(更像是通过调用属性来调用一个方法,比较像计算属性,oc中无法真正存储值。)
swift类型属性:使用static、 class来定义。static(不支持重写) class(支持子类重写)。

class HealthArticlesVC: BaseViewController {
// 存储属性
var age = 12
let name = "小花"
var ppName: String = { return "abc" }()
 private let _searchTextField: UITextField = UITextField() //搜索
}
//计算属性  应用场景:一般用于获取现有属性加工和计算后的结果。
var petInfo:String{
    get {
        return "getbackName"
    }
    set (newInfo) {
        print("\(newInfo)")
    }
}
//类型属性
struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int { return 1}
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

10.extension 中能增加存储属性吗?
extension :可以增加计算属性,不能增加存储属性。
extension是用来给存在的类型添加新行为的并不能改变类型或者接口本身。因为 extension 不能为类或结构体增加实际的存储空间。
(swift 没有分类的概念 只有extension扩展)

11.swift 中 closure闭包 与 OC 中 block 的区别?
相同点:都是一段可以执行的代码块
1、closure 是匿名函数,block 是一个结构体对象。
2、closure 可以通过逃逸闭包来在内部修改变量,block 通过 __block 修饰符。

12.作用域关键字的区别
. private 只可以在本类而且在同一个作用域中被访问.
. fileprivate 可以在本类中进行访问.
. internal (默认值) 只能访问自己module(模块)的任何internal实体,不能访问其他模块中的internal实体.
. public 类似于final,可以被其他module被访问,不可以被重载和继承.
. open 可以被其他module被访问、被重载、被继承.


private.png

public_open.png

二、冷门题
1.什么是柯里化?
柯里化:把接受多个参数的函数变成接受一个单一参数(最初函数的第一个)的函数,并且返回接受余下的参数和返回结果的新函数。

func multiplyTwoNumbers(_one:Int) ->  (Int) -> Int {
return{$0* one}
}

//调用
let a = multiplyTwoNumbers(2)
let b = a(3)
print(b)//输出6

2.swift 中map、flatMap、compactMap的区别
map: 正常遍历

flatMap:已弃用:4.1,重命名为:“compactMap(_:)”,消息:“对于闭包返回可选值的情况,请使用compactMap。
flatMap:可以自动解包。特殊表达式可以去nil。

//二维数组可以解包
 let array = [[1,2,3], [4,5,6], [7,8,9]]
 let arrayFlatMap = array.flatMap { $0 }
 print(arrayFlatMap) //[1, 2, 3, 4, 5, 6, 7, 8, 9]

//这种则无法解包
var customArr2 : [Any] = ["李一", "张2", "刘3", "赵四", "王五",["吴6","范7","吕宝贝8"]] 
 let array2 = customArr2.flatMap { $0}
   print(array2)//["李一", "张2", "刘3", "赵四", "王五", ["吴6", "范7", "吕宝贝8"]]

let fruits = ["apple", "banana", "orange", ""]
let counts = fruits.flatMap { fruit -> Int? in
    let length = fruit.characters.count
    guard length > 0 else {
        return nil
    }
    return length
}
// [5,6,6]

compactMap :函数会自动过滤掉映射结果为nil或者无效的值,只保留非空的映射结果。

 let possibleNumbers = ["1", "2", "three", "///4///", "5"]
        //普通map遍历
         let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
        print(mapped)
        //[Optional(1), Optional(2), nil, nil, Optional(5)]
      
          let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
        print(compactMapped) //[1, 2, 5]

3.什么是函数式编程?
面向对象编程:将要解决的问题抽象成一个类,通过给类定义属性和方法,让类帮助我们解决需要处理的问题(即命令式编程,给对象下一个个命令)。
函数式编程:数学意义上的函数,即映射关系(如:y = f(x),就是 y 和 x 的对应关系,可以理解为"像函数一样的编程")。它的主要思想是把运算过程尽量写成一系列嵌套的函数调用。
例:
数学表达式
(1 + 2) * 3 - 4
传统编程
var a = 1 + 2
var b = a * 3
var c = b - 4
函数式编程
var result = subtract(multiply(add(1,2), 3), 4)

函数式编程的好处:
代码简洁,开发迅速;
接近自然语言,易于理解;
更方便的代码管理;
易于"并发编程";
代码的热升级。

4.associatedtype 的作用
简单来说就是 protocol 使用的泛型
例如定义一个列表协议

protocol ListProtcol {
    associatedtype Element
    func push(_ element:Element)
    func pop(_ element:Element) -> Element?
}

实现协议的时候, 可以使用 typealias 指定为特定的类型, 也可以自动推断, 如

class IntList: ListProtcol {
    typealias Element = Int // 使用 typealias 指定为 Int
    var list = [Element]()
    func push(_ element: Element) {
        self.list.append(element)
    }
    func pop(_ element: Element) -> Element? {
        return self.list.popLast()
    }
}
class DoubleList: ListProtcol {
    var list = [Double]()
    func push(_ element: Double) {// 自动推断
        self.list.append(element)
    }
    func pop(_ element: Double) -> Double? {
        return self.list.popLast()
    }
}

使用泛型也可以

class AnyList<T>: ListProtcol {
    var list = [T]()
    func push(_ element: T) {
        self.list.append(element)
    }
    func pop(_ element: T) -> T? {
        return self.list.popLast()
    }
}
//可以使用 where 字句限定 Element 类型, 如:
extension ListProtcol where Element == Int {
    func isInt() ->Bool {
        return true
    }
}

5.Self 的使用场景

Self 通常在协议中使用, 用来表示实现者或者实现者的子类类型.
例如, 定义一个复制的协议

protocol CopyProtocol {
    func copy() -> Self
}
//如果是结构体去实现, 要将Self 换为具体的类型
struct SomeStruct: CopyProtocol {
    let value: Int
    func copySelf() -> SomeStruct {
        return SomeStruct(value: self.value)
    }
}

如果是类去实现, 则有点复杂, 需要有一个 required 初始化方法, 具体可以看这里

class SomeCopyableClass: CopyProtocol {
    func copySelf() -> Self {
        return type(of: self).init()
    }
    required init(){}
}

6.OC中的协议和swift中的协议 有什么区别?
Objective-C 的协议:声明方法,不能实现。
Swift 中的协议:它可以定义计算属性、方法、关联类型、静态方法和静态计算属性等。
Swift 的协议还支持泛型、默认实现、条件约束。

protocol MyProtocol {
    // 计算属性
    var name: String { get set }
    var age: Int { get }
    // 方法
    func greet() -> String
    // 关联类型
    associatedtype Item
    func processItem(item: Item)
    // 静态方法
    static func staticMethod() -> String
    // 静态属性
    static var staticProperty: String { get set }
}

extension MyProtocol {
    // 默认实现
    func defaultImplementation() -> String {
        return "Default implementation"
    }
}

7.属性默认
OC中 属性默认 关键字 nonatomic,strong ,readwrite
swift中 属性默认 关键字 nonatomic,strong

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • 总结关于swift的面试题------持续更新 来源于网上、书籍等 侵权即删-联系我[741136856@qq.c...
    橘子的皮阅读 1,601评论 0 12
  • Swift语言至今诞生有一年多的时间了,已经成为当前最流行语言之一。虽然它的语法简单好用,但实际上Swift是一门...
    TedX阅读 9,973评论 1 39
  • 问题一: 下面代码中变量 tutorial1.difficulty 和 tutorial2.difficulty ...
    CrystalZhu阅读 249评论 0 2
  • 初级 1、swift和OC的区别? 1)swift是静态语言,有类型推断,OC是动态语言。 2)swift是一门支...
    kakaLP阅读 18,337评论 1 33
  • https://www.jianshu.com/p/bdaa49f9d1a4[https://www.jiansh...
    Abner_XuanYuan阅读 15,822评论 2 40