答面試題·答J_Knight_《2017年5月iOS招人心得(附面試題)》中的面試題(一)

離職找工做中,刷一刷網上的面試題。原文連接ios

1. 爲何說Objective-C是一門動態語言

由於OC能夠在運行時能夠改變其結構:新的函數能夠被引進,已有的函數能夠被刪除等。因此,OC是一門動態語言。它具備至關多的動態特性,基本的,也是常常被提到和用到的有動態類型(Dynamic typing),動態綁定(Dynamic binding)和動態加載(Dynamic loading)面試

  1. 動態類型(Dynamic typing)
    運行時再決定對象的類型。簡單的說,就是id類型。id能夠指向任意類型的對象,而後使用的時候再肯定對象原本的類型。
id obj = someInstance;
if ([obj isKindOfClass:someClass])
{
    someClass *classSpecifiedInstance = (someClass *)obj;
    // Do Something to classSpecifiedInstance which now is an instance of someClass
    //...
}
複製代碼
  1. 動態綁定(Dynamic binding)
    便是在實例所屬類肯定後,將某些屬性和相應的方法綁定到實例上。例如class_addMethod這個方法就能夠動態的添加方法。編程

  2. 動態加載(Dynamic loading)
    讓程序在運行時添加代碼模塊以及其餘資源。用戶能夠根據須要加載一些可執行代碼和資源,而不是在啓動時就加載全部組件。好比@2x,@3x資源,就是在運行的時候根據不一樣的設備加載不一樣的資源。安全

2. 講一下MVC和MVP,MVVM?

它們都是MVC的變種,結構劃分爲:bash

  • view : 視圖
  • model : 業務數據
  • x(c,vm,p):業務邏輯的處理者,做爲M、V的橋樑

其中mvp和mvvm中的v是包含了ViewController的。服務器

看圖說話。網絡

1. MVC

2. iOS開發實際應用時的MVC

用MVC開發的時候,View和Controller耦合會很嚴重, 像 viewDidLoadviewWillAppear這些view的生命週期都會在controller裏面來管理。再加上controller還要負責代理、數據源、網絡請求等,因而controller就變得愈來愈龐大,愈來愈混亂,很很差測試。

3. MVP

跟MVC相比,咱們把全部view相關的東西都化做view模塊,其他的邏輯放到一個模塊,因而就有了MVP。MVP中的V包含了 UIViewController,它負責全部跟UI相關的東西,好比view的生命週期管理,佈局。因此P的責任更加單一,只是經過數據和狀態更新View。因爲V和P的分離,咱們會寫不少事件傳遞的代碼來鏈接V和P。好比:

import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

protocol GreetingViewPresenter {
    init(view: GreetingView, person: Person)
    func showGreeting()
}

class GreetingPresenter : GreetingViewPresenter {
    unowned let view: GreetingView
    let person: Person
    required init(view: GreetingView, person: Person) {
        self.view = view
        self.person = person
    }
    func showGreeting() {
        let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var presenter: GreetingViewPresenter!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }

    func didTapButton(button: UIButton) {
        self.presenter.showGreeting()
    }

    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }

    // layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
複製代碼

MVP帶給咱們更好的可測試性的同時又帶來了大量的代碼。像上面這個例子裏,由於V和P的分離,在須要傳遞事件時,View中的一個方法只調用Presenter的一個方法的狀況會時常發生多線程

func didTapButton(button: UIButton) {
        self.presenter.showGreeting()
    }
複製代碼

4. MVVM.

MVVM它跟 MVP 很像:

  • 把 ViewController 看作 View。
  • View 和 Model 之間沒有緊耦合

MVVM和MVP的區別主要在於數據綁定這一塊。經過響應式編程的框架好比ReactiveCocoa來把View和ViewModel綁定在一塊兒,這樣咱們就不用寫不少刷新頁面的代碼了。架構

5. VIPER

最後說一下VIPER這個框架,它不屬於MV(X)架構,它更像是樂高積木同樣搭建你的應用。VIPER對職責劃分了5個模塊。併發

  • View(頁面) - 展現給用戶的界面
  • Interactor(交互器) - 包括數據(Entities)或者網絡相關的業務邏輯。好比建立新的 entities 或者從服務器上獲取數據;要實現這些功能,你可能會用到一些服務和管理(Services and Managers):這些可能會被誤覺得成是外部依賴東西,可是它們就是 VIPER 的 Interactor 模塊。
  • Presenter(展現器) - 包括 UI(but UIKit independent)相關的業務邏輯,能夠調用 Interactor 中的方法。
  • Entities(實體) - 純粹的數據對象。不包括數據訪問層,由於這是 Interactor 的職責。
  • Router(路由) - 負責 VIPER 模塊之間的轉場

