iOS基礎面試題

weixin_34249678發表於2016-06-22

GitHub: https://github.com/LiCheng244/LCUtils
個人部落格: http://www.licheng244.com/


1. Object-c的類可以多重繼承麼?可以實現多個介面麼?Category是什麼?重寫一個類的方式用繼承好還是分類好?為什麼?

Object-c的類不可以多重繼承;可以實現多個介面,通過實現多個介面可以完成C++的多重繼承;Category是類別,又叫分類;一般情況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其他類與原有類的關係。

2. #import 跟#include 又什麼區別,@class呢, #import<> 跟 #import”"又什麼區別?

import是Objective-C匯入標頭檔案的關鍵字;
include是C/C++匯入標頭檔案的關鍵字;
使用#import標頭檔案會自動只匯入一次,不會重複匯入,相當於#include和#pragma once;
c語言解決重複匯入標頭檔案的方法是:在標頭檔案的最開始加上巨集定義的方法(MYLIB_H)。
@class告訴編譯器某個類只是一個類名,當執行時,才去檢視類的實現檔案,可以解決標頭檔案的相互包含;這種寫法叫做類的前置宣告。
import<>用來包含系統的標頭檔案,#import””用來包含使用者標頭檔案。

3. 屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什麼作用,在那種情況下用?

readwrite 是可讀可寫特性;需要生成getter方法和setter方法時
readonly 是隻讀特性 只會生成getter方法 不會生成setter方法 ;不希望屬性在類外改變
assign 是賦值特性,setter方法將傳入引數賦值給例項變數;僅設定變數時;
retain 表示持有特性,setter方法將傳入引數先保留,再賦值,傳入引數的retaincount會+1;
copy 表示拷貝特性,setter方法將傳入物件複製一份;需要完全一份新的變數時。
nonatomic 非原子操作,決定編譯器生成的setter getter是否是原子操作,atomic表示多執行緒安全,一般使用nonatomic

4. 寫一個setter方法用於完成@property (nonatomic,retain)NSString *name,寫一個setter方法用於完成@property(nonatomic,copy)NSString *name
  • (void)setName:(NSString *) str
    {
    [str retain];
    [name release];
    name = str;
    }
  • (void)setName:(NSString *)str
    {
    id t = [str copy];
    [name release];
    name = t;
    }

######5. 對於語句NSString*obj = [[NSData alloc] init]; obj在編譯時和執行時分別時什麼型別的物件?
>編譯時是NSString的型別;執行時是NSData型別的物件

