iOS經典面試題大全

1.INTERVIEW 共勉

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

2.INTERVIEW 準備

3.iOS developers 方向

4.INTERVIEW QUESTION

4.1深copy和淺copy

  • 淺拷貝:
    1.對內存地址的複製,讓目標對象指針和源對象指向同一片內存空間.
    2.內存銷燬的時候,指向這片空間的指針須要從新定義纔可使用,要否則會成爲野指針
    3.拷貝指向原來對象的指針,使原對象的引用計數加+1
    4.至關於建立了一個指向原對象的新指針,並無建立一個新的對象.

  • 深拷貝:
    1.拷貝對象的具體內容,而內存地址是自主分配的
    2.拷貝結束以後,兩個對象存在的值是相同的,內存地址是不同的
    3.兩個對象沒有任何關係
  • 本質區別:
    1.深拷貝是內容拷貝,淺拷貝是指針拷貝
    2.是否有新的內存地址
    3.是否影響內存地址的引用計數.
  • 案例一
NSString * str1 = @"copyStr";

    NSMutableString *str2 = [str1 copy];

    NSMutableString *str3 = [str1 mutableCopy];

    NSLog(@"str1:%p--%@",str1,str1);

    NSLog(@"str1:%p--%@",str2,str2);

    NSLog(@"str1:%p--%@",str3,str3);
2018-04-14 14:50:54.117652+0800 MutyCopy-Copy[2644:63575] str1:0x109a48068--copyStr
2018-04-14 14:50:54.117885+0800 MutyCopy-Copy[2644:63575] str1:0x109a48068--copyStr
2018-04-14 14:50:54.118010+0800 MutyCopy-Copy[2644:63575] str1:0x600000259a40--copyStr

1.str1,str2地址相同,而Str3地址不一樣
2.NSString的copy是淺拷貝,copy返回的對象是不可變對象
3.mutablecopy是深拷貝web

*案例二:面試

NSMutableString * str1 = [NSMutableString stringWithString:@"mutableStr"];

    NSMutableString * str2 = [str1 copy];

    NSMutableString * str3 = [str1 mutableCopy];

    NSLog(@"str:%p-----%@",str1,str1);

    NSLog(@"str:%p-----%@",str2,str2);

    NSLog(@"str:%p-----%@",str3,str3);
2018-04-14 15:04:50.092820+0800 MutyCopy-Copy[2685:70866] str:0x60000025b210-----mutableStr
2018-04-14 15:04:50.093059+0800 MutyCopy-Copy[2685:70866] str:0x60000022ca40-----mutableStr
2018-04-14 15:04:50.093217+0800 MutyCopy-Copy[2685:70866] str:0x60000025b540-----mutableStr

1.str1,str2,str3地址都不一樣
2.NSMutableString對象copy與mutableCopy都是深拷貝
3.copy返回的對象是不可變對象編程

4.2 iOS程序的啓動過程

  • 首先找到程序入口,執行main函數
  • main -->> UIApplicationMain
  • 建立UIApplication對象
  • 建立UIApplication的代理對象,給UIApplication對象代理屬性賦值
  • 開啓主運行循環,做用接收事件,讓程序一直運行
  • 加載info.plist,判斷有沒有指定main.storyboard,若是指定就去加載.

4.3 loadView

  • 何時被調用?
    每次訪問VC的view並且view爲nil,loadView方法被調用
  • 做用
    loadView方法是用來負責建立VC的view.
  • 默認實現是怎樣的?
    默認實現即[spuer loadView]
    1.它會先去查找與UIViewController相關聯的xib文件,經過加載xib文件來建立VC的view.
    2.若是在初始化VC指定了xib文件名,就會根據傳入的xib文件名加載對應的xib文件.若是沒有明顯xib文件名,就會加載跟本身同名的xib文件.
    3.若是沒有找到關聯的xib文件,就會建立一個空白的UIView,而後賦值給VC的view屬性

4.4 單例模式

4.5 多線程

  • 進程
    1.進程是指系統中正在運行的一個應用程序
    2.每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內.
  • 線程
    1.1個進程要想執行任務,必須得有線程,每1個進程至少要有1條線程
    2.線程是進程的基本執行單元
    3.一個進程的素有任務都在線程中執行
  • 多線程
    1.1個進程中能夠開啓多條線程,每條線程能夠並行(同時)執行不一樣的任務
    2.進程--工廠,線程--工廠工人
    3.多線程能夠提升程序的執行效率

