2020年iOS進階面試題總結(一)

準備找工做的你,能夠看看,複習複習!!web

一、說一下OC的反射機制

在動態運行下咱們能夠構建任何一個類,而後咱們經過這個類知道這個類的全部的屬性和方法,而且若是咱們建立一個對象,咱們也能夠經過對象找到這個類的任意一個方法,這就是反射機制。
好比NSClassFormString,NSStringFormSelector,NSSelectorFormString
參考連接面試

二、block的本質是什麼?有幾種block?分別是怎樣產生的?

參考連接
block與函數相似,只不過是直接定義在另外一個函數裏,和定義它的那個函數共享同一個範圍內的東西,
block的強大之處是:在聲明它的範圍裏,全部變量均可覺得其捕獲,這也就是說,那個範圍內的所有變量,在block依然能夠用,默認狀況下,爲block捕獲的變量,是不能夠在block裏修改的,不過聲明的時候能夠加上__block修飾符,這樣就能夠再block內修改了。算法

block自己和其餘對象同樣,有引用計數,當最後一個指向block的引用移走以後,block就回收了,回收時也釋放block所捕獲的變量。swift

Block的實現是經過結構體的方式實現,在編譯的過程當中,將Block生成對應的結構體,在結構體中記錄Block的匿名函數,以及使用到的自動變量,在最後的使用中,經過Block結構體實例訪問成員中存放的匿名函數地址調用匿名函數,並將自身做爲參數傳遞。
block其實就是C語言的擴充功能,實現了對C的閉包實現,一個帶有局部變量的匿名函數,
block的本質也是一個OC對象,它內部也有一個isa指針,block是封裝了函數調用以及函數調用環境的OC對象,爲了保證block內部可以正常訪問外部的變量,block有一個變量捕獲機制。static 修飾的變量爲指針傳遞,一樣會被block捕獲。局部變量由於跨函數訪問因此須要捕獲,全局變量在哪裏均可以訪問 ,因此不用捕獲。
當block內部訪問了對象類型的auto變量時,若是block在棧上,block內部不會對變量產生強應用,不論block的結構體內部的變量時__strong修飾仍是__weak修飾,都不會對變量產生強引用
默認狀況下block不能修改外部的局部變量數組

1.static修飾

static修飾的age變量傳遞到block內部的是指針,在__main_block_func_0函數內部就能夠拿到age變量的內存地址,所以就能夠在block內部修改age的值。瀏覽器

有三種類型緩存

__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
複製代碼

__block內存管理安全

當block內存在棧上時,並不會對__block變量產生內存管理,當block被copy到堆上時會調用block內部的copy函數,copy函數內部會滴啊用_Block_object_assign函數,_Block_object_assign函數會對__block變量造成強引用(至關於retain)。
當block被copy到堆上時,block內部引用的__block變量也會被複制到堆上,而且持有變量,若是block複製到堆上的同時,__block變量已經存在堆上了,則不會複製。bash

當block從堆中移除的話,就會調用dispose函數,也就是__main_block_dispose_0函數,__main_block_dispose_0函數內部會調用_Block_object_dispose函數,會自動釋放引用的__block變量。服務器

解決循環引用問題

使用__weak和__unsafe_unretained修飾符合一解決循環引用的問題,__weak會使block內部將指針變爲弱指針。
__weak 和 __unsafe_unretained的區別。
__weak不會產生強引用,指向的對象銷燬時,會自動將指針置爲nil
__unsafe_unretained不會產生強引用,不安全,指向的對象銷燬時,指針存儲的地址值不變
__strong 和 __weak
在block內部從新使用__strong修飾self變量是爲了在block內部有一個強指針指向weakSelf避免在block調用的時候weakSelf已經被銷燬。

2.__block修飾的變量爲何能在block裏面能改變其值?

__block用於解決block內部不能修改auto變量值的問題,__block不能修飾靜態變量和全局變量
_block 所起到的做用就是隻要觀察到該變量被 block 所持有,就將「外部變量」在棧中的內存地址放到了堆中。進而在block內部也能夠修改外部變量的值。

做爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個個人點擊加入羣聊iOS交流羣:789143298 ,無論你是小白仍是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術, 你們一塊兒交流學習成長!

3.NSDictionary使用原理