實際上 VIPER 模塊能夠只是一個頁面(screen),也能夠是你應用裏整個的用戶使用流程(the whole user story)- 好比說「驗證」這個功能,它能夠只是一個頁面,也能夠是連續相關的一組頁面。你的每一個「樂高積木」想要有多大,都是你本身來決定的。

這篇文章對幾個模式分析得很好。值得好好讀一讀。文中的幾個例子也是引自這篇文章。

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

  • 避免循環引用。

    @interface SubObj : NSObject
    @property(nonatomic,strong) id delegate;
    @end
    
    @interface ViewController : UIViewController
    @property(nonatomic,strong)SubObj * obj;
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.obj = [[SubObj alloc] init];
        self.obj.delegate = self;
    }
    
    @end
    複製代碼

    上面的代碼中就出現了ViewController持有了SubObj,同時由於Suobj的delegate是強引用的,因此持有了ViewController,出現了循環引用。

  • delegate主要是事件拋到給代理來作。dataSource主要是數據來源。

  • 通常狀況下,簡單功能的回調用block,系列函數的回調選擇delegate。

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

  • 屬性的實質就是變量+get方法+set方法
  • 屬性的關鍵字有:
    • 原子性nonatomic,atomic
    • 讀寫權限readonly,readwrite
    • 指定讀寫方法getter,setter
    • 持有方式strong,assign,weak,copy,unsafe_unretained
    • 是否能夠爲空nullable,nonnull,null_resettable,null_unspecified
    • 類屬性class
  • @synthesize 表示由系統自動生成get和set方法,若是本身實現了get或者set方法則會替換掉系統生成的。@dynamic必須本身提供get和set方法。

5. 屬性的默認關鍵字是什麼

基本數據類型默認關鍵字是 atomic,readwrite,assign

其餘類型默認關鍵字是 atomic,readwrite,strong

6. NSString爲何要用copy關鍵字,若是用strong會有什麼問題?(注意:這裏沒有說用strong就必定不行。使用copy和strong是看狀況而定的)

防止被修改。好比:

@interface ViewController : UIViewController

@property(nonatomic,strong)NSString * text;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString *test =  [NSMutableString stringWithFormat:@"123"];
    self.text = test;
    [test appendString:@"456"];
}

@end
複製代碼

這個時候打印self.text則變成了被修改後的值123456

7. 如何令本身所寫的對象具備拷貝功能?

實現NSCopying協議就讓對象有了拷貝功能。

8. 可變集合類 和 不可變集合類的 copy 和 mutablecopy有什麼區別?若是是集合是內容複製的話,集合裏面的元素也是內容複製麼?

  • 簡單的說,copy會生成一個不可變的對象,mutableCopy會生成一個可變對象

    NSArray *array = @[@1,@2];
    NSArray *array2 = [array copy];//不可變
    NSMutableArray *array3 = [array mutableCopy];//可變
    NSArray *array4 = [array3 copy];//不可變
    NSMutableArray *array5 = [array3 mutableCopy];//可變
    複製代碼
  • 集合裏面的元素並無內容拷貝。仍是原來的對象。

    NSArray<NSMutableString *> *array = @[[NSMutableString stringWithString:@"123"]];
    NSArray<NSMutableString *> *array2 = [array copy];
    NSMutableString *item = array2[0];
    [item appendString:@"456"];
    NSLog(@"%@",array);
    複製代碼

    打印結果是

    (
        123456
    )
    複製代碼

9.爲何IBOutlet修飾的UIView也適用weak關鍵字?

由於view被添加到superView上面後,就被superView持有了。咱們通常在IB裏面的拖的view都是加在了根view或者它的子view上。而根view又被它的controller持有,因此IBOutlet能夠用weak。若是,在IB裏面拖出來的view是一個單獨的view沒有被加到任何其餘view上,則須要用strong

