2018ios面試題

weixin_34378969發表於2018-02-21

1.為什麼說Objective-C是一門動態的語言?

object-c類的型別和資料變數的型別都是在執行是確定的,而不是在編譯時確定。例如:多型特性,我們可以使用父類物件來指向子類物件,並且可以用來呼叫子類的方法。執行時(runtime)特性,我們可以動態的新增方法,或者替換方法。

2.講一下MVC和MVVM?

MVC:簡單來說就是,邏輯、試圖、資料進行分層,實現解耦。

MVVM:是Model-View-ViewMode模式的簡稱。由檢視(View)、檢視模型(ViewModel)、模型(Model)三部分組成.比MVC更加釋放控制器臃腫,將一部分邏輯(耗時,公共方法,網路請求等)和資料的處理等操作從控制器裡面搬運到ViewModel中

MVVM的特點:

低耦合。View可以獨立於Model變化和修改,一個ViewModel可以繫結到不同的View上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。

可重用性。可以把一些檢視的邏輯放在ViewModel裡面,讓很多View重用這段檢視邏輯。

獨立開發。開發人員可以專注與業務邏輯和資料的開發(ViewModel)。設計人員可以專注於介面(View)的設計。

可測試性。可以針對ViewModel來對介面(View)進行測試

3.為什麼代理要用weak?代理的delegate和dataSource有什麼區別?block和代理的區別?

代理是使用weak來修飾的。1.使用weak是為了避免迴圈引用。2.當使用weak修飾的屬性,當物件釋放的時候,系統會對屬性賦值nil,object-c有個特性就是對nil物件傳送訊息也就是呼叫方法,不會cash。

delegate:表示代理,代理可以讓A物件通知B物件,我(A)發生的變化,前提B遵循了A的代理,並且實現了A的代理方法。

dataSource:表示資料來源,如果A物件宣告瞭資料來源,當我們建立A物件的時候,我們就該實現資料來源,來告訴A,他所需要的一些資料。例如:tableView資料來源方法,需要告訴它,我要實現幾組cell,每組cell多少行cell,實現的cell什麼樣式,什麼內容

同樣delegate和dataSource,都是可以使用require和optional來修飾的。

代理和Block的區別

相同點:代理和Block大多是我們都可以用來做倒序傳值的。我們都得注意避免迴圈引用。

不同點:代理使用weak修飾,代理必須先宣告方法。當我們呼叫代理的時候要判斷是否已經實現。

block:使用的是copy來修飾,block儲存的是一段程式碼,其實也就是一個函式。當我們呼叫block的時候要判斷是否已經實現。

4.屬性的實質是什麼?包括哪幾個部分?屬性預設的關鍵字都有哪些?@dynamic關鍵字和@synthesize關鍵字是用來做什麼的?

屬性是描述類的特徵,也就是具備什麼特性。三個部分,帶下劃線的成員變數,get、setter方法。

預設關鍵字:readwrite,assign, atomic; -- 是針對基本型別(NSInteger, BOOL, NSUInteger, int, 等)

但是針對引用型別, 預設:strong, readwrite, atomic (例如:NSString, NSArray, NSDictory等)

@dynamic :修飾的屬性,其getter和setter方法編譯器是不會自動幫你生成。必須自己是實現的。

@synthesize:修飾的屬性,其getter和setter方法編譯器是會自動幫你生成。不必自己實現,可以指定與屬性相對應的成員變數。

6.NSString為什麼要用copy關鍵字,如果用strong會有什麼問題?

可變型別(NSMutableArray,NSMutableString等)是不可邊型別(NSString,NSArray等)的子類,因為多型的原因,我們可以使用賦值指向子類物件,也就是我們可以使用不可邊型別去接受可變型別。

1.當我們使用strong修飾A不可邊型別的時候,並且使用B可變型別給A賦值,再去修改可變型別B值的時候,A所指向的值也會發生改變。引文strong只是讓建立的物件引用計數器+1,並返回當前物件的內容地址,當我們修改B指向的內容的時候,A指向的內容也同樣發生了改變,因為他們指向的記憶體地址是相同的,是一份內容。

