1九、iOS面試題·自整理·Three

1.請簡述你對工廠方法的理解?mysql

工廠Mycontrol,設計控件是用到工廠設計模式。類簇相似於工廠設計模式;工廠模式就是定義建立對象的接口,讓子類決定實例化哪個類。這樣,類的實例化就推遲到了子類程序員

 

2.UITableView有哪些優化方式?web

    UITableView的優化主要從三個方面入手:面試

提早計算並緩存好高度(佈局),由於heightForRowAtIndexPath:是調用最頻繁的方法;sql

異步繪製,遇到複雜界面,遇到性能瓶頸時,可能就是突破口;數據庫

滑動時按需加載,這個在大量圖片展現,網絡加載的時候很管用!(SDWebImage已經實現異步加載,配合這條性能槓槓的)。設計模式

 

3.你如何理解blockblock有什麼用途?數組

  咱們能夠把Block當作Objective-C的匿名函數。Block容許開發者在兩個對象之間將任意的語句當作數據進行傳遞,每每這要比引用定義在別處的函數直觀。另外,block的實現具備封閉性(closure),而又可以很容易獲取上下文的相關狀態信息。緩存

block是代碼塊,其本質和變量相似。不一樣的是代碼塊存儲的數據是一個函數體。使用Block,就能夠像其餘標準函數同樣,傳入參數,並獲得返回值。網絡

做爲OC對象的屬性,實現對象之間的傳值    • Block能夠看作是一個變量,所以能夠做爲OC對象的屬性

4.請問怎樣可以保證定位更省電?

1.開啓開始定位以後,不關閉,讓其持續定位

  2.設置距離篩選器:座標移動到指定距離纔會調用代理方法

  3.設置精準度:經過下降計算的過程(GPS),來達到省電的目的

5.請簡述NSUserDefaults的使用場景和 使用注意事項?

SUserDefaults適合存儲輕量級的數據,他不只能夠存儲基本數據類型,還能夠存儲NSNumberIntegerFloatDouble),NSStringNSDateNSArrayNSDictionaryBOOL類型。

可是NSUserDefaults不能存儲自定義的類對象,若把一個對象存儲到NSUserDefaults會報錯。聰明的人會把對象放進數組,再把數組存入NSUserDefaults,不過這樣作事錯誤的,由於數組中包含了自定義對象。

若要在NSUserDefaults中存入自定義對象,則對象須要遵循NSCoding協議,並實現encodeWithCoder方法和initWithCoder方法。具體參考http://my.oschina.NET/u/1245365/blog/294449

值得一提的是,我發現類型爲NSNull的空數據也是沒法存入NSUserDefaults的。若數據中有NSNull類型空數據,把它置nil便可存入NSUserDefaults

總之,NSUserDefaults是一種操做簡單的數據庫

6.iOS中數據庫使用什麼技術實現的 ?

使用SqliteCoreData實現的

7.iOS中如何實現數據模型的存儲?

 歸檔也是iOS提供給開發者的一種數據存儲的方式,事實上,幾乎全部的數據類型均可以經過歸檔來進行存取。其存儲與讀取的過程,主要封裝在兩個類中:NSKeyedArchiverNSKeyedUnarchiver

8.爲何說Objective-C是一門動態的語言?

   Objective-C是動態語言,它並不是經過調用類的方法來執行功能,而是給對象發送消息,

  對象在接收到消息以後會去找匹配的方法來運行。

9.講一下MVC和MVVM,MVP?

1.MVC做爲老牌架構, 優勢在於將業務場景按展現數據類型劃分出多個模塊, 每一個模塊中的C層負責業務邏輯和業務展現, 而M和V應該是互相隔離的以作重用, 另外每一個模塊處理得當也能夠做爲重用單元. 拆分在於解耦, 順便作了減負, 隔離在於重用, 提高開發效率. 缺點是沒有區分業務邏輯和業務展現, 對單元測試不友好.

2.MVP做爲MVC的進階版, 提出區分業務邏輯和業務展現, 將全部的業務邏輯轉移到P層, V層接受P層的數據更新通知進行頁面展現. 優勢在於良好的分層帶來了友好的單元測試, 缺點在於分層會讓代碼邏輯優勢繞, 同時也帶來了大量的代碼工做, 對程序員不夠友好.

3.MVVM做爲集大成者, 經過數據綁定作數據更新, 減小了大量的代碼工做, 同時優化了代碼邏輯, 只是學習成本有點高, 對新手不夠友好.

4.MVP和MVVM由於分層因此會創建MVC兩倍以上的文件類, 須要良好的代碼管理方式.

