中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ios面試試題的示例分析

發布時間:2021-08-04 10:22:13 來源:億速云 閱讀:105 作者:小新 欄目:移動開發

這篇文章給大家分享的是有關ios面試試題的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

使用了第三方庫, 有看他們是怎么實現的嗎?

例:SD、YY、AFN、MJ等!

<1>.SD為例:

1.入口 setImageWithURL:placeholderImage:options:
會先把 placeholderImage 顯示,然后 SDWebImageManager 根據 URL 開始處理圖片。
2.進入 SDWebImageManagerdownloadWithURL:delegate:options:userInfo:,
交給 SDImageCache 從緩存查找圖片是否已經下載 queryDiskCacheForKey:delegate:userInfo:.
3.先從內存圖片緩存查找是否有圖片,
如果內存中已經有圖片緩存,SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4.SDWebImageManagerDelegate 回調 webImageManager:didFinishWithImage:
到 UIImageView+WebCache 等前端展示圖片。
5.如果內存緩存中沒有,生成 NSInvocationOperation
添加到隊列開始從硬盤查找圖片是否已經緩存。
6.根據 URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。
這一步是在 NSOperation 進行的操作,所以回主線程進行結果回調 notifyDelegate:。
7.如果上一操作從硬盤讀取到了圖片,將圖片添加到內存緩存中
(如果空閑內存過小,會先清空內存緩存)。
SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo:。
進而回調展示圖片。
8.如果從硬盤緩存目錄讀取不到圖片,
說明所有緩存都不存在該圖片,需要下載圖片,
回調 imageCache:didNotFindImageForKey:userInfo:。
9.共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片。
10.圖片下載由 NSURLConnection 來做,
實現相關 delegate 來判斷圖片下載中、下載完成和下載失敗。
11.connection:didReceiveData: 中
利用 ImageIO 做了按圖片下載進度加載效果。
12.connectionDidFinishLoading: 數據下載完成后交給 SDWebImageDecoder 做圖片解碼處理。
13.圖片解碼處理在一個 NSOperationQueue 完成,
不會拖慢主線程 UI。如果有需要對下載的圖片進行二次處理,
最好也在這里完成,效率會好很多。
14.在主線程 notifyDelegateOnMainThreadWithInfo:
宣告解碼完成,
imageDecoder:didFinishDecodingImage:userInfo
回調給 SDWebImageDownloader。
15.imageDownloader:didFinishWithImage:
回調給 SDWebImageManager 告知圖片下載完成。
16.通知所有的 downloadDelegates 下載完成,
回調給需要的地方展示圖片。
17.將圖片保存到 SDImageCache 中,
內存緩存和硬盤緩存同時保存。
寫文件到硬盤也在以單獨 NSInvocationOperation 完成,
避免拖慢主線程。
18.SDImageCache 在初始化的時候會注冊一些消息通知,
在內存警告或退到后臺的時候清理內存圖片緩存
應用結束的時候清理過期圖片。
19.SDWI 也提供了 UIButton+WebCache 和
MKAnnotationView+WebCache,方便使用。
20.SDWebImagePrefetcher 可以預先下載圖片,
方便后續使用。

2.強連通分量了解嘛?

概念:

有向圖強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly

connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量(strongly

connected components)。

定義:

有向圖強連通分量:

在有向圖G中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly connected)。

如果有向圖G的每兩個頂點都強連通,則稱G是一個強連通圖。

非強連通圖有向圖的極大強連通子圖,成為強連通分量(strongly connected components)。

下圖中,子圖{1,2,3,4}為一個強連通分量,因為頂點1,2,3,4兩兩可達,{5},{6}也分別是兩個強連通分量。

ios面試試題的示例分析

直接根據定義,用雙向遍歷取交際的方法求強連通分量,時間復雜度為O(N^2+M)。更好的方法是Kosaraju算法或者Tarjan算法

兩者的時間復雜度都是O(N+M)。本文介紹的是Tarjan算法。

算法原理:(Tarjan)

need-to-insert-img

Tarjan算法是基于對圖深度優先搜索的算法,每個強連通分量為搜索樹中的一顆子樹。

搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時可以盤對棧頂到棧中的節點是否為一個強連通分量。

定義DFN(u)為節點u搜索的次序編號(時間戳)。Low(u)為u或者u的子樹能夠追溯到的最早的棧中的節點的次序號。

由定義可以得出:

Low(u)= Min { DFN(u), Low(v)} ((u,v)為樹枝邊,u為v的父節點DFN(v),(u,v)為指向棧中節點的后向邊(非橫叉邊))