2.當我們使用copy修飾A不可邊型別的時候,並且使用B可變型別給A賦值,再去修改可變型別B值的時候,A所指向的值不會發生改變。因為當時用copy的修飾的時候,會拷貝一份內容出來,並且返回指標給A,當我們修改B指向的內容的時候,A指向的內容是沒有發生改變的。因為A指向的記憶體地址和B指向的記憶體地址是不相同的,是兩份內容

3.copy修飾不可邊型別(NSString,NSArray等)的時候,且使用不可邊型別進行賦值,表示淺拷貝,只拷貝一份指標,和strong修飾一樣,當修飾的是可變型別(NSMutableArray,NSMutableString等)的時候,且使用可邊型別進行賦值,表示深拷貝,直接拷貝新一份內容,到記憶體中。表示兩份內容。

7.如何令自己所寫的物件具有拷貝功能?

必須遵循nscopying協議,如果想實現可變和不可邊拷貝時,必須同時遵循nscoping和nsmutablecoping協議。並且實現

- (id)copyWithZone:(NSZone *)zone;

9.為什麼IBOutlet修飾的UIView也適用weak關鍵字?

在xib或者Sb拖控制元件時,其實控制元件就載入到了父控制元件的subviews陣列裡面,進行了強引用,即使使用了weak,也不造成物件的釋放。

10.nonatomic和atomic的區別?atomic是絕對的執行緒安全麼?為什麼?如果不是,那應該如何實現?

nonatomic:表示非原子,不安全,但是效率高。

atomic:表示原子行,安全,但是效率定。

atomic:不能絕對保證執行緒的安全,當多執行緒同時訪問的時候,會造成執行緒不安全。可以使用執行緒鎖來保證執行緒的安全。

13.程式和執行緒的區別?同步非同步的區別?並行和併發的區別?

程式:是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程式是系統進行資源分配和排程的一個獨立單位.

執行緒:是程式的一個實體,是CPU排程和分派的基本單位,它是比程式更小的能獨立執行的基本單位.執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程式的其他的執行緒共享程式所擁有的全部資源.

同步:阻塞當前執行緒操作,不能開闢執行緒。

非同步:不阻礙執行緒繼續操作,可以開闢執行緒來執行任務。

併發:當有多個執行緒在操作時,如果系統只有一個CPU,則它根本不可能真正同時進行一個以上的執行緒,它只能把CPU執行時間劃分成若干個時間段,再將時間 段分配給各個執行緒執行,在一個時間段的執行緒程式碼執行時,其它執行緒處於掛起狀。.這種方式我們稱之為併發(Concurrent)。

並行:當系統有一個以上CPU時,則執行緒的操作有可能非併發。當一個CPU執行一個執行緒時,另一個CPU可以執行另一個執行緒,兩個執行緒互不搶佔CPU資源,可以同時進行,這種方式我們稱之為並行(Parallel)。

區別:併發和並行是即相似又有區別的兩個概念,並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔內發生。在多道程式環境下,併發性是指在一段時間內巨集觀上有多個程式在同時執行,但在單處理機系統中,每一時刻卻僅能有一道程式執行,故微觀上這些程式只能是分時地交替執行。倘若在計算機系統中有多個處理機,則這些可以併發執行的程式便可被分配到多個處理機上,實現並行執行,即利用每個處理機來處理一個可併發執行的程式,這樣,多個程式便可以同時執行。

14.執行緒間通訊?

當使用dispath-async函式開闢執行緒執行任務的完成時,我們需要使用dispatch_async(dispatch_get_main_queue(), ^{ });函式會到主執行緒內重新整理UI。並完成通訊

15.GCD的一些常用的函式?(group,barrier,訊號量,執行緒同步)

我們使用佇列組來開闢執行緒時,佇列組中的佇列任務是併發,當所有的佇列組中的所有任務完成時候,才可以呼叫佇列組完成任務。