5.在MVP和MVVM中, V和P或者VM之間理論上是多對多的關係, 不一樣的佈局在相同的邏輯下只須要替換V層, 而相同的佈局不一樣的邏輯只須要替換P或者VM層. 但實際開發中P或者VM每每由於耦合了V層的展現邏輯退化成了一對一關係(好比SceneA中須要顯示"xxx+Name", VM就將Name格式化爲"xxx + Name". 某一天SceneB也用到這個模塊, 全部的點擊事件和頁面展現都同樣, 只是Name展現爲"yyy + Name", 此時的VM由於耦合SceneA的展現邏輯, 就顯得比較尷尬), 針對此類狀況, 一般有兩種辦法, 一種是在VM層加狀態進而判斷輸出狀態, 一種是在VM層外再加一層FormatHelper. 前者可能由於狀態過多顯得代碼難看, 後者雖然比較優雅且拓展性高, 可是過多的分層在數據還原時就略顯笨拙, 你們應該按需選擇.

這裏隨便瞎扯一句, 有些文章上來就說MVVM是爲了解決C層臃腫, MVC難以測試的問題, 其實並非這樣的. 按照架構演進順序來看, C層臃腫大部分是沒有拆分好MVC模塊, 好好拆分就好了, 用不着MVVM. 而MVC難以測試也能夠用MVP來解決, 只是MVP也並不是完美, 在VP之間的數據交互太繁瑣, 因此才引出了MVVM. 當MVVM這個徹底體出現之後, 咱們從結果看起源, 發現它作了好多事情, 其實並非, 它的前輩們付出的努力也並很多!

10.爲何在默認狀況下沒法修改被block捕獲的變量? __block都作了什麼?

  若是是用block(用static也能夠)修飾的局部變量,在block內部訪問的話,而是把這個局部變量的地址傳遞過去了,因此會跟蹤這個局部變量的變化,而且能夠修改,
若是block內部引用的變量是全局變量的話,那麼在block內部訪問,他也是把這個變量的地址傳遞過去了.。

11.模擬一下循環引用的一個狀況?block實現界面反向傳值如何實現?

ClassA和ClassB分屬兩個不一樣的線程,ClassB一般由ClassA發起請求建立,並由ClassA使用,ClassB則會在必要時通知ClassA一些事件。二者中各保留了對方的一個引用計數指針RefPtr。

若是在析構時釋放成員變量的話,就會發生循環引用的問題,致使兩個對象釋放失敗。

block的回調的使用步驟

        1.聲明    : 在誰那裏調用就在誰那裏聲明

            實現代碼

                typedef void(^MyBlock)(NSString *name);//block的重命名

                @property (nonatomic,copy) MyBlock block;//block的聲明

 

        2.實現    : 誰要裝值就在誰那裏實現

            實現代碼

                 SecondViewController *secondVC = [[SecondViewController alloc] init];

                 [self presentViewController:secondVC animated:YES completion:nil];//在這裏沒用導航控制器,用presentViewController來進入下一個視圖

                 //block實現

                 secondVC.block = ^void(NSString *name)

                 {

                    _label.text = name;

                 };//block的位置擺放很做用的,由於它是一個函數,不過必定不能放在使用它的對象的外面和前面就行了

 

        3.調用    : 誰要傳值就在誰那裏調用

                self.block(@"呵呵");//block的調用

 總結一句話:block用在不一樣視圖控制器之間的值回傳,回傳還有代理、單例,在回傳中最簡單的就是用block了

 */

 12.objc在向一個對象發送消息時,發生了什麼?

SomeClass * someObject; someObject = nil; [someObject doSomething];

就像這樣,向nil發送了doSomething;OC中nil是被當作0定義的。也就是說runtime要去獲取這個nil的信息,會去讀取內存中0的位置,這確定是不容許的,會返回nil,0,0.0等數據,根據返回值類型。

比較讓你混淆的是,殭屍對象。殭屍對象並非nil,殭屍對象是你的object被銷燬或者用於其餘地方了,可是指向它的指針還在。會發生向一個object發送一個它沒有的方法。

13.何時會報unrecognized selector錯誤?iOS有哪些機制來避免走到這一步?

   對象未實現該方法。

   對象已經被釋放。

  

    使用[id respondsToSelector:]進行判斷。


forward.jpeg