好比,咱們同時開啓2條線程下載文件A,文件B.設計模式

  • 多線程的原理
    1.同一時間,CPU只能處理1條線程,只有1條線程在工做
    2.多線程併發執行,是CPU快速地在多條線程之間調度切換
注意:若是線程很是很是多,會發生什麼狀況?
cpu會在多個多線程之間進行調度,消耗大量的CPU資源.這樣的話,每條線程被調度執行的頻次會下降,線程執行效率下降.
  • 多線程的優缺點
    1.優勢:
    1.1.能適當的提升程序的執行效率
    1.2.能適當調高資源利用率
    2.缺點:
    2.1.開啓線程須要佔用必定的內存空間,若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能
    2.2線程越多,CPU在調度線程上的開銷就越大.
  • 多線程應用
    1.什麼是主線程?
    一個iOS程序運行後,默認會開啓1條線程 ,稱爲主線程
    2.主線程的主要做用?
    2.1.顯示/刷新UI界面
    2.2.處理UI事件
    3.主線程使用注意?
    3.1.別將比較耗時的操做放到主線程中
    3.2.耗時操做會卡住主線程,嚴重影響UI的流暢度
    4.多線程實現技術方案?
    pthread,NSThread,GCD,NSOperation四中方案.

4.6 NSThread

  • 建立,啓動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadFun) object:nil];
[thread start];

線程一啓動,就會告訴CPU準別就緒,能夠隨時接受CPU調度.CPU調度當前線程以後,就會在線程thread中執行self的run方法安全

  • 主線程用法

  • 其餘方式建立線程
    建立線程後自動啓動線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

隱式建立並啓動線程服務器

[self performSelectorInBackground:@selector(run) withObject:nil];
  • 線程狀態

1.啓動線程,start.就緒狀態-->>運行狀態.當新廠任務執行完畢,自動進入死亡狀態
2.阻塞(暫停)線程,進入阻塞狀態網絡

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

3.強制中止線程
進入死亡狀態多線程

注意:一旦線程中止了,就不能再次開啓任務.
  • 多線程的安全隱患
    1.資源共享
    一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源.當多線程訪問同一塊資源時,很容易引起數據錯亂和數據安全問題.
    2.如圖,

若是,多個線程訪問同一塊資源的話,會形成數據錯亂的.
咱們應該怎麼解決呢?併發

image

3.如圖,
線程A和線程B同時訪問資源變量Integer,
爲了防止搶奪資源,
線程A在讀取資源變量Integer以前先加一把鎖,
而後讀取Integer的數據並在線程A中完成數據操做(17+1=18),
而後把數據寫入Integer中,
最後開鎖Unlock.在線程A對Integer操做的過程當中,
線程B是無權訪問Integer的,
只有線程A_Unlock後,線程B才能夠訪問資源變量Integer.
4.互斥鎖使用格式
@synchronized(self){//須要鎖定的代碼}異步

注意: 鎖定1分代碼只用1把鎖,用多把鎖是無效的

5.互斥鎖的優缺點
互斥鎖的使用前提:多條線程搶奪同一塊資源
優勢:能有效防止因多線程搶奪資源形成的數據安全問題
缺點:須要消耗大量的CPU

6.nonatomic和atomic
atomic:
原子屬性,爲setter方法加鎖(默認就是atomic)
線程安全,須要消耗大量的資源
nonatomic:
非原子屬性,不會爲setter方法加鎖
非線程安全,適合內存小的移動設備

4.7 GCD

  • 什麼是GCD?
    全程Grand Central Dispatch,中樞調度器
    純C語言,提供了很是多強大的函數
  • GCD的優點
    1.GCD是蘋果公司爲多核的並行運算提出的解決方案
    2.GCD會自動利用更多的CPU內核
    3.GCD自動管理線程的生命週期(建立線程,調度任務,銷燬線程)
  • GCD有2個核心概念
    1.任務:執行什麼操做
    2.隊列;用來存聽任務
  • 任務和隊列

1.執行任務
GCD中有2個用來執行任務的函數
1.1用同步的方式執行任務

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:隊列
block:任務

1.2用異步的方式執行任務

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

1.3同步和異步的區別
同步:只能在當前線程中執行任務,不具有開啓新線程的能力
異步:能夠再新的線程中執行任務,具有開啓新線程的能力

  • 隊列的類型
    GCD的隊列能夠分爲2大類型
    併發隊列:可讓多個任務併發執行(併發功能只能在異步函數下才有效)
    串行隊列:讓任務一個接着一個地執行
  • 容易混淆的術語
    有4個術語比較容易混淆:
    同步,異步,併發,串行

注意: 同步函數 + 主隊列 == 死鎖
  • 併發隊列
    GCD默認已經提供了全局的併發隊列,供整個應用使用,不須要手動建立
    使用dispatch_get_global_queue函數得到全局的併發隊列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority,  隊列的優先級
unsigned long flags);
全局併發隊列
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  • 串行隊列
    GCD中得到串行的2中途徑
    1.使用dispatch_queue_create函數建立串行隊列
