Swift面试题集合

为啥数组越界抛出异常而字典不会?
答案:因为下标访问的实现不一样,
struct Array {
subscript(index: Int) -> Element
}

struct Dictionary {
subscript(key: Key) -> Value?
}
字典当下标访问不到时候,会返回一个nil
而数组下标越界时,只能throw一个error


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


定义静态方法时关键字 static 和 class 有什么区别?
答案:
static 定义的方法不可以被子类继承, class 则可以

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



如何理解copy-on-write?
答案:
当你有一个占用内存很大的一个值类型,并且不得不将它赋值给另一个变量或者当做函数的参数传递的时候,拷贝它的值是一个非常消耗内存的操作,因为你不得不拷贝它所有的东西放置在另一块内存中。
为了优化这个问题,Swift 对于一些特定的值类型(集合类型:Array、Dictionary、Set)做了一些优化,在对于 Array 进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int 等值类型,在赋值的时候就会发生拷贝。下面来看代码验证一下:
先看一下基本类型(Int、String等)
var num1 = 10
var num2 = num1
print(address(of: &num1)) //0x7ffee0c29828
print(address(of: &num2)) //0x7ffee0c29820

var str1 = "abc"
var str2 = str1
print(address(of: &str1)) //0x7ffee0c29810
print(address(of: &str2)) //0x7ffee0c29800

//打印内存地址
func address(of object: UnsafeRawPointer) -> String {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}
复制代码
从上面的打印我们可以看出基本类型在进行赋值的时候就发生了拷贝操作
在看一下集合类型
var arr1 = [1,2,3,4,5]
var arr2 = arr1
print(address(of: &arr1)) //0x6000023b06b0
print(address(of: &arr2)) //0x6000023b06b0

arr2[2] = 4
print(address(of: &arr1)) //0x6000023b06b0
print(address(of: &arr2)) //0x6000023b11f0


总结

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

如何将Swift 中的协议(protocol)中的部分方法设计为可选(optional)?
答案:答:
1.在协议和方法前面添加 @objc,然后在方法前面添加 optional关键字,改方式实际上是将协议转为了OC的方式
@objc protocol someProtocol {
@objc optional func test()
}

2.使用扩展(extension),来规定可选方法,在 swift 中,协议扩展可以定义部分方法的默认实现

protocol someProtocol {
func test()
}

extension someProtocol{
func test() {
print("test")
}
}
第一种方式是OC的方式只适合引用类型,不能适用于用于值类型,结构体,枚举。



Swift中的访问权限?
答案:open public internal fileprivate private
open: 可以被任何类使用,包括override和继承。
public: 可以被任何类访问。但其他module中不可以被override和继承,而在module内可以被override和继承。
interanal:默认的访问级别,可以在模块内的任何地方被访问
filePrivate:在不同的.swift中不能被访问,在同一个.swift中可以访问
private: 只允许当前对象的当前.swift 文件访问
参考链接


Class 和 Struct 的区别?

类和结构体的不同点:
  • 类可以继承而结构体不行。
  • 结构体(和枚举)是值类型, 而类是引用类型,引用计数允许对实例进行多个引用,结构体的持有者只能有一个。
类和结构体的共同点:
  • 定义属性
  • 定义提供功能的方法
  • 可以用下标点来访问值
  • 可以进行扩展
  • 可以遵循协议

Explain [weak self] and [unowned self]?

  1. weak 和unowned都是用来解决引用循环的,都不会对对象进行强引用
  2. weak用的是weak 引用,unowned用的是unowend的引用
  3. weak引用会在对象没有强引用的时候,把weak指针全部销毁,而unowned 引用在对象没有强引用的时候不会销毁,这时如果通过unowned引用去访问这个对象会造成运行时的错误。
  4. weak修饰的对象是可选类型的,而owned修饰的对象期望是一直有值的是非可选性的。
  5. 当我们知道两个对象的生命周期并不相关,那么我们必须使用 weak。相反,unowned修饰的对象的生命周期和其它对象的生命周期短或者同样的生命周期时,则应该使用 unowned。。
    参考链接