Method resolution
objc運行時會調用+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機會提供一個函數實現。若是你添加了函數,那運行時系統就會從新啓動一次消息發送的過程,不然 ,運行時就會移到下一步,消息轉發(Message Forwarding)。
返回Nil和self,去調用第三步methodSignatureForSelector和forwarInvocation;返回receiver,若是receiver有響應就直接處理,若是沒有就去對應的對象內去調用第三步;調用子類的函數,子類沒有進行這幾個方法的重載,在父類處理時返回子類,會死循環。
Fast forwarding
若是目標對象實現了-forwardingTargetForSelector:,Runtime 這時就會調用這個方法,給你把這個消息轉發給其餘對象的機會。 只要這個方法返回的不是nil和self,整個消息發送的過程就會被重啓,固然發送的對象會變成你返回的那個對象。不然,就會繼續Normal Fowarding。 這裏叫Fast,只是爲了區別下一步的轉發機制。由於這一步不會建立任何新的對象,但下一步轉發會建立一個NSInvocation對象,因此相對更快點。
Normal forwarding
這一步是Runtime最後一次給你挽救的機會。首先它會發送-methodSignatureForSelector:消息得到函數的參數和返回值類型。若是-methodSignatureForSelector:返回nil,Runtime則會發出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。若是返回了一個函數簽名,Runtime就會建立一個NSInvocation對象併發送-forwardInvocation:消息給目標對象。

14.可否向編譯後獲得的類中增長實例變量?可否向運行時建立的類中添加實例變量?爲何?

  

  1. 不能向編譯後獲得的類增長實例變量
  2. 能向運行時建立的類中添加實例變量

解釋:

  1. 編譯後的類已經註冊在runtime中,類結構體中的objc_ivar_list實例變量的鏈表和instance_size實例變量的內存大小已經肯定,runtime會調用class_setvarlayout或class_setWeaklvarLayout來處理strong weak引用.因此不能向存在的類中添加實例變量

     2.運行時建立的類是能夠添加實例變量,調用class_addIvar函數.可是的在調用objc_allocateClassPair以後,objc_registerClassPair以前,緣由同上.

15.runtime如何實現weak變量的自動置nil?

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

16.給類添加一個屬性後,在類結構體裏哪些元素會發生變化?

定義結構體會

17.runloop是來作什麼的?runloop和線程有什麼關係?主線程默認開啓了runloop麼?子線程呢?

總的說來,Run loop,正如其名,loop表示某種循環,和run放在一塊兒就表示一直在運行着的循環。
實際上,run loop和線程是緊密相連的,能夠這樣說run loop是爲了線程而生,沒有線程,它就沒有存在的必要。
Run loops是線程的基礎架構部分,Cocoa和CoreFundation都提供了run loop對象方便配置和管理線程的run loop。
每一個線程,包括程序的主線程(main thread)都有與之相應的run loop對象。
18.runloop的mode是用來作什麼的?有幾種mode?

用來控制一些特殊操做只能在指定模式下運行,通常能夠經過指定操做的運行mode 來控制執行時機,以提升用戶體驗
系統默認註冊了 5 個 Mode

kCFRunLoopDefaultMode:App 的默認 Mode,一般主線程是在這個 Mode

下運行,對應 OC 中的:NSDefaultRunLoopMode

UITrackingRunLoopMode:界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑

動,保證界面滑動時不受其餘 Mode 影響

kCFRunLoopCommonModes:這是一個標記 Mode,不是一種真正的 Mode,事件

能夠運行在全部標有 common modes 標記的模式中,對應 OC 中的

NSRunLoopCommonModes , 帶 有 common modes 標 記 的 模 式 有 :UITrackingRunLoopMode 和 kCFRunLoopDefaultMode

UIInitializationRunLoopMode:在啓動 App 時進入的第一個 Mode,啓動完成後

就再也不使用
GSEventReceiveRunLoopMode:接受系統事件的內部 Mode,一般用不到 

runloop和線程的關係:主線程的run loop默認是啓動的, 子線程的runloop默認是不開啓的,須要咱們本身手動開啓循環; 。
19.爲何把NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環之後,滑動scrollview的時候NSTimer卻不動了?

RunLoop只能運行在一種mode下,若是要換mode,當前的loop也須要停下重啓成新的。利用這個機制,ScrollView滾動過程當中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode會切換到UITrackingRunLoopMode來保證ScrollView的流暢滑動:只能在NSDefaultRunLoopMode模式下處理的事件會影響scrllView的滑動。

若是咱們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環中的時候, ScrollView滾動過程當中會由於mode的切換,而致使NSTimer將再也不被調度。

同時由於mode仍是可定製的,因此:

Timer計時會被scrollView的滑動影響的問題能夠經過將timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)來解決。

20.蘋果是如何實現Autorelease Pool的?