/**建立自己的佇列*/dispatch_queue_tdispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);/**建立一個佇列組*/dispatch_group_t dispatchGroup = dispatch_group_create();/**將佇列任務新增到佇列組中*/dispatch_group_async(dispatchGroup, dispatchQueue, ^(){NSLog(@"dispatch-1");});/**將佇列任務新增到佇列組中*/dispatch_group_async(dispatchGroup, dispatchQueue, ^(){NSLog(@"dspatch-2");});/**佇列組完成呼叫函式*/dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){NSLog(@"end");})

barrier:表示柵欄,當在併發佇列裡面使用柵欄時候,柵欄之前的併發任務開始併發執行,執行完畢後,執行柵欄內的任務,等柵欄任務執行完畢後,再併發執行柵欄後的任務。

dispatch_queue_tconcurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-1");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-2");});dispatch_barrier_async(concurrentQueue, ^(){NSLog(@"dispatch-barrier"); });dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-3");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-4");});

訊號量:Semaphore是通過‘計數’的方式來標識執行緒是否是等待或繼續執行的。訊號量

dispatch_semaphore_create(int)// 建立一個訊號,並初始化訊號的計數大小/* 等待訊號,並且判斷訊號量,如果訊號量計數大於等於你建立時候的訊號量的計數,就可以通過,繼續執行,並且將你傳入的訊號計數減1,

* 如果傳入的訊號計數小於你建立的計數,就表示等待,等待訊號計數的變化

*  如果等待的時間超過你傳入的時間,也會繼續下面操作

*  第一個引數:semaphore 表示訊號量

*  第二個引數:表示等待的時間

*    返回int 如果傳入的訊號計數大於等於你建立訊號的計數時候,返回0.  反之,返回的不等於0

*/intresult = dispatch_semaphore_wait(dispatch_semaphore_tsemaphore,time outTime);// 表示等待,也是阻礙執行緒// 表示將訊號技術+1dispatch_semaphore_signl(dispatch_semaphore_tsemaphore);

實現執行緒的同步的方法:序列佇列,分組,訊號量。也是可以使用併發佇列。

//加入佇列dispatch_async(concurrentQueue, ^{//1.先去網上下載圖片dispatch_sync(concurrentQueue, ^{          });//2.在主執行緒展示到介面裡dispatch_sync(dispatch_get_main_queue(), ^{    });});

16.如何使用佇列來避免資源搶奪?

當我們使用多執行緒來訪問同一個資料的時候,就有可能造成資料的不準確性。這個時候我麼可以使用執行緒鎖的來來繫結。也是可以使用序列佇列來完成。如:fmdb就是使用FMDatabaseQueue,來解決多執行緒搶奪資源。

17.資料持久化的幾個方案(fmdb用沒用過)

持久化方案:

plist,儲存字典,陣列比較好用

preference:偏好設定,實質也是plist

NSKeyedArchiver:歸檔,可以儲存物件

sqlite:資料庫,經常使用第三方來操作,也就是fmdb

coreData:也是資料庫儲存,蘋果官方的

18.說一下appdelegate的幾個方法?從後臺到前臺呼叫了哪些方法?第一次啟動呼叫了哪些方法?從前臺到後臺呼叫了哪些方法?

1773988-ab883455881116d0.gif

19.NSCache優於NSDictionary的幾點?

1.nscache 是可以自動釋放記憶體的。

2.nscache是執行緒安全的,我們可以在不同的執行緒中新增,刪除和查詢快取中的物件。

3.一個快取物件不會拷貝key物件。

20.知不知道Designated Initializer?使用它的時候有什麼需要注意的問題?

個人理解:初始化函式,如果你想自定義初始化函式時,也是必須要初始化父類,以來保證可以繼承父類的一些方法或者屬性。

Designated Initializer

21.實現description方法能取到什麼效果?

description是nsobject的一個例項的方法,返回的是一個nsstring。當我們使用nslog列印的時候,列印出來的一般都是物件的記憶體地址,如果我們實現description方法時,我們就可以使用nslog列印物件的時候,我們可以把它裡面的屬性值和記憶體地址一起列印出來.列印什麼,就是看你寫什麼了。