10. nonatomic和atomic的區別?atomic是絕對的線程安全麼?爲何?若是不是,那應該如何實現?

  • nonatomic表示非原子性,不安全,效率高。atomic表示原子性,效率低。

  • atomic不是絕對的線程安全的。

    @property (atomic, assign)    int       intA;
    
    //thread A
    for (int i = 0; i < 10000; i ++) {
        self.intA = self.intA + 1;
        NSLog(@"Thread A: %d\n", self.intA);
    }
    
    //thread B
    for (int i = 0; i < 10000; i ++) {
        self.intA = self.intA + 1;
        NSLog(@"Thread B: %d\n", self.intA);
    }
    複製代碼

    即便我將intA聲明爲atomic,最後的結果也不必定會是20000。緣由就是由於self.intA = self.intA + 1;不是原子操做,雖然intA的getter和setter是原子操做,但當咱們使用intA的時候,整個語句並非原子的,這行賦值的代碼至少包含讀取(load),+1(add),賦值(store)三步操做,當前線程store的時候可能其餘線程已經執行了若干次store了,致使最後的值小於預期值。這種場景咱們也能夠稱之爲多線程不安全。

    @property (atomic, strong) NSString*                 stringA;
    
    //thread A
    for (int i = 0; i < 100000; i ++) {
        if (i % 2 == 0) {
            self.stringA = @"a very long string";
        }
        else {
            self.stringA = @"string";
        }
        NSLog(@"Thread A: %@\n", self.stringA);
    }
    
    //thread B
    for (int i = 0; i < 100000; i ++) {
        if (self.stringA.length >= 10) {
            NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
        }
        NSLog(@"Thread B: %@\n", self.stringA);
    }
    複製代碼

    雖然stringA是atomic的property,並且在取substring的時候作了length判斷,線程B仍是很容易crash,由於在前一刻讀length的時候self.stringA = @"a very long string";,下一刻取substring的時候線程A已經將self.stringA = @"string";,當即出現out of bounds的Exception,crash,多線程不安全。

    這段例子引用於這篇文章

11. UICollectionView自定義layout如何實現?

繼承UICollectionViewLayout本身實現prepareLayout,collectionViewContentSize,layoutAttributesForElementsInRect:這三個方法。

12. 用StoryBoard開發界面有什麼弊端?如何避免?

平日裏開發,複用較多的模塊用的xib來寫。單獨的模塊,如設置界面用的storyboard來開發。

關於StoryBoard的討論能夠參看喵神的這篇文章

13. 進程和線程的區別?同步異步的區別?並行和併發的區別?

進程和線程的區別

進程(process)

狹義的定義:進程就是一段程序的執行過程。

廣義定義:進程是一個具備必定獨立功能的程序關於某次數據集合的一次運行活動,它是操做系統分配資源的基本單元。

簡單來說進程的概念主要有兩點:第一,進程是一個實體。每個進程都有它本身的地址空間,通常狀況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。文本區域存儲處理器執行的代碼;數據區域存儲變量和進程執行期間使用的動態分配的內存;堆棧區域存儲着活動過程當中調用的指令和本地變量。第二,進程是一個「執行中的程序」。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成爲一個活動的實體,咱們稱其爲進程。

進程狀態:進程有三個狀態,就緒,運行和阻塞。就緒狀態其實就是獲取了除cpu外的全部資源,只要處理器分配資源立刻就能夠運行。運行態就是獲取了處理器分配的資源,程序開始執行,阻塞態,當程序條件不夠時,須要等待條件知足時候才能執行,如等待I/O操做的時候,此刻的狀態就叫阻塞態。

說說程序,程序是指令和數據的有序集合,其自己沒有任何運動的含義,是一個靜態的概念,而進程則是在處理機上的一次執行過程,它是一個動態的概念。進程是包含程序的,進程的執行離不開程序,進程中的文本區域就是代碼區,也就是程序。

線程(thread)

一般在一個進程中能夠包含若干個線程,固然一個進程中至少有一個線程,否則沒有存在的意義。線程能夠利用進程所擁有的資源,在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位,因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統多個程序間併發執行的程度。

引用於這篇文章

同步和異步的區別

同步是串行的順序執行,異步是並行的同時執行

並行和併發的區別

併發和並行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不一樣的任務。

前者是邏輯上的同時發生(simultaneous),然後者是物理上的同時發生.

併發性(concurrency),又稱共行性,是指能處理多個同時性活動的能力,併發事件之間不必定要同一時刻發生。

並行(parallelism)是指同時發生的兩個併發事件,具備併發的含義,而併發則不必定並行

14. 線程間通訊?

線程間通訊指的是:一、一個線程傳遞數據給另外一個線程,二、在一個線程中執行完特定任務後,轉到另外一個線程繼續執行任務。

在iOS中能夠用這些方法來進行線程間通訊:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

dispatch_async(otherQueue, ^{
    // dosth
});

複製代碼