當DFN(u)=Low(u)時,以u為根的搜索子樹上所有節點是一個強連通分量。

3.遇到tableView卡頓嘛?會造成卡頓的原因大致有哪些?

可能造成tableView卡頓的原因有:

1.最常用的就是cell的重用, 注冊重用標識符

如果不重用cell時,每當一個cell顯示到屏幕上時,就會重新創建一個新的cell

如果有很多數據的時候,就會堆積很多cell。

如果重用cell,為cell創建一個ID,每當需要顯示cell 的時候,都會先去緩沖池中尋找可循環利用的cell,如果沒有再重新創建cell

2.避免cell的重新布局

cell的布局填充等操作 比較耗時,一般創建時就布局好

如可以將cell單獨放到一個自定義類,初始化時就布局好

3.提前計算并緩存cell的屬性及內容

當我們創建cell的數據源方法時,編譯器并不是先創建cell 再定cell的高度

而是先根據內容一次確定每一個cell的高度,高度確定后,再創建要顯示的cell,滾動時,每當cell進入憑虛都會計算高度,提前估算高度告訴編譯器,編譯器知道高度后,緊接著就會創建cell,這時再調用高度的具體計算方法,這樣可以方式浪費時間去計算顯示以外的cell

4.減少cell中控件的數量

盡量使cell得布局大致相同,不同風格的cell可以使用不用的重用標識符,初始化時添加控件,

不適用的可以先隱藏

5.不要使用ClearColor,無背景色,透明度也不要設置為0

渲染耗時比較長

6.使用局部更新

如果只是更新某組的話,使用reloadSection進行局部更

7.加載網絡數據,下載圖片,使用異步加載,并緩存

8.少使用addView 給cell動態添加view

9.按需加載cell,cell滾動很快時,只加載范圍內的cell

10.不要實現無用的代理方法,tableView只遵守兩個協議

11.緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時存在,這兩者同時存在才會出現“竄動”的bug。所以我的建議是:只要是固定行高就寫預估行高來減少行高調用次數提升性能。如果是動態行高就不要寫預估方法了,用一個行高的緩存字典來減少代碼的調用次數即可

12.不要做多余的繪制工作。在實現drawRect:的時候,它的rect參數就是需要繪制的區域,這個區域之外的不需要進行繪制。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text,然后再調用繪制方法。

13.預渲染圖像。當新的圖像出現時,仍然會有短暫的停頓現象。解決的辦法就是在bitmap context里先將其畫一遍,導出成UIImage對象,然后再繪制到屏幕;

14.使用正確的數據結構來存儲數據。

4.M、V、C相互通訊規則你知道的有哪些?

MVC 是一種設計思想,一種框架模式,是一種把應用中所有類組織起來的策略,它把你的程序分為三塊,分別是:

M(Model):實際上考慮的是“什么”問題,你的程序本質上是什么,獨立于 UI 工作。是程序中用于處理應用程序邏輯的部分,通常負責存取數據。

C(Controller):控制你 Model 如何呈現在屏幕上,當它需要數據的時候就告訴 Model,你幫我獲取某某數據;當它需要 UI 展示和更新的時候就告訴 View,你幫我生成一個 UI 顯示某某數據,是 Model 和 View 溝通的橋梁。

V(View):Controller 的手下,是 Controller 要使用的類,用于構建視圖,通常是根據 Model 來創建視圖的。

要了解 MVC 如何工作,首先需要了解這三個模塊間如何通信。

MVC通信規則

ios面試試題的示例分析

Controller to Model

可以直接單向通信。Controller 需要將 Model 呈現給用戶,因此需要知道模型的一切,還需要有同 Model 完全通信的能力,并且能任意使用 Model 的公共 API。

Controller to View

可以直接單向通信。Controller 通過 View 來布局用戶界面。

Model to View

永遠不要直接通信。Model 是獨立于 UI 的,并不需要和 View 直接通信,View 通過 Controller 獲取 Model 數據

View to Controller

View 不能對 Controller 知道的太多,因此要通過間接的方式通信。

Target

action。首先 Controller 會給自己留一個 target,再把配套的 action 交給 View 作為聯系方式。那么 View

接收到某些變化時,View 就會發送 action 給 target 從而達到通知的目的。這里 View 只需要發送

action,并不需要知道 Controller 如何去執行方法。

代理。有時候 View 沒有足夠的邏輯去判斷用戶操作是否符合規范,他會把判斷這些問題的權力委托給其他對象,他只需獲得答案就行了,并不會管是誰給的答案。