-(NSString*)description{NSString* string = [NSStringstringWithFormat:@"",self,self.name,self.age];returnstring;      }

22.objc使用什麼機制管理物件記憶體?

使用記憶體管理計數器,來管理記憶體的。當記憶體管理計數器為0的時候,物件就會被釋放。

Block

1.block的實質是什麼?一共有幾種block?都是什麼情況下生成的?

block:本質就是一個object-c物件.

block:儲存位置,可能分為3個地方:程式碼去,堆區、棧區(ARC情況下會自動拷貝到堆區,因此ARC下只能有兩個地方:程式碼去、堆區)

程式碼區:不訪問棧區的變數(如區域性變數),且不訪問堆區的變數(alloc建立的物件),此時block存放在程式碼去。

堆區:訪問了處於棧區的變數,或者堆區的變數,此時block存放在堆區。–需要注意實際是放在棧區,在ARC情況下會自動拷貝到堆區,如果不是ARC則存放在棧區,所在函式執行完畢就回釋放,想再外面呼叫需要用copy指向它,這樣就拷貝到了堆區,strong屬性不會拷貝、會造成野指標錯區。

2.為什麼在預設情況下無法修改被block捕獲的變數? __block都做了什麼?

預設情況下,block裡面的變數,拷貝進去的是變數的值,而不是指向變數的記憶體的指標。

當使用__block修飾後的變數,拷貝到block裡面的就是指向變數的指標,所以我們就可以修改變數的值。

3.模擬一下迴圈引用的一個情況?block實現介面反向傳值如何實現?

Person *p = [[Person alloc]init];

[p setPersonBlock:^(NSString*str) 

{    p.name = str;}];

Runtime

1.objc在向一個物件傳送訊息時,發生了什麼?

根據物件的isa指標找到類物件id,在查詢類物件裡面的methodLists方法函式列表,如果沒有在好到,在沿著superClass,尋找父類,再在父類methodLists方法列表裡面查詢,最終找到SEL,根據id和SEL確認IMP(指標函式),在傳送訊息;

3.什麼時候會報unrecognized selector錯誤?iOS有哪些機制來避免走到這一步?

當傳送訊息的時候,我們會根據類裡面的methodLists列表去查詢我們要動用的SEL,當查詢不到的時候,我們會一直沿著父類查詢,當最終查詢不到的時候我們會報unrecognized selector錯誤

當系統查詢不到方法的時候,會呼叫+(BOOL)resolveInstanceMethod:(SEL)sel動態解釋的方法來給我一次機會來新增,呼叫不到的方法。或者我們可以再次使用-(id)forwardingTargetForSelector:(SEL)aSelector重定向的方法來告訴系統,該呼叫什麼方法,一來保證不會崩潰。

4.能否向編譯後得到的類中增加例項變數?能否向執行時建立的類中新增例項變數?為什麼?

1.不能向編譯後得到的類增加例項變數

2.能向執行時建立的類中新增例項變數

解釋:

1.編譯後的類已經註冊在runtime中,類結構體中的objc_ivar_list例項變數的連結串列和instance_size例項變數的記憶體大小已經確定,runtime會呼叫class_setvarlayout或class_setWeaklvarLayout來處理strong weak引用.所以不能向存在的類中新增例項變數

2.執行時建立的類是可以新增例項變數,呼叫class_addIvar函式.但是的在呼叫objc_allocateClassPair之後,objc_registerClassPair之前,原因同上.

5.runtime如何實現weak變數的自動置nil?

runtime 對註冊的類, 會進行佈局,對於 weak 物件會放入一個 hash 表中。 用 weak 指向的物件記憶體地址作為 key,當此物件的引用計數為0的時候會 dealloc,假如 weak 指向的物件記憶體地址是a,那麼就會以a為鍵, 在這個 weak 表中搜尋,找到所有以a為鍵的 weak 物件,從而設定為 nil。

6.給類新增一個屬性後,在類結構體裡哪些元素會發生變化?

instance_size :例項的記憶體大小