NSDictionary是使用hash表來實現key和vaLue之間的映射和存儲的
hash原理
hash概念:哈希表的本質是一個數組,數組中沒一個元素稱爲一個箱子,箱子中存放的是鍵值對。
哈希表的存儲過程:
1.根據key計算出它的哈希值h
2.假設箱子的個數爲n,那麼這個鍵值對應應該在第(h % n)個箱子中。
3.若是該箱子中已經有了鍵值對,就使用開放尋址法或者拉鍊法解決衝突。
在使用拉鍊法解決哈希衝突時,每一個箱子實際上是一個鏈表,屬於同一個箱子的全部鍵值對都會排列在鏈表中。

哈希表還有一個重要的屬性:負載因子(load factor),它用來衡量哈希表的空/滿程度,必定程度上也能夠體現查詢的效率,計算公式爲:

4.NSCache優於NSDictionary的幾點?

NSCache是一個容器,相似於NSDictionary,經過key-value形式存儲和查詢值,用於臨時存儲對象。
NSCache賽過NSDictionary之處在於,當系統資源將要耗盡時,它能夠自動刪減緩存。若是採用普通的字典,那麼就要本身編寫掛鉤,在系統發出「低內存」通知時手工刪減緩存。
NSCache並不會「拷貝」鍵,而是會「保留」它。此行爲用NSDictionary也能夠實現,然而須要編寫至關複雜的代碼。NSCache對象不拷貝鍵的緣由在於:不少時候,鍵都是不支持拷貝操做的對象來充當的。所以,NSCache不會自動拷貝鍵,因此說,在鍵不支持拷貝操做的狀況下,該類用起來比字典更方便。另外,NSCache是線程安全的,而NSDictionary則絕對不具有此優點。

5.屬性

屬性是OC的一項特性,用於封裝對象的數據,OC對象一般會把其所須要的數據保存爲各類實例對象,實例對象通常經過存取方法來訪問,其中獲取方法用於讀取變量值,而設置方法用於寫入變量值,開發者能夠令編譯器自動編寫與屬性相關的存取方法。

6.理解objc_msgSend的做用

objc_msgSend叫作消息傳遞,消息有名稱或選擇子,能夠接受參數
Runtime時執行的流程是這樣的:
一個對象的方法像這樣[obj foo],編譯器轉成消息發送objc_msgSend(obj, foo),Runtime時執行的流程是這樣的:
首先,經過obj的isa指針找到它的 class ;
在 class 的 method list 找 foo ;
若是 class 中沒到 foo,繼續往它的 superclass 中找 ;
一旦找到 foo 這個函數,就去執行它的實現IMP 。

7.什麼是指針常量和常量指針

指針常量:(指針變量前加const) int *const p;指針自己是一個常量。在聲明的時候初始化,裏面的值(存放的地址)不能更改。

常量指針:(在類型前加const) const int *p;指針自己是一個變量,初始化是最好給一個常量的地址,它裏面值(存放的地址)能夠改變。

8.若你去設計一個通知中心,你會怎樣設計?

NSNotification:這是一個包裝通知信息的類,相似一個model,存儲了notificationName,object,userInfo等信息.
NSNotificationCenter:顧名思義~通知中心,就是用來管理通知的接收和發送的類.
1.定義一個類TestNotification,用來存儲notificationName,object,userInfo等信息.這裏咱們仿照系統的API進行設計.這裏另外加了兩個參數observer和selector.
2.設計通知中心類TestNotificationCenter,一樣仿照系統的API進行設計.

9. KVO、KVC的實現原理

KVO是基於runtime機制實現的
當某個類的屬性對象第一次被觀察時,系統就會在運行期動態地建立該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內實現真正的通知機制
若是原類爲Person,那麼生成的派生類名爲NSKVONotifying_Person
每一個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那麼系統會偷偷將isa指針指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法
鍵值觀察通知依賴於NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個被觀察屬性發生改變以前, willChangeValueForKey:必定會被調用,這就 會記錄舊的值。而當改變發生後,didChangeValueForKey:會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。
補充:KVO的這套實現機制中蘋果還偷偷重寫了class方法,讓咱們誤認爲仍是使用的當前類,從而達到隱藏生成的派生類
KVC底層實現原理(以下)
KVC運用了一個isa-swizzling技術. isa-swizzling就是類型混合指針機制, 將2個對象的isa指針互相調換, 就是俗稱的黑魔法.
KVC主要經過isa-swizzling, 來實現其內部查找定位的. 默認的實現方法�由NSOject提供isa指針, 如其名稱所指,(就是is a kind of的意思), 指向分發表對象的類. 該分發表實際上包含了指向實現類中的方法的指針, 和其它數據。