DataSoure。View 沒有擁有他們所顯示數據的權力,View 只能向 Controller 請求數據進行顯示,Controller 則獲取 Model 的數據整理排版后提供給 View。

Model 訪問 Controller

同樣的 Model 是獨立于 UI 存在的,因此無法直接與 Controller 通信,但是當 Model 本身信息發生了改變的時候,會通過下面的方式進行間接通信。

Notification & KVO一種類似電臺的方法,Model 信息改變時會廣播消息給感興趣的人 ,只要 Controller 接收到了這個廣播的時候就會主動聯系 Model,獲取新的數據并提供給 View。

從上面的簡單介紹中我們來簡單概括一下 MVC 模式的優點。

1.低耦合性

2.有利于開發分工

3.有利于組件重用

4.可維護性

5.NStimer準嗎?談談你的看法?如果不準該怎樣實現一個精確的NSTimer?

1.不準

2.不準的原因如下:

1、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main負責所有主線程事件,例如UI界面的操作,復雜的運算,這樣在同一個runloop中timer就會產生阻塞。

2、模式的改變。主線程的 RunLoop 里有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。

當你創建一個 Timer 并加到 DefaultMode 時,Timer 會得到重復回調,但此時滑動一個ScrollView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,并且也不會影響到滑動操作。所以就會影響到NSTimer不準的情況。

PS:DefaultMode 是 App 平時所處的狀態,rackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。

方法一:

1、在主線程中進行NSTimer操作,但是將NSTimer實例加到main runloop的特定mode(模式)中。避免被復雜運算操作或者UI界面刷新所干擾。

self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

2、在子線程中進行NSTimer的操作,再在主線程中修改UI界面顯示操作結果;