######6. 常見的object-c的資料型別有那些, 和C的基本資料型別有什麼區別?如:NSInteger和int
>object-c的資料型別有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,這些都是class,建立後便是物件。
而[C語言](http://lib.csdn.net/base/c)的基本資料型別int,只是一定位元組的記憶體空間,用於存放數值;
NSInteger是基本資料型別,並不是NSNumber的子類,當然也不是NSObject的子類。NSInteger是基本資料型別Int或者Long的別名(NSInteger的定義typedef long NSInteger),它的區別在於,NSInteger會根據系統是32位還是64位來決定是本身是int還是Long。 

######7. id 宣告的物件有什麼特性?
>Id 宣告的物件具有執行時的特性,即可以指向任意型別的objcetive-c的物件;

######8. Objective-C如何對記憶體管理的,說說你的看法和解決方法?
>Objective-C的記憶體管理主要有三種方式ARC(自動記憶體計數)、手動記憶體計數、記憶體池。

######9. 記憶體管理的幾條原則時什麼?按照預設法則.哪些關鍵字生成的物件需要手動釋放?在和property結合的時候怎樣有效的避免記憶體洩露?
>誰申請,誰釋放。遵循Cocoa Touch的使用原則;
記憶體管理主要要避免“過早釋放”和“記憶體洩漏”,對於“過早釋放”需要注意@property設定特性時,一定要用對特性關鍵字,對於“記憶體洩漏”,一定要申請了要負責釋放,要細心。
關鍵字alloc 或new 生成的物件需要手動釋放;
設定正確的property屬性,對於retain需要在合適的地方釋放。

######10. 如何對iOS裝置進行效能測試?
>Profile-> Instruments ->Time Profiler

######11. Object C中建立執行緒的方法是什麼?如果在主執行緒中執行程式碼,方法是什麼?如果想延時執行程式碼、方法又是什麼?
>執行緒建立有三種方法:`使用NSThread建立`、`使用GCD的dispatch`、`使用子類化的NSOperation,然後將其加入NSOperationQueue`;
在主執行緒執行程式碼,方法是`performSelectorOnMainThread`;
如果想延時執行程式碼可以用`performSelector:onThread:withObject: afterDelay:` 或者使用GCD的函式: `
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2秒後非同步執行這裡的程式碼...});`

######12. 淺複製和深複製的區別?
>**淺層複製:**只複製指向物件的指標,而不復制引用物件本身。
**深層複製:**複製引用物件本身。
意思就是說我有個A物件,複製一份後得到A_copy物件後,對於淺複製來說,A和A_copy指向的是同一個記憶體資源,複製的只不過是是一個指標,物件本身資源還是隻有一份,那如果我們對A_copy執行了修改操作,那麼發現A引用的物件同樣被修改,這其實違背了我們複製拷貝的一個思想。深複製就好理解了,記憶體中存在了兩份獨立物件本身。
**用網上一哥們通俗的話將就是:**
淺複製好比你和你的影子,你完蛋,你的影子也完蛋
深複製好比你和你的克隆人,你完蛋,你的克隆人還活著。

######13. 類別的作用?繼承和類別在實現中有何區別?
>類別主要有3個**作用:**
(1) 將類的實現分散到多個不同檔案或多個不同框架中。
(2) 建立對私有方法的前向引用。
(3) 向物件新增非正式協議。 
**category:** 可以在不獲悉,不改變原來程式碼的情況下往裡面新增新的方法,只能新增,不能刪除修改。並且如果類別和原來類中的方法產生名稱衝突,則類別將覆蓋原來的方法,因為類別具有更高的優先順序。
**繼承:** 可以增加、擴充套件父類方法,並且可以增加屬性。

######14. 類別和類擴充套件的區別? 
>category和extensions的不同在於 extensions可以新增屬性。另外extensions新增的方法是必須要實現的。extensions可以認為是一個私有的Category。

######15. 代理的作用?
>代理的目的是改變或傳遞控制鏈。允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指標。可以減少框架複雜度。另外一點,代理可以理解為java中的回撥監聽機制的一種類似。
 
######16. 什麼是推送訊息?
>iOS中訊息推送機制又叫訊息機制,其包括兩類:一類是**本地通知**;另一類是推送通知,也叫**遠端通知**。
兩種通知在iOS中的表現一致,可以通過橫幅或者彈出提醒兩種形式告訴使用者,並且點選通知可以會開啟應用程式。
但是實現原理卻完全不同:
**本地通知: **是由本地應用觸發的,它是基於時間行為的一種通知形式; 
**推送通知:**是由應用服務提供商發起的,通過蘋果的APNs(Apple Push Notification Server)傳送到應用客戶端,如下圖:![](http://upload-images.jianshu.io/upload_images/1975627-3d0fc96dc7372a95?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

######17. 什麼時候使用NSMutableArray,什麼時候使用NSArray?
>當陣列在程式執行時,需要不斷變化的,使用NSMutableArray;
當陣列在初始化後,便不再改變的,使用NSArray。
需要指出的是,使用NSArray只表明的是該陣列在執行時不發生改變,即不能往NSAarry的陣列裡新增和刪除元素,但不表明其陣列內的元素的內容不能發生改變。
NSArray是執行緒安全的,NSMutableArray不是執行緒安全的,多執行緒使用到NSMutableArray需要注意。

######17. frame和bounds有什麼不同?
>**frame**指的是:該view在父view座標系統中的位置和大小。(參照點是父親的座標系統)
**bounds**指的是:該view在本身座標系統中 的位置和大小。(參照點是本身座標系統)

######18. 談談Object-C的記憶體管理方式及過程?
>1). 當你使用new,alloc和copy方法建立一個物件時,該物件的保留計數器值為1.當你不再使用該物件時,你要負責向該物件傳送一條release或autorelease訊息.這樣,該物件將在使用壽命結束時被銷燬.
2). 當你通過任何其他方法獲得一個物件時,則假設該物件的保留計數器值為1,而且已經被設定為自動釋放,你不需要執行任何操作來確保該物件被清理.如果你打算在一段時間內擁有該物件,則需要保留它並確保在操作完成時釋放它.
3). 如果你保留了某個物件,你需要(最終)釋放或自動釋放該物件.必須保持retain方法和release方法的使用次數相等.

######19. Objective-C堆和棧的區別?
>**管理方式:**
對於棧來講,是由編譯器自動管理,無需我們手工控制;
對於堆來說,釋放工作由程式設計師控制,容易產生memory leak。
**申請大小:**
**棧:**在Windows下,棧是向低地址擴充套件的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
**堆:**堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。這是由於系統是用連結串列來儲存的空閒記憶體地址的,自然是不連續的,而連結串列的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。
碎片問題:對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低。對於棧來講,則不會存在這個問題,因為棧是先進後出的佇列,他們是如此的一一對應,以至於永遠都不可能有一個記憶體塊從棧中間彈出
**分配方式:**
堆都是動態分配的,沒有靜態分配的堆。
棧有2種分配方式:靜態分配和動態分配。
靜態分配是編譯器完成的,比如區域性變數的分配。
動態分配由alloca函式進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。
**分配效率:**
棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。
堆則是C/C++函式庫提供的,它的機制是很複雜的。