objc_ivar_list *ivars:屬性列表

RunLoop

1.runloop是來做什麼的?runloop和執行緒有什麼關係?主執行緒預設開啟了runloop麼?子執行緒呢?

runloop:字面意思就是跑圈,其實也就是一個迴圈跑圈,用來處理執行緒裡面的事件和訊息。

runloop和執行緒的關係:每個執行緒如果想繼續執行,不被釋放,就必須有一個runloop來不停的跑圈,以來處理執行緒裡面的各個事件和訊息。

主執行緒預設是開啟一個runloop。也就是這個runloop才能保證我們程式正常的執行。子執行緒是預設沒有開始runloop的

2.runloop的mode是用來做什麼的?有幾種mode?

model:是runloop裡面的模式,不同的模式下的runloop處理的事件和訊息有一定的差別。

系統預設註冊了5個Mode:

(1)kCFRunLoopDefaultMode: App的預設 Mode,通常主執行緒是在這個 Mode 下執行的。

(2)UITrackingRunLoopMode: 介面跟蹤 Mode,用於 ScrollView 追蹤觸控滑動,保證介面滑動時不受其他 Mode 影響。

(3)UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成後就不再使用。

(4)GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到。

(5)kCFRunLoopCommonModes: 這是一個佔位的 Mode,沒有實際作用。

注意iOS 對以上5中model進行了封裝

NSDefaultRunLoopMode;

NSRunLoopCommonModes

3.為什麼把NSTimer物件以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)新增到主執行迴圈以後,滑動scrollview的時候NSTimer卻不動了?

nstime物件是在NSDefaultRunLoopMode下面呼叫訊息的,但是當我們滑動scrollview的時候,NSDefaultRunLoopMode模式就自動切換到UITrackingRunLoopMode模式下面,卻不可以繼續響應nstime傳送的訊息。所以如果想在滑動scrollview的情況下面還呼叫nstime的訊息,我們可以把nsrunloop的模式更改為NSRunLoopCommonModes

類結構

1.isa指標?(物件的isa,類物件的isa,元類的isa都要說)

在oc中,類也是物件,所屬元類。所以經常說:萬物皆物件

物件的isa指標指向所屬的類

類的isa指標指向了所屬的元類

元類的isa指向了根元類,根元類指向了自己。

1773988-f3b90f8a4d90e32b.png

2.類方法和例項方法有什麼區別?

呼叫的方式不同,類方法必須使用類呼叫,在方法裡面不能呼叫屬性,類方法裡面也必須呼叫類方法。儲存在元類結構體裡面的methodLists裡面

例項方法必須使用例項物件呼叫,可以在例項方法裡面使用屬性,例項方法也必須呼叫例項方法。儲存在類結構體裡面的methodLists裡面

3.介紹一下分類,能用分類做什麼?內部是如何實現的?它為什麼會覆蓋掉原來的方法?

category:我們可以給類或者系統類新增例項方法方法。我們新增的例項方法,會被動態的新增到類結構裡面的methodList列表裡面。categort

4.執行時能增加成員變數麼?能增加屬性麼?如果能,如何增加?如果不能,為什麼

可以新增屬性的,但必須我們實現它的getter和setter方法。但是沒有新增帶下滑線同名的成員變數

但是我們使用runtime我們就可以實現新增成員變數方法如下

- (void)setName:(NSString *)name {/** *  為某個類關聯某個物件 * *@paramobject#> 要關聯的物件 description#> *@paramkey#>    要關聯的屬性key description#> *@paramvalue#>  你要關聯的屬性 description#> *@parampolicy#> 新增的成員變數的修飾符 description#> */objc_setAssociatedObject(self, @selector(name), name,  OBJC_ASSOCIATION_COPY_NONATOMIC);}- (NSString *)name {/** *  獲取到某個類的某個關聯物件 * *@paramobject#> 關聯的物件 description#> *@paramkey#>    屬性的key值 description#> */returnobjc_getAssociatedObject(self, @selector(name));}

5.objc中向一個nil物件傳送訊息將會發生什麼?(返回值是物件,是標量,結構體)