dispatch_queue_t =
dispatch_queue_create(const char*label,  隊列名稱 
dispatch_queue_attr_t attr);  隊列屬性,通常用NULL便可

2.使用主隊列
放在主隊列中的任務,都會放到主線程中執行
使用dispatch_get_main_queue()得到主隊列

dispatch_queue_t queue = dispatch_get_main_queue();
  • 從子線程回到主線程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      執行耗時的異步操做...
      dispatch_async(dispatch_get_main_queue(), ^{
      回到主線程,執行UI刷新操做
        });
});
  • 延時執行

設定好延遲的時間後,它會先執行後邊的代碼,2秒後再調用self的run方法(而且不會卡主線程,在主線程調最後會回到主線程,在子線程調最後會回到子線程)

withObject:參數
afterDelay:延遲的時間
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];

使用GCD函數(2秒後自動開啓新線程 執行block中的代碼,不會卡主當前的線程,在主/子線程調用均可以使用)

DISPATCH_TIME_NOW:如今開始的意
2.0 * NSEC_PER_SEC:設置的秒數(直接更改數字便可)
dispatch_get_main_queue():主隊列的意思
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    2秒後執行這裏的代碼... 在哪一個線程執行,跟隊列類型有關  
});

3.會卡住主線程

[NSThread sleepForTimeInterval:3]
  • 只執行一次
    使用dispatch_once函數能保證某段代碼在程序運行過程當中只被執行1次
    在設計模式中,單例模式也會用到
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

     程序運行過程當中,永遠只執行1次的代碼(這裏面默認是線程安全的)
});
  • 隊列組
    需求:1.分別異步執行2個耗時的操做,其次,等2個異步操做都執行完畢後,再回到主線程執行操做
dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     執行1個耗時的異步操做
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     執行1個耗時的異步操做
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
     等前面的異步操做都執行完畢後,回到主線程...
});
  • GCD的建立和釋放
    在iOS6.0以前,在GCD中每當使用帶creat單詞的函數建立對象以後,都應該對其進行一次release操做.
    在iOS6.0以後,GCD被歸入到了ARC的內存管理機制中,在使用GCD的時候咱們就像對待普通OC對象同樣對待GCD,所以再也不須要咱們調用release方法.
  • GCD 的基本使用

    image

    1.異步函數+併發隊列

1.建立隊列(併發隊列)

    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_CONCURRENT);

    異步函數
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

2.異步函數+串行隊列

1.建立隊列(串行隊列)
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);

    異步函數
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

3.同步函數+串行隊列

1.建立隊列(串行隊列)
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.www", DISPATCH_QUEUE_SERIAL);

    同步函數
    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

4.同步函數+併發隊列

//得到全局併發隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   // 同步函數
    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

5.異步函數+主隊列

1.得到主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();

    異步函數
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

6.同步函數+主隊列

1.得到主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_sync(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });

4.8 NSOperation

  • NSOperation做用?
    配合使用NSOperation 和NSOperationQueue也能實現多線程編程
  • NSOperation 和NSOperationoQueue實現多線程的具體步驟

  • NSOperation的子類
    NSOperation是個抽象類,並不具有封裝操做的能力,必須使用它的子類
    子類的方式有3中:
    1.NSInvocationOperation
    2.NSBlockOperation
    3.自定義子類繼承NSOperation,實現內部響應的方法
  • NSInvocationOperation
    1.建立對象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

2.調用start方法開始執行操做