- (void)timerMethod2 {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
- (void)newThread
{
@autoreleasepool
{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}

總結:

一開始的時候系統就為我們將主線程的main runloop隱式的啟動了。

在創建線程的時候,可以主動獲取當前線程的runloop。每個子線程對應一個runloop

方法二:

使用示例

使用mach內核級的函數可以使用mach_absolute_time()獲取到CPU的tickcount的計數值,可以通過”mach_timebase_info”函數獲取到納秒級的精確度 。然后使用mach_wait_until(uint64_t deadline)函數,直到指定的時間之后,就可以執行指定任務了。

關于數據結構mach_timebase_info的定義如下:

struct mach_timebase_info {uint32_t numer;uint32_t denom;};
#include
#include
static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
static mach_timebase_info_data_t timebase_info;
static uint64_t nanos_to_abs(uint64_t nanos) {
return nanos * timebase_info.denom / timebase_info.numer;
}
void example_mach_wait_until(int seconds)
{
mach_timebase_info(&timebase_info);
uint64_t time_to_wait = nanos_to_abs(seconds * NANOS_PER_SEC);
uint64_t now = mach_absolute_time();
mach_wait_until(now + time_to_wait);
}

iOS面試題:騰訊二面以及參考思路:

編譯過程做了哪些事情?

1.C++,Objective C都是編譯語言。編譯語言在執行的時候,必須先通過編譯器生成機器碼,機器碼可以直接在CPU上執行,所以執行效率較高。

iOS開發目前的常用語言是:Objective和Swift。二者都是編譯語言,換句話說都是需要編譯才能執行的。二者的編譯都是依賴于Clang + LLVM. OC和Swift因為原理上大同小異,知道一個即可!

iOS編譯

不管是OC還是Swift,都是采用Clang作為編譯器前端,LLVM(Low level vritual machine)作為編譯器后端。所以簡單的編譯過程如圖

ios面試試題的示例分析

編譯器前端

編譯器前端的任務是進行:語法分析,語義分析,生成中間代碼(intermediate representation )。在這個過程中,會進行類型檢查,如果發現錯誤或者警告會標注出來在哪一行。

ios面試試題的示例分析

編譯器后端

編譯器后端會進行機器無關的代碼優化,生成機器語言,并且進行機器相關的代碼優化。iOS的編譯過程,后端的處理如下

LVVM優化器會進行BitCode的生成,鏈接期優化等等

ios面試試題的示例分析

LLVM機器碼生成器會針對不同的架構,比如arm64等生成不同的機器碼。

ios面試試題的示例分析

執行一次XCode build的流程

當你在XCode中,選擇build的時候(快捷鍵command+B),會執行如下過程

編譯信息寫入輔助文件,創建編譯后的文件架構(name.app)

處理文件打包信息,例如在debug環境下

ios面試試題的示例分析

執行CocoaPod編譯前腳本

例如對于使用CocoaPod的工程會執行CheckPods Manifest.lock

編譯各個.m文件,使用CompileC和clang命令。

編譯各個.m文件,使用CompileC和clang命令。

1.CompileC ClassName.o ClassName.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
2.export.US-ASCII
3.export PATH="..."
4.clang-x objective-c -arch x86_64 -fmessage-length=0 -fobjc-arc...
-Wno-missing-field-initializers ... -DDEBUG=1 ... -isysroot
iPhoneSimulator10.1.sdk -fasm-blocks ... -I 上文提到的文件 -F 所需要的Framework-iquote 所需要的Framework ... -c ClassName.c -o ClassName.o

通過這個編譯的命令,我們可以看到

ios面試試題的示例分析

2.字典大致實現原理;

一:字典原理

NSDictionary(字典)是使用hash表來實現key和value之間的映射和存儲的

方法:- (void)setObject:(id)anObject forKey:(id)aKey;

Objective-C中的字典NSDictionary底層其實是一個哈希表

二:哈希原理

散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。

給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數后若能得到包含該關鍵字的記錄在表中的地址,則稱表M為哈希(Hash)表,函數f(key)為哈希(Hash) 函數。

哈希概念:哈希表的本質是一個數組,數組中每一個元素稱為一個箱子(bin),箱子中存放的是鍵值對。

三:哈希存儲過程

1.根據 key 計算出它的哈希值 h。

2.假設箱子的個數為 n,那么這個鍵值對應該放在第 (h % n) 個箱子中。

3.如果該箱子中已經有了鍵值對,就使用開放尋址法或者拉鏈法解決沖突。

在使用拉鏈法解決哈希沖突時,每個箱子其實是一個鏈表,屬于同一個箱子的所有鍵值對都會排列在鏈表中。

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

負載因子 = 總鍵值對數 / 箱子個數

負載因子越大,意味著哈希表越滿,越容易導致沖突,性能也就越低。因此,一般來說,當負載因子大于某個常數(可能是 1,或者 0.75 等)時,哈希表將自動擴容。

哈希表在自動擴容時,一般會創建兩倍于原來個數的箱子,因此即使 key 的哈希值不變,對箱子個數取余的結果也會發生改變,因此所有鍵值對的存放位置都有可能發生改變,這個過程也稱為重哈希(rehash)。

哈希表的擴容并不總是能夠有效解決負載因子過大的問題。假設所有 key 的哈希值都一樣,那么即使擴容以后他們的位置也不會變化。雖然負載因子會降低,但實際存儲在每個箱子中的鏈表長度并不發生改變,因此也就不能提高哈希表的查詢性能。

基于以上總結,細心的朋友可能會發現哈希表的兩個問題:

1.如果哈希表中本來箱子就比較多,擴容時需要重新哈希并移動數據,性能影響較大。

2.如果哈希函數設計不合理,哈希表在極端情況下會變成線性表,性能極低。

3.block和函數指針的理解;

相似點:

函數指針和Block都可以實現回調的操作,聲明上也很相似,實現上都可以看成是一個代碼片段。

函數指針類型和Block類型都可以作為變量和函數參數的類型。(typedef定義別名之后,這個別名就是一個類型)

不同點:

函數指針只能指向預先定義好的函數代碼塊(可以是其他文件里面定義,通過函數參數動態傳入的),函數地址是在編譯鏈接時就已經確定好的。

Block本質是Objective-C對象,是NSObject的子類,可以接收消息。

函數里面只能訪問全局變量,而Block代碼塊不光能訪問全局變量,還擁有當前棧內存和堆內存變量的可讀性(當然通過__block訪問指示符修飾的局部變量還可以在block代碼塊里面進行修改)。

從內存的角度看,函數指針只不過是指向代碼區的一段可執行代碼,而block實際上是程序運行過程中在棧內存動態創建的對象,可以向其發送copy消息將block對象拷貝到堆內存,以延長其生命周期。

感謝各位的閱讀!關于“ios面試試題的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

ios
AI

彰化市| 赤峰市| 防城港市| 改则县| 思茅市| 庆元县| 铁力市| 中宁县| 铜鼓县| 巴东县| 甘德县| 汾阳市| 南城县| 祁东县| 荆门市| 贺州市| 宾阳县| 清远市| 湛江市| 金溪县| 浙江省| 成安县| 体育| 昌宁县| 凭祥市| 安徽省| 昔阳县| 浪卡子县| 信宜市| 巍山| 星子县| 乌拉特后旗| 炉霍县| 海伦市| 万宁市| 伊金霍洛旗| 玛多县| 扶余县| 洛浦县| 靖宇县| 肇东市|