• 如果一個方法返回值是一個物件,那麼傳送給nil的訊息將返回0(nil)。例如:Person * motherInlaw = [ aPerson spouse] mother]; 如果spouse物件為nil,那麼傳送給nil的訊息mother也將返回nil。

• 如果方法返回值為指標型別,其指標大小為小於或者等於sizeof(void*),float,double,long double 或者long long的整型標量,傳送給nil的訊息將返回0。

• 如果方法返回值為結構體,正如在《Mac OS X ABI 函式呼叫指南》,傳送給nil的訊息將返回0。結構體中各個欄位的值將都是0。其他的結構體資料型別將不是用0填充的。

• 如果方法的返回值不是上述提到的幾種情況,那麼傳送給nil的訊息的返回值將是未定義的。

詳細解答

高階

1.UITableview的優化方法(快取高度,非同步繪製,減少層級,hide,避免離屏渲染)

快取高度:當我們建立frame模型的時候,計算出來cell的高度的時候,我們可以將cell的高度快取到字典裡面,以cell的indexpath和Identifier作為為key。

NSString*key = [[HeightCache shareHeightCache] makeKeyWithIdentifier:@"YwywProductGradeCell"indexPath:indexPath];if([[HeightCache shareHeightCache] existInCacheByKey:key]) {return[[HeightCache shareHeightCache] heightFromCacheWithKey:key];}else{    YwywProductGradeModelFrame *modelFrame =self.gradeArray[indexPath.row];    [[HeightCache shareHeightCache] cacheHieght:modelFrame.cellHight key:key];returnmodelFrame.cellHight;}

非同步繪製、減少層級:目前還不是很清楚

hide:個人理解應該是hidden吧,把可能會用到的控制元件都建立出來,根據不同的情況去隱藏或者顯示出來。

避免離屏渲染:只要不是同時使用邊框/邊框顏色以及圓角的時候,都可以使用layer直接設定。不會造成離屏渲染。

4.SDWebImage的快取策略?

sd載入一張圖片的時候,會先在記憶體裡面查詢是否有這張圖片,如果沒有會根據圖片的md5(url)後的名稱去沙盒裡面去尋找,是否有這張圖片,如果沒有會開闢執行緒去下載,下載完畢後載入到imageview上面,並md(url)為名稱快取到沙盒裡面。

5.AFN為什麼新增一條常駐執行緒?

AFN 目的:就是開闢執行緒請求網路資料。如果沒有常住執行緒的話,就會每次請求網路就去開闢執行緒,完成之後銷燬開闢執行緒,這樣就造成資源的浪費,開闢一條常住執行緒,就可以避免這種浪費,我們可以在每次的網路請求都新增到這條執行緒。

6.KVO的使用?實現原理?(為什麼要建立子類來實現)

kvo:鍵值觀察,根據鍵對應的值的變化,來呼叫方法。

註冊觀察者:addObserver:forKeyPath:options:context:

實現觀察者:observeValueForKeyPath:ofObject:change:context:

移除觀察者:removeObserver:forKeyPath:(物件銷燬,必須移除觀察者)

注意

使用kvo監聽A物件的時候,監聽的本質不是這個A物件,而是系統建立的一箇中間物件NSKVONotifying_A並繼承A物件,並且A物件的isa指標指向的也不是A的類,而是這個NSKVONotifying_A物件

kvo詳解

kvo詳解2

7.KVC的使用?實現原理?(KVC拿到key以後,是如何賦值的?知不知道集合操作符,能不能訪問私有屬性,能不能直接訪問_ivar)

kvc:鍵值賦值,使用最多的即使字典轉模型。利用runtime獲取物件的所有成員變數, 在根據kvc鍵值賦值,進行字典轉模型

setValue: forKey: 只查詢本類裡面的屬性

setValue: forKeyPath:會查詢本類裡面屬性,沒有會繼續查詢父類裡面屬性。

kvc詳解

作品連結:https://www.jianshu.com/p/f9eb6b315c08

相關文章