最近應該有很多小伙伴去跳槽面試的吧丹锹,相信各位有的已經(jīng)順利收到offer了,而有些則是碰壁了,那么我在這里給大家準備了相關面試資料精钮,還有相關算法資料。想了解的可找我拿這篇文章給大家總結了在iOS面試的時候可能會遇到的技術面試題剃斧,以及這些面試題答案轨香,這些答案只是給大家一些參考,大家可以再結合自己理解進行回答幼东,有需要的朋友們下面來一起看看吧臂容。
1. 給一個數(shù)組,要求寫一個函數(shù)根蟹,交換數(shù)組中的兩個元素
- 二X程序員:
好簡單啊脓杉,直接寫出以下結果
func swap(_ nums: inout [Int], _ p: Int, _ q: Int) {
let temp = nums[p]
nums[p] = nums[q]
nums[q] = temp
}
- 普通程序員:
首先跟面試官溝通,是什么類型的數(shù)組娜亿?面試官會說丽已,任意。普通程序員微微一笑买决,寫出以下代碼
func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {
let temp = nums[p]
nums[p] = nums[q]
nums[q] = temp
}
- 文藝程序員:
與面試官溝通沛婴,是什么類型的數(shù)組?有什么其他要求和限制督赤?面試官會說嘁灯,這是一個Swift面試題。文藝程序員心領神會躲舌,于是寫出以下答案:
func swap<T>(_ nums: inout [T], _ p: Int, _ q: Int) {
(nums[p], nums[q]) = (nums[q], nums[p])
}
同時對以上代碼寫上相應測試丑婿,檢測各種邊界情況,再確認無誤后,才會說羹奉,這道題目我完成了秒旋。
這道題目看似簡單,實際上考察了程序員的審題诀拭、交流迁筛、以及測試的意識。技術上考察了Swift的泛型和Tuple的性質(zhì)耕挨。
2. 下面代碼有什么問題
public class Node {
public var value: Int
public var prev: Node?
public var post: Node?
public init(_ value: Int) {
self.value = value
}
}
答案:應該在 var prev 或者 var post 前面加上 weak细卧。
原因:表面上看,以上代碼毫無問題筒占。但是我這樣一寫贪庙,問題就來了:
let head = Node(0)
let tail = Node(1)
head.post = tail
tail.prev = head
此時,head 和 tail 互相指向翰苫,形成循環(huán)引用(retain cycle)止邮。
3. 實現(xiàn)一個函數(shù),輸入是任一整數(shù)奏窑,輸出要返回輸入的整數(shù) + 2
這道題很多人上來就這樣寫:
func addTwo(_ num: Int) -> Int {
return num + 2
}
接下來面試官會說农尖,那假如我要實現(xiàn) + 4 呢?程序員想了一想良哲,又定義了另一個方法:
func addFour(_ num: Int) -> Int {
return num + 4
}
這時面試官會問,假如我要實現(xiàn)返回 + 6, + 8 的操作呢助隧?能不能只定義一次方法呢筑凫?正確的寫法是利用 Swift 的柯西特性:
func add(_ num: Int) -> (Int) -> Int {
return { val in
return num + val
}
}
let addTwo = add(2), addFour = add(4), addSix = add(6), addEight = add(8)
4. 精簡以下代碼
func divide(dividend: Double?, by divisor: Double?) -> Double? {
if dividend == nil {
return nil
}
if divisor == nil {
return nil
}
if divisor == 0 {
return nil
}
return dividend! / divisor!
}
這題考察的是guard let語句以及optional chaining,最佳答案是:
func divide(dividend: Double?, by divisor: Double?) -> Double? {
guard let dividend = dividend, let divisor = divisor, divisor != 0 else {
return nil
}
return dividend / divisor
}
5. 以下函數(shù)會打印出什么并村?
var car = "Benz"
let closure = { [car] in
print("I drive \(car)")
}
car = "Tesla"
closure()
因為 clousre已經(jīng)申明將car 復制進去了([car])巍实,此時clousre 里的 car是個局部變量,不再與外面的car有關哩牍,所以會打印出”I drive Benz”棚潦。
此時面試官微微一笑,將題目略作修改如下:
var car = "Benz"
let closure = {
print("I drive \(car)")
}
car = "Tesla"
closure()
此時 closure沒有申明復制拷貝car膝昆,所以clousre 用的還是全局的 car 變量丸边,此時將會打印出 “I drive Tesla”
6. 以下代碼會打印出什么?
protocol Pizzeria {
func makePizza(_ ingredients: [String])
func makeMargherita()
}
extension Pizzeria {
func makeMargherita() {
return makePizza(["tomato", "mozzarella"])
}
}
struct Lombardis: Pizzeria {
func makePizza(_ ingredients: [String]) {
print(ingredients)
}
func makeMargherita() {
return makePizza(["tomato", "basil", "mozzarella"])
}
}
let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()
lombardis1.makeMargherita()
lombardis2.makeMargherita()
答案:打印出如下兩行
[“tomato”, “basil”, “mozzarella”]
[“tomato”, “basil”, “mozzarella”]
在Lombardis
的代碼中荚孵,重寫了makeMargherita
的代碼妹窖,所以永遠調(diào)用的是Lombardis
中的 makeMargherita
。
再進一步收叶,我們把 protocol Pizzeria
中的 func makeMargherita()
刪掉骄呼,代碼變?yōu)?/p>
protocol Pizzeria {
func makePizza(_ ingredients: [String])
}
extension Pizzeria {
func makeMargherita() {
return makePizza(["tomato", "mozzarella"])
}
}
struct Lombardis: Pizzeria {
func makePizza(_ ingredients: [String]) {
print(ingredients)
}
func makeMargherita() {
return makePizza(["tomato", "basil", "mozzarella"])
}
}
let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()
lombardis1.makeMargherita()
lombardis2.makeMargherita()
這時候打印出如下結果:
[“tomato”, “mozzarella”]
[“tomato”, “basil”, “mozzarella”]
因為lombardis1
是Pizzeria
,而 makeMargherita()
有默認實現(xiàn),這時候我們調(diào)用默認實現(xiàn)蜓萄。
7. Swift 中定義常量和 Objective-C 中定義常量有什么區(qū)別隅茎?
一般人會覺得沒有差別,因為寫出來好像也確實沒差別嫉沽。
OC是這樣定義常量的:
const int number = 0;
Swift 是這樣定義常量的:
let number = 0
首先第一個區(qū)別辟犀,OC中用 const
來表示常量,而 Swift 中用let
來判斷是不是常量耻蛇。
上面的區(qū)別更進一步說踪蹬,OC中 const
表明的常量類型和數(shù)值是在compilation time
時確定的;而 Swift 中 let
只是表明常量(只能賦值一次)臣咖,其類型和值既可以是靜態(tài)的跃捣,也可以是一個動態(tài)的計算方法,它們在runtime
時確定的夺蛇。
8. Swift 中 struct 和 class 什么區(qū)別疚漆?舉個應用中的實例
struct
是值類型,class
是引用類型刁赦。
看過WWDC的人都知道娶聘,struct 是蘋果推薦的,原因在于它在小數(shù)據(jù)模型傳遞和拷貝時比 class 要更安全甚脉,在多線程和網(wǎng)絡請求時尤其好用丸升。我們來看一個簡單的例子:
class A {
var val = 1
}
var a = A()
var b = a
b.val = 2
此時 a 的 val 也被改成了 2,因為 a 和 b 都是引用類型牺氨,本質(zhì)上它們指向同一內(nèi)存狡耻。解決這個問題的方法就是使用 struct
:
struct A {
var val = 1
}
var a = A()
var b = a
b.val = 2
此時 A 是struct,值類型猴凹,b 和 a 是不同的東西夷狰,改變 b 對于 a 沒有影響。
9. Swift 到底是面向?qū)ο筮€是函數(shù)式的編程語言郊霎?
這里有一個iOS交流圈:[891 488 181] 有興趣的都可以來了解沼头,分享BAT,阿里面試題、面試經(jīng)驗书劝,討論技術进倍,裙里資料直接下載就行, 大家一起交流學習成長庄撮!
Swift 既是面向?qū)ο蟮谋嘲疲质呛瘮?shù)式的編程語言。
說 Swift 是 Object-oriented
洞斯,是因為 Swift 支持類的封裝毡庆、繼承坑赡、和多態(tài),從這點上來看與 Java 這類純面向?qū)ο蟮恼Z言幾乎毫無差別么抗。
說 Swift 是函數(shù)式編程語言毅否,是因為 Swift 支持 map, reduce, filter, flatmap
這類去除中間狀態(tài)、數(shù)學函數(shù)式的方法蝇刀,更加強調(diào)運算結果而不是中間過程螟加。
什么是多態(tài)?什么是分類?什么是協(xié)議?
答:
多態(tài)在面向?qū)ο笳Z言中指同一個接口有多種不同的實現(xiàn)方式,在OC中,多態(tài)則是不同對象對同一消息的不同響應方式;子類通過重寫父類的方法來改變同一方法的實現(xiàn).體現(xiàn)多態(tài)性
通俗來講: 多態(tài)就父類類型的指針指向子類的對象,在函數(shù)(方法)調(diào)用的時候可以調(diào)用到正確版本的函數(shù)(方法)。
多態(tài)就是某一類事物的多種形態(tài).繼承是多態(tài)的前提;
答:
分類: 在不修改原有類代碼的情況下,可以給類添加方法
Categroy 給類擴展方法,或者關聯(lián)屬性, Categroy底層結構也是一個結構體:內(nèi)部存儲這結構體的名字,那個類的分類,以及對象和類方法列表,協(xié)議,屬性信息
通過Runtime加載某個類的所有Category數(shù)據(jù)
把所有Category的方法吞琐、屬性捆探、協(xié)議數(shù)據(jù),合并到一個大數(shù)組中后面參與編譯的Category數(shù)據(jù)站粟,會在數(shù)組的前面
將合并后的分類數(shù)據(jù)(方法黍图、屬性、協(xié)議)奴烙,插入到類原來數(shù)據(jù)的前面
答:
協(xié)議:協(xié)議是一套標準助被,這個標準中聲明了很多方法,但是不關心具體這些方法是怎么實現(xiàn)的切诀,具體實現(xiàn)是由遵循這個協(xié)議的類去完成的揩环。
在OC中,一個類可以實現(xiàn)多個協(xié)議幅虑,通過協(xié)議可以彌補單繼承的缺陷但是協(xié)議跟繼承不一樣丰滑,協(xié)議只是一個方法列表,方法的實現(xiàn)得靠遵循這個協(xié)議的類去實現(xiàn)倒庵。
文章到這里就結束了吨枉,如果你有什么意見和建議歡迎給我留言。關于面試問題的資料有興趣可以加我了解或者咨詢問題