每個線程的 autoreleasepool 其實就是一個指針的堆棧;

每個指針表明一個須要 release 的對象或者 POOL_SENTINEL(哨兵對象,表明一個 autoreleasepool 的邊界);

一個 pool token 就是這個 pool 所對應的 POOL_SENTINEL 的內存地址。當這個 pool 被 pop 的時候,全部內存地址在 pool token 以後的對象都會被 release ;

這個堆棧被劃分紅了一個以 page 爲結點的雙向鏈表。pages 會在必要的時候動態地增長或刪除;

Thread-local storage(線程局部存儲)指向 hot page ,即最新添加的 autoreleased 對象所在的那個 page 。

21.isa指針?(對象的isa,類對象的isa,元類的isa都要說)

1、類的基本概念:

一、類其實也是一個對象, 這個對象會在這個類第一次被使用的時候建立

二、只要有了類對象, 未來就能夠經過類對象來建立實例對象

三、實例對象中有一個isa指針, 指向建立本身的類對象

四、類對象中保存了當前對象全部的對象方法

五、當給一個實例對象發送消息的時候, 會根據實例對象中的isa指針去對應的類對象中查找

六、全部類對象的繼承關係就是元類對象的繼承關係

 

2、isa指針

1.每個對象都包含一個isa指針.這個指針指向當前對象所屬的類。

2.[d bark];表示給d所指向的對象發送一條bark消息,調用對象的bark方法,此時對象會順着內部的isa指針找到存儲於類中的方法並執行。

3.isa是對象中的隱藏指針,指向建立這個對象的類。

4.經過isa指針咱們能夠在運行的時候知道當前對象是屬於那個類。

 

3、元類

一、元類的定義:元類是類對象的類,每一個類都有本身獨一無二的元類,即

   (1)當你給對象發送消息時,消息是在尋找這個對象的類的方法列表。

   (2)當你給類發消息時,消息是在尋找這個類的元類的方法列表。

元類是必不可少的,由於它存儲了類的類方法。每一個類都必須有獨一無二的元類,由於每一個類都有獨一無二的類方法。

二、元類的類:

   (1)元類,就像類同樣,它也是一個對象,也能夠調用它的方法。這就意味着他必須也有一個類。

 (2)全部的元類都使用根元類(繼承體系中處於頂端的類的元類)做爲他們的類。即全部NSObject的子類的元類都會以NSObject的元類做爲他們的類。

   (3)全部的元類使用根元類做爲他們的類,根元類的元類則就是它本身。也就是說基類的元類的isa指針指向他本身。

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

由於分類方法加入類中這一操做是在運行期系統加載分類時完成的,運行期系統會把分類中所實現的每個方法都加入類的方法列表中,具體步驟以下:

  1. category的實例方法、協議以及屬性添加到類上

  2. category的類方法和協議添加到類的metaclass上

category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的後面,因此category的方法會「覆蓋」掉原來類的同名方法。

23.運行時能增長成員變量麼?能增長屬性麼?若是能,如何增長?若是不能,爲何?

不少人在面試的時候都會被問到Category,既然容許用Category給類增長方法和屬性,那爲何不容許增長成員變量?
在Objective-C提供的runtime函數中,確實有一個class_addIvar()函數用於給類添加成員變量,可是閱讀過蘋果的官方文檔的人應該會看到:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

大概的意思說,這個函數只能在「構建一個類的過程當中」調用。一旦完成類定義,就不能再添加成員變量了。通過編譯的類在程序啓動後就被runtime加載,沒有機會調用addIvar。程序在運行時動態構建的類須要在調用objc_registerClassPair以後才能夠被使用,一樣沒有機會再添加成員變量。

24.objc中向一個nil對象發送消息將會發生什麼?(返回值是對象,是標量,結構體)

在 Objective-C 中向 nil 發送消息是徹底有效的——只是在運行時不會有任何做用:

  1. 若是一個方法返回值是一個對象,那麼發送給nil的消息將返回0(nil)。例如:
    Person * motherInlaw = [[aPerson spouse] mother];
    若是 spouse 對象爲 nil,那麼發送給 nil 的消息 mother 也將返回 nil。
  2. 若是方法返回值爲指針類型,其指針大小爲小於或者等於sizeof(void*),float,double,long double 或者 long long 的整型標量,發送給 nil 的消息將返回0。
  3. 若是方法返回值爲結構體,發送給 nil 的消息將返回0。結構體中各個字段的值將都是0。
  4. 若是方法的返回值不是上述提到的幾種狀況,那麼發送給 nil 的消息的返回值將是未定義的。
相關文章
相關標籤/搜索