- (void)start;

一旦執行操做,就會調用target的sel方法

默認狀況下,調用了start方法後並不會開一條新線程去執行操做,而是在當前線程同步執行操做;只有將NSOperation放到一個NSOperationQueue中,纔會異步執行操做
  • NSBlockOperation
    1.建立NSBlockOperation對象
+ (id)blockOperationWithBlock:(void (^)(void))block;

經過addExecutionBlock:方法添加更多的操做

- (void)addExecutionBlock:(void (^)(void))block;
只要NSBlockOperation封裝的操做數 > 1,就會異步執行操做
  • NSOperationQueue做用
    若是將NSOperation添加到NSOperationQueue(操做隊列)中,系統會自動異步執行NSOperationQueue中的操做
- (void)addOperation:(NSOperation *)operation;
- (void)addOperationWithBlock:(void (^)(void))block;
  • 最大併發數
    同時執行的任務數
    最大併發數相關的方法
-(NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
  • 自定義NSOperation
    重寫-(void)main方法,在裏面實現想執行的任務
    重寫-(void)main方法的注意點:自動建立自動釋放池,若是是異步操做,沒法訪問主線程的自動釋放池

4.9 RunLoop

  • 若是沒有RunLoop,程序輸出後就退出了
int main(int argc, char * argv[]) {
    NSLog(@"main");
    return 0;
}
  • 若是有了RunLoop,因爲main函數裏面啓動了RunLoop,因此程序並不會立刻退出,保持持續運行狀態
int main(int argc, char * argv[]) {
    BOOL run = YES;
    do{
         //執行各類任務,處理各類事件
    }while(run);
    return 0;
}
  • main函數中的RunLoop,UIApplicationMaiin函數內部就啓動了一個RunLoop,因此UIApplicationMain函數一直沒有返回,保持了程序的持續運行,這個默認啓動的RunLoop跟主線程相關聯
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
  • RunLoop與線程
    1.每條線程都有惟一的與之對應的RunLoop對象
    2.主線程的RunLoop已經自動建立好了,子線程的RunLoop須要主動建立
    3.RunLoop在第一次獲取時建立,在線程結束時銷燬
  • 得到RunLoop對象

  • RunLoop相關類

Core Foundation中關於RunLoop的5個類:
CFRunLoopRef:它本身,也就表明一個RunLoop對象
CFRunLoopModeRef:RunLoop的運行模式
CFRunLoopSourceRef:事件源
CFRunLoopTimerRef:時間的觸發器
CFRunLoopbaserverRef:觀察者 監聽CFRunLoopRef的狀態

  • CFRunLoopModeRef
    系統默認註冊了5個Mode模式:
    kCFRunLoopDefaultMode:App的默認Mode,一般主線程是在這個Mode下運行
    UITrackingRunLoopMode:界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響
    UIInitializationRunLoopMode: 在剛啓動 App 時第進入的第一個 Mode,啓動完成後就再也不使用
    GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,一般用不到
    kCFRunLoopCommonModes: 這是一個佔位用的Mode,不是一種真正的Mode
  • RunLoop處理邏輯

    image

1.通知觀察者,即將進入Loop
2.通知觀察者,將要處理定時器
3.通知觀察者,將要處理非基於端口的源
4.處理非基於端口的源
5.若是有基於端口的源準備好並處於等待狀態,當即啓動,跳到第9步
6.通知觀察者,線程即將休眠
7.休眠,等待喚醒
8.通知觀察者,線程剛被喚醒
9.處理喚醒時收到的消息,以後跳到第2步
10.通知觀察者,即將推出Loop

  • RunLoop應用

  • RunLoop面試題
    1.什麼是RunLoop?
    字面意思運行循環
    其實它內部就是do-while循環,這個循環內部不斷處理各類任務(好比Source,Timer,Observer);
    一個線程對應一個RunLoop,主線程的RunLoop默認啓動,子線程的RunLoop手動啓動;
    RunLoop只能選擇一個Mode啓動,若是當前Mode中沒有任何Source,Timer,那麼就直接退出RunLoop.

4.10 HTTP通訊過程-請求/響應

HTTP協議規定:1個完整的由客戶端發給服務器的HTTP請求中包含如下內容

文章來源於網絡,若有侵權,請聯繫小編刪除。

相關文章
相關標籤/搜索