面向對象
1、Hash表實現
- 概念:通過把關鍵碼值(key)映射到表中的一個位置來訪問記錄,Hash實現的關鍵是散列函數和沖突解決(鏈地址法和開放定址法)。
H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1))
其中: H ( key ) 為關鍵字 key 的直接哈希地址, m 為哈希表的長度, di 為每次再探測時的地址增量。
采用這種方法時,首先計算出元素的直接哈希地址 H ( key ) ,如果該存儲單元已被其他元素占用,則繼續查看地址為 H ( key ) + d 2 的存儲單元,如此重復直至找到某個存儲單元為空時,將關鍵字為 key 的數據元素存放到該單元。
增量 d 可以有不同的取法,并根據其取法有不同的稱呼:
( 1 ) d i = 1 , 2 , 3 , …… 線性探測再散列;
( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探測再散列;
( 3 ) d i = 偽隨機序列 偽隨機再散列;
2、什么是進程和線程?有什么區別?
- 進程:是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。
- 線程:操作系統能夠進行運算調度的最小單位。
- 區別:線程被包含在進程中,是進程中實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每個線程并行執行不同的任務。
3、內存幾大區域?各自職責?
- 棧區:由編譯器自動分配和釋放,一般存放函數的形參、局部變量
- 堆區:由程序員手動分配和釋放,若不釋放,則程序結束由操作系統回收
- 全局區:由編譯器自動分配和釋放,程序結束由系統釋放。全局變量和靜態變量
- 常量區:由編譯器分配和釋放,程序結束由系統釋放,存放常量字符
- 代碼區:存放函數的二進制代碼
4、架構、框架和設計模式的區別?
- 架構:一種頂層概括性的設計概念,像是藍圖,將不同的需求抽象為具體組件并使組件互相通信
- 框架:軟件框架是提取特定領域軟件的共性部分形成的體系結構,不同領域的軟件項目有不同的框架類型
- 設計模式:一套被反復使用、多數人知曉、經過分類編目的總結,代碼設計的一個成果,可以用在不同軟件框架中一種實際問題的解決方案。(在軟件開發中總結出的一種適合解決某種問題的方法
5、MVC、MVVM、MVP架構的不同?
-
MVC:
- 優點:簡單易上手、沒有多層調用
- 缺點:耦合性強;不利于單元測試,Controller擔負了太多的事件處理,處理用戶操作,管理View的聲明周期,API接口調用,處理錯誤,處理回調,監聽通知,處理視圖方向等,容易造成臃腫,維護不方便
-
MVP:
- 優點:利于單元測試、明確責任分配
- 缺點:要寫一些額外的代碼(如綁定),若業務邏輯復雜,也會造成Presenter的臃腫
-
MVVM:
- 優點:責任劃分明確;邏輯清晰;可測性高;雙向綁定,配合第三方開源庫使用方便
- 缺點:1、數據綁定使得Bug很難被定位;2、對于過大的項目,數據綁定需要花費更多的內存;依賴于開源庫實現雙向綁定,學習起來比較復雜
UI
1、UIView和CALayer的區別?
- UIView 和 CALayer 都是 UI 操作的對象。兩者都是 NSObject 的子類,發生在 UIView 上的操作本質上也發生在對應的 CALayer 上。
- UIView 是 CALayer 用于交互的抽象。UIView 是 UIResponder 的子類( UIResponder 是 NSObject 的子類),提供了很多 CALayer 所沒有的交互上的接口,主要負責處理用戶觸發的種種操作。
- CALayer 在圖像和動畫渲染上性能更好。這是因為 UIView 有冗余的交互接口,而且相比 CALayer 還有層級之分。CALayer 在無需處理交互時進行渲染可以節省大量時間。
- CALayer的動畫要通過邏輯樹、動畫樹和顯示樹來實現
2、loadView的作用?
- loadView用來自定義view,只要實現了這個方法,其他通過xib或storyboard創建的view都不會被加載
3、layoutIfNeeded、layoutSubviews和setNeedsLayout的區別?
- layoutIfNeeded:方法調用后,在主線程對當前視圖及其所有子視圖立即強制更新布局。
- layoutSubviews:方法只能重寫,我們不能主動調用,在屏幕旋轉、滑動或觸摸界面、子視圖修改時被系統自動調用,用來調整自定義視圖的布局。
- setNeedsLayout:方法與layoutIfNeeded相似,不同的是方法被調用后不會立即強制更新布局,而是在下一個布局周期進行更新。
4、iOS的響應鏈?什么情況會影響響應鏈?
- 事件UIResponder:繼承該類的對象才能響應。
- 事件處理:touchesBegain、touchesMoved、touchesEnded、touchesCancelled;
- 事件響應過程:
- 1、UIApplication(及delegate)接收事件傳遞給 keyWindow
- 2、keyWindow遍歷subViews的hitTest:withEvent:方法和pointInside方法找到點擊區域內的視圖并處理事件
- 3、UIView的子視圖也會遍歷其hitTest:withEvent:方法,以此類推,直到找到點擊區域內最上層的視圖,將視圖逐步返回給UIApplication
- 4、最后找到的視圖成為第一響應者,若無法響應,調用其nextResponder方法,一直找到響應鏈中能處理該事件的對象。
- 5、最后到Application依然沒有能處理該事件的對象的話,就廢棄該事件。
- ?? 以下幾種情況會忽略,hidden為YES,alpha小于等于0.01,userInteractionEnabled為NO
- UIControl的子類和UIGestureRecognizer優先級較高,會打斷響應鏈;
5、UIImageView添加圓角的方式
- cornerRadius(iOS9之后不會導致離屏渲染)
- CoreGraphic繪制
- UIBezierPath
6、iOS有哪些實現動畫的方式
- UIView Animation:系統提供的基于CALayer Animation的封裝,可以實現平移、縮放、旋轉等功能。
- CALayer Animation:底層CALayer配合CAAnimation的子類,可以實現更復雜的動畫
- UIViewPropertyAnimator:iOS10后引入的用于處理交互的動畫,可以實現UIView Animation的所有效果
7、使用drawRect有什么影響?
- 處理touch事件時會調用setNeedsDisplay進行強制重繪,帶來額外的CPU和內存開銷
OC
1、iOS的內存管理機制
- 通過引用計數機制實現的,當使用new、alloc、copy、retain、strong操作時,會使該對象的引用計數+1,需要對應release、autorelease操作,會使對象的引用計數-1。當引用計數為0時,對象會被系統銷毀
2、@property的相關修飾詞
- 原子性:
- nonatomic:非原子性,在使用該屬性時編譯器不會對其進行加鎖(同步鎖的開銷較大)
- atomic:原子性(默認),編譯器合成的方法會通過鎖定機制確保原子性(非線程安全)
- 讀寫權限:
- readwrite:可讀寫(默認),若不用@dynamic修飾,編譯器會自動為其生成setter和getter方法的聲明和實現,否則編譯器僅僅會生成方法的聲明,沒有實現
- readonly:只讀,若不用@dynamic修飾僅生成getter方法,否則編譯器僅會生成方法的聲明,沒有實現
3、內存管理語義
strong:修飾對象引用類型(表示持有當前實例),保留新值釋放舊值,若修飾IMMutable不可變類型建議用copy
copy:指修飾不可變集合類型、AttributeString、Block(ARC下strong和copy一樣,把棧中的Block拷貝到堆中)(設置方法不保留新值而是將其copy,不可變類型的可變類型子類)
weak:修飾對象弱引用類型(非持有關系),不保留新值,不釋放舊值(屬性所指對象被摧毀時,屬性值也會清空置nil)
assign:修飾基本數據類型(也可以修飾引用類型,但可能會產生野指針),執行簡單賦值操作
unsafe_unretained:修飾引用類型(也可以修飾基本類型),所指對象被摧毀時,屬性值不會被清空(unsafe)
4、方法名
- getter=指定方法名
- setter=指定方法名
5、dynamic和synthesis的區別
- dynamic:告訴編譯器不要幫我自動合成setter和getter方法,自己來實現,若沒有實現,當方法被調用時會報方法找不到。
- synthesis:指定實例變量的名字,子類重載父類屬性也需要synthesis(重新set和get、使用dynamic,在Protocol定義屬性、在category定義屬性,默認不會自動合成)。
6、array為何用copy修飾?mutableArray為何用strong修飾?
- array:若用strong修飾,在其被賦值可變的子類后,內容可能會在不知不覺中修改,用copy防止被修改。
- mutableArray若用copy修飾會返回一個NSArray類型,若調用可變類型的添加、刪除、修改方法時會因為找不到對應的方法而crash。
7、深拷貝和淺拷貝(注意NSString類型)
NSString:strong和copy修飾的屬性是同等的,指向同一個內存地址,mutableCopy才是內存拷貝;
NSMutableString:strong和copy不同,strong指向同一個內存地址,copy則會進行內存拷貝,mutableCopy也會進行內存拷貝;NSArray:對于字符類型和自定義對象結果是不同的,strong和copy都指向相同內存地址,mutableCopy也僅僅是指針拷貝;但是mutableCopy可以把Array變成MutableArray
PS:copy產生一個不可變類型,mutableCopy產生一個可變類型;對于字符類型mutableCopy會拷貝字符,copy對于NSArray是不拷貝的,對于NSMutableArray 是拷貝的;對于對象類型,僅僅是指針拷貝
8、Block的類型
-
_NSConcreteGlobalBlock
:全局Block也是默認的block類型,一種優化操作,私有和公開,保持私有可防止外部循環引用,存儲在數據區,當捕獲外部變量時會被copy到堆上 -
_NSConcreteStackBlock
:棧Block,存儲在棧區,待其作用域結束,由系統自動回收 -
_NSConcreteMallocBlock
:堆Block,計數器為0 的時候銷毀
OC進階
1、Foundation和CoreFoundation的轉換
__bridge
:負責傳遞指針,在OC和CF之間轉換,不轉移管理權,若把OC橋接給CF,則OC釋放后CF也無法使用。__bridge_retained
:將OC換成CF對象,并轉移對象所有權,同時剝奪ARC的管理權,之后需要使用CFRelease釋放。__bridge_transfer
:將CF轉換成OC,并轉移對象所有權,由ARC接管,所以不需要使用CFRelease釋放。
2、array和set的區別?查找速度和遍歷速度誰更快
- array:分配的是一片連續的存儲單元,是有序的,查找時需要遍歷整個數組查找,查找速度不如Hash。
- set:不是連續的存儲單元,且數據是無序的,通過Hash映射存儲的位置,直接對數據hash即可判斷對應的位置是否存在,查找速度較快。
- 遍歷速度:array的數據結構是一片連續的內存單元,讀取速度較快,set是不連續的非線性的,讀取速度較慢。
3、圖片顯示的過程
imageNamed:方法適用于經常使用,并且圖片小、數量少的場合,方便快速加載;
imageWithContentsOfFile:方法適用于圖片比較大,并且圖片數量非常多的場合,此時需要考慮程序的性能
1、當從磁盤中加載一張圖片時,這個時候圖片是沒有被解壓縮;
2、將生成的 UIImage 賦值給 UIImageView;
3、接著一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化
-
4、在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否字節對齊等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:
- 分配內存緩沖區用于管理文件 IO 和解壓縮操作;
- 將文件數據從磁盤讀到內存中;
- 將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個非常耗時的 CPU 操作;
- 最后 Core Animation 使用未壓縮的位圖數據渲染 UIImageView 的圖層。
4、dispatch_once如何只保證只執行一次?
- 多線程中,若有一個線程在訪問其初始化操作,另外一個線程進來后會延遲等待,有內存屏障來保證程序指令的順序執行
5、NSThread、NSRunLoop、NSAutoreleasePool三者之間的關系?
根據官方文檔中對 NSRunLoop 的描述,我們可以知道每一個線程,包括主線程,都會擁有一個專屬的 NSRunLoop 對象,并且會在有需要的時候(使用懶加載的方式,只有用到才會創建)自動創建。
根據官方文檔中對NSAutoreleasePool的描述,我們可知,在主線程的 NSRunLoop 對象(在系統級別的其他線程中應該也是如此,比如通過 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 獲取到的線程)的每個 event loop 開始前,系統會自動創建一個 autoreleasepool ,并在 event loop 結束時 drain。
-
需要手動添加autoreleasepool的情況:
- 如果你編寫的循環中創建了大量的臨時對象;
- 如果你創建了一個輔助線程
6、分類可擴展的區別?(可從內存布局、加載順序、分類方法和原類方法的執行順序來回答)
7、OC對象釋放的流程?
- release:引用計數器減一,直到為0時開始釋放
- dealloc:對象銷毀的入口
- dealloc中會調用_objc_rootDealloc函數,又會調用rootDealloc函數,又會調用object_dispose函數,又會調用objc_destructInstance函數,通過object_cxxDestruct移除成員變量,通過_object_remove_assocations移除當前對象的關聯對象,又調用clearDeallocating函數,調用clearDeallocating_slow函數,通過調用weak_clear_no_lock清理weak表,以及將weak指針置位nil
- sidetable_clearDeallocating:清理有指針isa的對象
- clearDeallocating_slow:清理非指針isa的對象,清理weak表、將weak指針置位nil,清空引用計數
8、CADisplayLink和NSTimer的區別?
- CADisplayLink:以和屏幕刷新率相同的頻率將內容畫到屏幕上的定時器,需手動添加runloop
- NSTimer:可自動添加到當前線程的runloop,默認defualt模式,也可以手動添加的loopMode;
9、用runtime實現方法交換有什么風險
- 風險1:若不在load中交換是非原子性的,在initial方法中不安全
- 風險2:重寫父類方法時,大部分都是需要調用super方法的,swizzling交換方法,若不調用,可能會出現一些問題,如:命名沖突、改變參數、調用順序、難以預測、難以調試。