Extension in swift?
答案:类,结构体,枚举,协议都可以进行扩展

  1. 扩展实例方法和类方法
  2. 添加计算型属性
  3. 添加初始化的方法
  4. 在扩展中遵循协议

Non-Escaping and Escaping Closures?
例如一个闭包作为一个方法的参数,在这个函数在这个方法执行结束的时候这个闭包没有被调用或者是延时调用,这时候系统会提示加上@escaping关键字标记为逃逸闭包


swift中可选类型的本质是什么?
一个为Optional的枚举 一个case是 none ,另一个case 是 some(wrapped)枚举关联的值

public enum Optional : 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`.
    case some(Wrapped)
}

map vs flatmap
都是接受一个闭包对数组每个元素进行遍历,进行相同的操作返回一个新的数组。Flatmap中没有为nil的元素,flatmap可以对数组降维。


filter VS reduce
filter接受一个过滤条件的闭包,返回一个符合过滤条件的数组
reduce给定义一个初始值,根据闭包处理这些元素。合并处理的结果


Define typealais ?
用 typealais name = existing type
一个type的alais声明之后,可以用声明的名字来替代现有的类型。可以是已经存在的类类型或者是复合类型,类型别名不创建新的类型,他只是一个涉及现有类型的名字。


Type safety & Type Inference? //类型安全和类型推断
类型安全: swift是个类型安全的语言,类型安全的语言鼓励我们明确值的类型,不能像一个字符串传递一个整数。
类型推断: 当你没有指定值的类型的时候,swift会根据你赋的值推断出合适的类型。


Explain Forced Unwrapping ?
当我将一个变量定义为可选类型的时候,我们要从中获取到值,我们需要在一个变量后面加上!


Optional binding ? //可选绑定
答案:当使用一个可选类型的变量,我们想知道改变量是否包含值,如果包含用该值作为临时的常量或者变量 if let realValue = optionalValue { }


Optional Chaining ?
答案:是一个查询调用属性,方法,下标的的过程,当一个可选项包含值,则属性,方法,下标调用成功,如果为nil就返回nil。多个查询链拼在一起的时候,任何一个链为nil ,则返回nil


closures VS blocks?
闭包:当一个函数函数定义在外层作用域中,比如一个函数作为另一个函数的返回值,它能够捕获局部变量,这些局部变量会存在函数之中不会随着局部作用域的结束而销毁,这样的函数叫做闭包。
Swift闭包表达式和函数是相同的类型,你可以函数传递给闭包;
Swift闭包和block都具有捕获语义,变量是可变的而不是复制的,OC中的__block 在swift中是默认行为。 __block 将对象从栈上copy到堆上

image.png


Explain Identical operator ?//相等的判断
答案:!==和 ===
判断是否引用相同的内存地址
是判断引用相等在OC中相当于==
==和!=
判断值相等用于值类型
OC中是 isEqual:


What is Downcasting ?//类型向下转化
答案: as 用类型向上转化,或者是桥接类型
as? 安全转化,如果失败返回nil
as! 强制转化,如果失败就会崩溃


What are benefits of Guard ?
答案:避免陷入一层层的if判断中


Explain generics in Swift ?
答案:用泛型所写的代码不必指明其准确的数据类型


What is the Swift main advantage ?
答案:1.可选类型可以避免一些闪退
2.类型安全的语言
3.统一的内存管理,全部用的是ARC


How is an “inout” parameter different from a regular parameter?
答案: inout通过引用传递,常规参数通过值传递


Define Properties in swift ?
答案:储存属性将常量或者变量作为实例的一部分储存,计算性属性计算一个值,不作为实例的一部分存储,计算性属性可以用在结构体,类,枚举,而储存性属性只能用在结构体和类


Explain Stored Property?
答案:存储属性var let ; 注意的是,一个结构体被声明为常量,即使里面的储存属性为变量也不能修改,因为结构体是值类型的。类是引用类型就是可以修改。


Explain Computed Property ?
答案:类,结构体,枚举都可以定义计算性属性,这些属性都不储存值,
他提供一个get 和一个可选的set 方法;
当一个计算型属性只有get方法,没有set,一个只读的计算性属性,总是返回值。
只能用var 每次计算都可能得到不同的值


Explain Lazy stored Property ?
答案:延时储存性属性仅在第一次调用改属性时计算。
Lazy变量初始化不是自动的,所以不是线程安全的


Explain Property observers ?
答案:willset { 用将要设置的 newValue } didset { 之前的oldValue }


Explain Default Property Values ?
答案: 如果一个属性有默认值,就不用在初始化的时候从新设置它的值


Expain Default Initializer ?
答案:swift为类和结构体提供默认的初始化方法,为属性提供默认的值,不止提供一个初始化的方法。默认初始化方法创建一个实例为它的所有属性设置默认值。


Explain Initializer Delegation for Value Types ?
答案:初始化委托,一个初始化的方法里面可以调用其它的初始化方法,来初始化一个实例。这个过程我们叫做初始化委托,可以可以避免重复的初始化代码。指定初始化方法,可以避免使用默认初始化的时候的绕过一些属性的设置。


Explain Initialiser designation for class types ? //初始化委托
答案:Rule1: 一个指定初始化必须要调用父类的指定初始化方法
Rule2: 一个类的便利初始化方法,必须要调用该类的其他初始化方法
Rule3:便利初始化方法最终必须调用该类的指定初始化方法
Explain Two Phase Initialization ?//解释两部分初始化
答案:第一个部分是现在自己的储存性属性赋值,
第二部分 每个类有修改储存属性值的机会
安全检查1:一个指定初始化方法必须确保自己的属性有值,才能去调用父类的初始化
安全检查2:在修改继承父类的属性之前一定要调用父类的初始化方法,如果不这样子类修改的值,可能被父类的初始化再次修改
安全检查3:一个便利初始化方法在给属性赋值的时候必须先调用委托本类的初始化方法,如果不这样便利初始化的属性可能被指定初始化所修改
安全检查4:在初始化方法的第一步未完成之前不能调用任何势力方法,或者self先关的的值


Explain Automatic Initializer Inheritance?
答案:swift默认情况下是不继承父类的初始化方法的,如果遇到特殊条件可以继承
条件1:如果你的子类没有定义指初始化方法,子类继承父类的所有指定初始化方法
条件2 如果子类实现了父类的所有指定初始化方法,子类就继承了父类的所有便利初始化方法

image.png

Explain Failable initialisers ?
答案:可能会初始化失败的初始化方法,用init?来表示:
例如,一个用枚举的rawValue初始化来初始化一个枚举,当这个rawValue不在枚举的范围内就会返回一个nil


Explain Initializing with closures ?
答案:相当于调用一个有返回值的func


Explain Enum and associated value ?
答案:你可以用swift的枚举来关联任何给定类型的关联值,关联值的类型可以不同类型的。


Explain Implicitly Assigned Raw Values ?
答案:当初处理整数或者字符串的枚举时,不必为每种情况分配原始值,swift会自动为你分配值。


Explain Difference between UIWindow and UIView ?
答案:UIwindow没有可视的内容,只是给视图提供一个可视的容器


What is UIButton hierarchy ?
答案: UIButton -> UIControl-> UIView -> UIResponder -> NSObject

image.png


内存中的5大区分别是什么?
• 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈。
• 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
• 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
• 文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放。
• 程序代码区:存放函数体的二进制代码。


指针和引用的区别?
如果电脑的存储器相当于一本书,那么指针就像粘在每一页的页码,想要哪页的内容直接找页码直接就会找到。


你可能感兴趣的:(Swift面试题集合)