######20. static 關鍵字的作用?
>1). 函式體內 static 變數的作用範圍為該函式體,不同於 auto 變數,該變數的記憶體只被分配一次,
因此其值在下次呼叫時仍維持上次的值;
2). 在模組內的 static 全域性變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問;
3). 在模組內的 static 函式只可被這一模組內的其它函式呼叫,這個函式的使用範圍被限制在宣告它的模組內;
4). 在類中的 static 成員變數屬於整個類所擁有,對類的所有物件只有一份拷貝;
5). 在類中的 static 成員函式屬於整個類所擁有,這個函式不接收 this 指標,因而只能訪問類的static 成員變數。

######21. 執行緒與程式的區別和聯絡?
>1). 程式和執行緒都是由作業系統所體會的程式執行的基本單元,系統利用該基本單元實現系統對應用的併發性
2). 程式和執行緒的主要差別在於它們是不同的作業系統資源管理方式。
3). 程式有獨立的地址空間,一個程式崩潰後,在保護模式下不會對其它程式產生影響,而執行緒只是一個程式中的不同執行路徑。
4). 執行緒有自己的堆疊和區域性變數,但執行緒之間沒有單獨的地址空間,一個執行緒死掉就等於整個程式死掉。所以多程式的程式要比多執行緒的程式健壯,但在程式切換時,耗費資源較大,效率要差一些。
5). 但對於一些要求同時進行並且又要共享某些變數的併發操作,只能用執行緒,不能用程式。

######22. 關於靜態變數static?
>在函式或方法範圍外定義的變數以及用static修飾的變數都是靜態變數。靜態變數的生命週期是從程式開始到程式結束。
類方法中可以使用帶有static修飾的靜態變數, 但是無論生成多少該類的物件, 都只有一個靜態變數, 也就是說多個物件共享一個靜態變數。
一個物件對靜態變數賦值後, 在使用這個變數之前, 如果有另外一個物件修改了該靜態變數的值, 如果不進行同步的話就會發生錯誤。
利用靜態變數的這種性質可以實現物件間的資訊共享和訊息傳遞等。

######23. super 和 self 的區別?
>**super: ** super和self不同, 並不確定指向某個物件。super只能被用於呼叫父類的方法, 不能通過super完成賦值, 也不能把方法的返回值指定為super。
**self: ** 是指收到當前訊息的例項變數, 例如:B類繼承A類, B類方法中呼叫[self a]; B類中沒有a方法, 是繼承自A類中的, A類中a方法呼叫了b方法[self b]; , 此時這個self 指的是B類的物件。

######24. 動態繫結?
>**動態繫結: **在程式執行時才確定物件的屬性和需要響應的訊息。
OC中的訊息是執行時才去繫結的。執行時系統首先會確定接受者的型別(動態型別識別),然後根據訊息名在類的方法列表裡選擇響應的方法執行,如果沒有就到父類中繼續尋找, 如果一直到NSObject也沒有找到要呼叫的方法, 就會包不能識別訊息的錯誤。

######25. nil 和  null 等等.
>**nil: **空指標, 表示一個空的物件, 這個的物件的指標指向空。值為0。初始化方法失敗的時候通常會返回nil。

######26. 靜態型別檢查的總結
>OC的靜態型別檢查是在編譯期完成的。
>1). 對於id型別的變數, 呼叫任何方法都會通過編譯(當然呼叫不恰當的方法會出現執行時錯誤)
2). id型別的變數和被靜態型別的變數之間是可以相互賦值的
3). 被定義為靜態型別物件的變數, 如果呼叫了類或父類中未定義的方法, 編譯器就會提示警告
4). 如果是靜態型別的變數, 父類型別的例項變數不可以賦值給子類型別的例項變數
5). 如果是靜態型別的變數, 子類型別的例項變數可以賦值給父類型別的例項變數
6). 若要判斷到底是哪一個類的方法被執行了, 不要看變數所宣告的型別, 而要看司機執行時這個變數的型別
7). id型別並不是(NSObject *)型別

######27. 過載
> **過載: **指的是一個函式、運算子或者方法定義有多種功能, 並根據情況來選擇合適的功能。
OC 可以通過動態繫結讓同一個訊息選擇器執行不同的功能來實現過載。

######28. 為什麼不可以直接訪問屬性, 而是要通過getter 和 setter方法來實現?
>為了封裝。
如果允許直接訪問類的例項變數, 那麼當類的實現發生了變化, 例項變數被刪除或者作用發生了變化時, 所有呼叫這個類的外部模組都需要修改。
如果使用類getter/setter的形式, 當類的實現發生了變化時則只需要修改getter/setter介面, 外部呼叫部分不需要做任何修改。
雖然子類可以直接訪問父類的例項變數, 但是要儘量用getter/setter方法來訪問父類中的例項變數, 這樣可以使程式儘可能的低耦合。

######29. 例項變數的可見性
>三種修飾符: private、protected、public
![](http://upload-images.jianshu.io/upload_images/1975627-eda82138d45f322e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

相關文章