15. GCD的一些經常使用的函數?(group,barrier,信號量,線程同步)

  • dispatch_group:作完一組操做後再執行後續的代碼

    它有兩種用法: 一種是dispatch_group_async

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"group one start");
    dispatch_group_async(group, queue, ^{
        //do something
    });
    
    dispatch_group_async(group, queue, ^{
        //do something
    });
    
    dispatch_group_notify(group, queue, ^{
        //do something
    });
    
    複製代碼

    第二種是dispatch_group_enterdispatch_group_leave:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        //do something
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        //do something
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, queue, ^{
        //do something
    });
    
    複製代碼
  • barrier

    • 經過dispatch_barrier_async添加的block會等到以前添加全部的block執行完畢再執行
    • 在dispatch_barrier_async以後添加的block會等到dispatch_barrier_async添加的block執行完畢再執行
    - (void)barrier {
        dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            // dosth1;
        });
        dispatch_async(queue, ^{
            // dosth2;
        });
        dispatch_barrier_async(queue, ^{
            // doBarrier;
        });
        dispatch_async(queue, ^{
            // dosth4;
        });
        dispatch_async(queue, ^{
            // dosth5;
        });
    }
    
    複製代碼
  • dispatch_semaphore

    當咱們多個線程要訪問同一個資源的時候,每每會設置一個信號量,當信號量大於0的時候,新的線程能夠去操做這個資源,操做時信號量-1,操做完後信號量+1,當信號量等於0的時候,必須等待,因此經過控制信號量,咱們能夠控制可以同時進行的併發數。

    信號量有如下3個函數

    dispatch_semaphore_create //建立一個信號量
    dispatch_semaphore_signal //信號量+1
    dispatch_semaphore_wait //等待,直到信號量大於0時,便可操做,同時將信號量-1
    
    複製代碼
    -(void)dispatchSignal{
        //crate的value表示,最多幾個資源可訪問
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);   
        dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
         
        //任務1
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 1");
            sleep(1);
            NSLog(@"complete task 1");
            dispatch_semaphore_signal(semaphore);       
        });
        //任務2
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 2");
            sleep(1);
            NSLog(@"complete task 2");
            dispatch_semaphore_signal(semaphore);       
        });
        //任務3
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task 3");
            sleep(1);
            NSLog(@"complete task 3");
            dispatch_semaphore_signal(semaphore);       
        });   
    }
    複製代碼

    執行結果爲

    run task 1
    run task 2
    complete task 1
    complete task 2
    run task 3
    complete task 3
    複製代碼

    因爲設定的信號值爲2,先執行兩個線程,等執行完一個,纔會繼續執行下一個,保證同一時間執行的線程數不超過2。

    若是咱們把信號量設置成1dispatch_semaphore_create(1),那麼執行結果就會變成順序執行

    run task 1
    complete task 1
    run task 2
    complete task 2
    run task 3
    complete task 3
    複製代碼

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

用鎖,或者把資源的操做放到單一線程中。

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

  • NSUserDefault
  • NSKeyedArchiver
  • CoreData
  • SQLite
  • fmdb
  • realm

18. 說一下AppDelegate的幾個方法?從後臺到前臺調用了哪些方法?第一次啓動調用了哪些方法?從前臺到後臺調用了哪些方法?

// 當應用程序啓動時(不包括已在後臺的狀況下轉到前臺),調用此回調。launchOptions是啓動參數,假如用戶經過點擊push通知啓動的應用,這個參數裏會存儲一些push通知的信息
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;

//應用即將從前臺狀態轉入後臺
- (void)applicationWillResignActive:(UIApplication *)application;
– (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);

//從後臺到前臺調用了:
– (void)applicationWillEnterForeground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;

複製代碼

19. NSCache優於NSDictionary的幾點?

  • NSCache 採用LRU規則,會對超出限制的數據進行自動清除
  • NSCache 在系統內存很低時,會自動釋放一些對象
  • NSCache 是線程安全的,在多線程操做中,不須要對 Cache 加鎖
  • NSCache 的 Key 只是作強引用,不須要實現 NSCopying 協議

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

  • 相似於Swift中的初始化方法。便捷初始化方法必須調用指定初始化方法。
  • 須要注意的是當子類實現了新的指定初始化方法後,須要在子類的指定初始化方法裏面用super調用父類的指定初始化方法,而且子類其餘的初始化方法須要調用到該指定初始化方法。

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

NSLog(@"%@")[NSString stringWithFormat:@"%@"]會轉換成description返回的字符串。

22. objc使用什麼機制管理對象內存?

使用引用計數機制管理對象內存。

相關文章
相關標籤/搜索