首先搜索setKey:方法.(key指成員變量名, 首字母大寫)
二、上面的setter方法沒找到, 若是類方法accessInstanceVariablesDirectly返回YES. 那麼按 _key, _isKey,key, iskey的順序搜索成員名.(NSKeyValueCodingCatogery中實現的類方法, 默認實現爲返回YES)
三、若是沒有找到成員變量, 調用setValue:forUnderfinedKey:

10.HTTP和HTTPs的請求過程?

www.jianshu.com/p/55cb014f6…

說說你理解weak屬性?

Runtime維護了一個weak表,用於存儲指向某個對象的全部weak指針。weak表實際上是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數組。

一、初始化時:runtime會調用objc_initWeak函數,初始化一個新的weak指針指向對象的地址。

二、添加引用時:objc_initWeak函數會調用 objc_storeWeak() 函數, objc_storeWeak() 的做用是更新指針指向,建立對應的弱引用表。

三、釋放時,調用clearDeallocating函數。clearDeallocating函數首先根據對象地址獲取全部weak指針地址的數組,而後遍歷這個數組把其中的數據設爲nil,最後把這個entry從weak表中刪除,最後清理對象的記錄。

追問的問題一:

1.實現weak後,爲何對象釋放後會自動爲nil?

runtime 對註冊的類, 會進行佈局,對於 weak 對象會放入一個 hash 表中。 用 weak 指向的對象內存地址做爲 key,當此對象的引用計數爲 0 的時候會 dealloc,假如 weak 指向的對象內存地址是 a ,那麼就會以 a 爲鍵, 在這個 weak 表中搜索,找到全部以 a 爲鍵的 weak 對象,從而設置爲 nil 。

追問的問題二:

2.當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢?
一、調用objc_release
二、由於對象的引用計數爲0,因此執行dealloc
三、在dealloc中,調用了_objc_rootDealloc函數
四、在_objc_rootDealloc中,調用了object_dispose函數
五、調用objc_destructInstance
六、最後調用objc_clear_deallocating,詳細過程以下:
a. 從weak表中獲取廢棄對象的地址爲鍵值的記錄
b. 將包含在記錄中的全部附有 weak修飾符變量的地址,賦值爲 nil
c. 將weak表中該記錄刪除
d. 從引用計數表中刪除廢棄對象的地址爲鍵值的記錄
10.iOS本地數據存儲安全
11.BAD_ACCESS的錯誤嗎?你是怎樣調試的?
BAD_ACCESS:無論何時當你遇到BAD_ACCESS這個錯誤,那就意味着你向一個已經釋放的對象發送消息。
BAD_ACCESS的本質:

在C和OC中,你一直在處理指針,指針無非是存儲另外一個變量的內存地址的變量。當向一個對象發送消息時,指向該對象的指針將會被引用,這意味着,你獲取了指針所指的內存地址,並訪問該存儲區域的值。
當該存儲器區域再也不映射到你的應用時,或者換句話說,該內存區域在你認爲使用的時候沒有使用,該內存區域是沒法訪問的,這時內核會拋出一個異常(EXC),代表你的應用程序不能訪問該存儲器區域(BAD_ACCESS).
當你碰到BAD_ACCESS,這意味着你試圖發送消息到的內存塊,但內存塊沒法執行該消息。可是,在某些狀況下,BAD_ACCESS是由被損壞的指針引發的,每當你的應用程序嘗試引用損壞的指針,一個異常就會被內核拋出。
調試請看

十二、不借用第三個變量,如何交換兩個變量的值?要求手動寫出交換過程。

int a = 10,b = 20;
    a = a+b;
    b = a - b;
    a = a - b;
//第二種方法,位異或運算
    a = a^b;
    
    b = a^b;
    
    a = a^b;
    
    
    //第三種方法,使用指針
    
    int *pa = &a;
    
    int *pb = &b;
    
    *pa = b;
    
    *pb = a;
    
    NSLog(@"after,a = %d",a);
    
    NSLog(@"after,b = %d",b);
複製代碼

13.用遞歸算法求1到n的和

int sum(int n)
{
    if (n==1)
        return 1;
    else
        return sum(n-1)+n;
}
複製代碼

14.category爲何不能添加屬性?

Category不能添加成員變量,能夠添加屬性,可是屬性要手動實現setter和getter方法。

Category的原理

簡單地說就是經過runtime動態的吧Category中的方法等添加到類中,
從category的定義也能夠看出category的可爲(能夠添加實例方法,類方法,甚至能夠實現協議,添加屬性)和不可爲(沒法添加實例變量)。
通過編譯的類在程序啓動後就被runtime加載,沒有機會調用addIvar。程序在運行時動態構建的類須要在調用objc_registerClassPair
以後才能夠被使用,一樣沒有機會再添加成員變量。

category爲何只能添加方法

由於方法和屬性並不「屬於」類實例,而成員變量「屬於」類實例。咱們所說的「類實例」概念,指的是一塊內存區域,包含了isa指針和全部的成員變量。因此假如容許動態修改類成員變量佈局,已經建立出的類實例就不符合類定義了,變成了無效對象。但方法定義是在objc_class中管理的,無論如何增刪類方法,都不影響類實例的內存佈局,已經建立出的類實例仍然可正常使用。

Category注意事項:

一、category的方法沒有「徹底替換掉」原來類已經有的方法,也就是說若是category和原來類都有methodA,那麼category附加完成以後,類的方法列表裏會有兩個methodA
二、category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的後面,這也就是咱們日常所說的category的方法會「覆蓋」掉原來類的同名方法,這是由於運行時在查找方法的時候是順着方法列表的順序查找的,它只要一找到對應名字的方法,就會罷休_,卻不知後面可能還有同樣名字的方法。
詳細請點擊

15.runloop和線程的關係

runloop 正如其名,loop是一種循環,和run放在一塊兒就是表示一直在運行着循環,實際上Runloop和線程是緊密相連的,能夠這樣說run loop是爲了線程而生,沒有線程,它就沒有存在必要。每一個線程,包括程序的主線程( main thread )都有與之相應的 run loop 對象。
主線程是默認開啓的,其餘線程須要手動開啓

深刻理解

1六、說一下autoreleasePool的實現原理。

autoreleasePool自動釋放池是OC的一種內存自動回收機制,它能夠延時加入autoreleasePool中的變量release的時機,在正常狀況下,建立的變量會在超出其做用於的時候release,可是若是將變量加入autoreleasePool,那麼release將延遲執行。
AutoreleasePool建立是在一個RunLoop事件開始以前(push),AutoreleasePool釋放是在一個RunLoop事件即將結束以前(pop)。
AutoreleasePool裏的Autorelease對象的加入是在RunLoop事件中,AutoreleasePool裏的Autorelease對象的釋放是在AutoreleasePool釋放時。
單個自動釋放池的執行過程就是objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)

詳細點擊

1七、說一下簡單工廠模式,工廠模式以及抽象工廠模式?

1八、如何設計一個網絡請求庫?

1九、delegate

代理(delegate)的主旨是:定義一套接口,某個對象若想接受另外一個對象的委託,則需聽從此接口,以便成爲其委託對象,而這另外一個對象的委託,則需聽從此接口,以便成爲其委託對象,而這另外一個對象則能夠給其委託對象回傳一些信息,也能夠發生相關事件時通知委託對象。
注意delegate需定義成weak。由於二者之間必須爲"非擁有關係",一般狀況下,扮演delegate的那個對象也要持有本對象。

20、說一下多線程,你日常是怎麼用的?

詳細地址

2一、說一下UITableViewCell的卡頓你是怎麼優化的?

1.UITableViewCell重用機制?
UITableView只會建立一屏幕(或者一屏幕多一點)的cell,其餘都是取出來重用的。每當cell滑出屏幕的時候,就會放到一個集合中,當要顯示某一位置的cell時,會先去集合中取,有的話,就直接拿出來顯示,沒有在建立。

2.tableView滑動爲何會卡頓?
cell賦值內容時,會根據內容設置佈局,也就能夠知道cell的高度,如有1000行,就會調用1000次 cellForRow方法,而咱們對cell的處理操做,都是在這個方法中賦值,佈局等等,開銷很大。

3.優化方法?
3.1優化:heightForRow方法處理cell高度。
思路:賦值和計算佈局分離。cellForRow負責賦值,heightRorRow負責計算高度。

3.2自定義cell繪製:
各個信息都是根據以前算好的佈局進行繪製的。須要異步繪製。重寫draeRect方法就不須要異步繪製了,由於drawRect原本就是異步繪製的。圖文混排的繪製,coreText繪製。

3.3按需加載(UIScrollView方面):
若是目標行與當前行相差超過指定行數,只在目標滾動範圍的先後制定n行加載。滾動很快時,只加載目標範圍內得cell,這樣按需加載,極大地提升了流暢性。

4.總結
1.提早計算並緩存好高度,由於heightForRow最頻繁的調用。
2.異步繪製,遇到複雜界面,性能瓶頸時,多是突破口。
3.滑動時按需加載,這個在大量圖片展現,網絡加載時,很管用。(SDWebImage已經實現異步加載)。
4.重用cells。
5.若是cell內顯示得內容來自web,使用異步加載,緩存結果請求。
6.少用或不用透明圖層,使用不透明視圖。
7.儘可能使全部的view opaque,包括cell自己。
8.減小subViews
9.少用addView給cell動態添加view,能夠初始化的時候就添加,而後經過hide控制是否顯示。

2二、看過哪些三方庫?說一下實現原理以及好在哪裏?

精確試試

2三、說一下HTTP協議以及常用的code碼的含義。

全套code

2四、設計一套緩存策略。

不清楚 有知道的能夠回答下

2五、HTTP協議 HTTPS

HTTP協議:即超文本傳輸協議,是一種詳細規定了瀏覽器和萬維網服務器之間互相通訊的規則,經過因特網傳送萬維網文檔的數據傳送協議
HTTP協議做用:HTTP協議是用於從www服務器傳輸超文本到本地瀏覽器的傳送協議,它可使瀏覽器更加高效,使網絡傳輸減小,它不只保證計算機正確快速的傳輸超文本文檔,還肯定傳輸文檔的哪一部分,以及哪部份內容首先顯顯示等。
URL:咱們在瀏覽器的地址欄裏輸入的網站地址叫作URL (Uniform Resource Locator,統一資源定位符)。就像每家每戶都有一個門牌地址同樣,每一個網頁也都有一個Internet地址。當你在瀏覽器的地址框中輸入一個URL或是單擊一個超級連接時,URL就肯定了要瀏覽的地址。瀏覽器經過超文本傳輸協議(HTTP),將Web服務器上站點的網頁代碼提取出來,並翻譯成漂亮的網頁。
HTTPS::是以安全爲目標的HTTP通道,簡單講HTTP的安全版,即HTTP下加入SSL層,HTTPS的安全基礎是SSL,所以加密的詳細內容就須要SSL。

2六、設計一個檢測主線和卡頓的方案。

2七、說一下runtime,工做是如何使用的?看過runtime源碼嗎?

2八、說幾個你在工做中使用到的線程安全的例子。

2九、用過哪些鎖?哪些鎖的性能比較高?

30、說一下HTTP和HTTPs的請求過程?

在HTTP/1.1協議中,定義了8種發送HTTP請求的方法
GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
各個方法的解釋以下(全部方法全爲大寫):
GET: 請求獲取Request-URI所標識的資源
POST: 在Request-URI所標識的資源後附加新的數據
HEAD: 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT: 請求服務器存儲一個資源,並用Request-URI做爲其標識
DELETE: 請求服務器刪除Request-URI所標識的資源
TRACE: 請求服務器回送收到的請求信息,主要用於測試或診斷
CONNECT: 保留未來使用
OPTIONS: 請求查詢服務器的性能,或者查詢與資源相關的選項和需求

根據HTTP協議的設計初衷,不一樣的方法對資源有不一樣的操做方式
PUT :增
DELETE :刪
POST:改
GET:查
最經常使用的是GET和POST(實際上GET和POST都能辦到增刪改查)
詳細內容

3一、說一下TCP和UDP

3二、說一下靜態庫和動態庫之間的區別

3三、load和initialize方法分別在何時調用的?

3四、NSNotificationCenter是在哪一個線程發送的通知?

3五、用過swift嗎?若是沒有,日常有學習嗎?

3六、說一下你對架構的理解?

3七、爲何必定要在主線程裏面更新UI?

像UIKit這樣大的框架上確保線程安全是一個重大的任務,會帶來巨大的成本。UIKit不是線程安全的,假如在兩個線程中設置了同一張背景圖片,頗有可能就會因爲背景圖片被釋放兩次,使得程序崩潰。或者某一個線程中遍歷找尋某個subView,然而在另外一個線程中刪除了該subView,那麼就會形成錯亂。apple有對大部分的繪圖方法和諸如UIColor等類改寫成線程安全可用,可仍是建議將UI操做保證在主線程中。

分享也是個學習的過程,以上內容多爲網絡收集而來,若有問題還請指正。

相關文章
相關標籤/搜索