iOS面試題總結(持續更新)

過段時間打算跳槽,找了一些面試題來作,在這裏作個總結方便review,但願能對要面試的童鞋有幫助。web

如下爲面試題:面試

  1. 運行如下代碼會有什麼結果

    NSString *str1 = @"str1";
            NSString *str2 = [NSString stringWithFormat:@"str1"];
            NSString *str3 = @"str1";
            NSLog(@"str1 == str2 --- %d", str1 == str2);
            NSLog(@"str1 == str3 --- %d", str1 == str3);
            NSLog(@"str1 isEqualToString str2 --- %d", [str1 isEqualToString:str2]);
            NSLog(@"str1 isEqualToString str3 --- %d", [str1 isEqualToString:str3]);   

     

  第一眼看這道題,只能肯定使用isEqualToString:來比較字符串是比較每個字符,因此isEqualToString確定是true,而在OC裏使用==號用於判斷是否指向同一個地址,那麼問題就來了,使用字面量建立的字符串與調用方法建立的有什麼區別呢?算法

  實踐出真知,老老實實敲代碼,打上斷點來一探究竟數組

  

  能夠看到使用字面量建立的字符串爲常量字符串,而用方法建立的則是指針字符串。常量字符串會在app銷燬後釋放,在app存在期間會一直存在,且相同的常量字符串都指向同一個地址。緩存

  運行結果就以下了安全

  

  2. 如下方式建立的timer有什麼區別

        [NSTimer scheduledTimerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
            //建立一個timer而且在當前的runloop中執行。
        }];
        [NSTimer scheduledTimerWithTimeInterval:1.f repeats:NO block:^(NSTimer * _Nonnull timer) {
            
        }];
     //timer加入到runloop才能fire成功 [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:
2.f] interval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) { }];
      //建立timer必須加入到run loop才能執行 [NSTimer timerWithTimeInterval:
1.f target:self selector:@selector(performTimer:) userInfo:nil repeats:YES]

   我以前對timer的理解就是指定timer啓動的時間、是否重複執行,是否馬上執行或手動fire。再深刻一點就是能夠將timer加入到不一樣的runloop,這樣就能在scrollview滑動的時候也不影響timer執行了。查資料發現,NSTimer在repeats爲YES的狀態下會對target強引用,而且在沒有invalidate的狀況下是不會釋放的,所以使用timer的時候就可能會出現循環引用的狀況。例如,控制器A強引用timer,同時timer的target爲A,這就產生了循環引用,當控制器被pop後,該控制器也不會銷燬,就會形成內存泄漏。因此在使用timer的時候就須要在適當的時機來釋放timer。僅僅是invalidate依然會形成循環引用,只能把timer置爲nil才能夠。拿以前的例子來講,就須要在控制器的生命週期來作這件事,viewWillAppear建立timer,在viewWillDisappear將timer置爲nil。app

  3. 若有需求「一段文字中的指定位置插入一張圖片」,請寫出實現思路。

  能夠利用富文本NSAttributeString與NSTextAttachment來實現,先找到要插入圖片的位置,而後利用NSTextAttachment來包裝圖片,最後用NSTextAttachment來生成NSAttributeString便可。異步

  4. WebView內存管理問題說說本身的經驗和見解

   UIWebView原本就有內存泄漏的問題,只能經過一些手段來減小內存泄漏,並不能徹底的解決,要解決的辦法就是使用WKWebView。優化的方法以下:oop

   (1) 收到內存警告時清除緩存優化

- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
{
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
}

  (2)釋放webView時

self.webView.delegate = nil;
[self.webView loadHTMLString:@"" baseURL:nil];
[self.webView stopLoading];
[self.webView removeFromSuperview];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[self.webView release];

   (3) webViewDidFinishLoad時

[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"WebKitCacheModelPreferenceKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];

  5. RunLoop和線程的關係

  一般狀況下線程在執行完代碼後就會銷燬,RunLoop其實就是事件處理循環,只有當接受到退出事件時纔會退出。在iOS開發中,RunLoop與線程是一一對應的關係,一個RunLoop對應一個線程。須要注意的是隻有主線程會在默認狀態下建立RunLoop,其餘的輔助線程須要本身顯示的調用

[NSRunLoop currentRunLoop]來得到與當前線程綁定的RunLoop(ps:若是RunLoop不存在則建立一個新的)。建立出來的RunLoop須要在其中添加有Timer/Source/Observer或者march port,否則RunLoop會銷燬。

  6. ARC經過什麼方式管理內存

  不僅是ARC,MRC也是利用引用計數來進行內存管理的,只是ARC管理就不須要本身來Release和Retain。系統會在自動釋放池結束時,對沒有強引用的對象統一進行釋放。

  7. 使用Block時,什麼狀況會形成循環引用,如何解決?

  由於block會對全部在block中使用到的對象進行強引用(capture),因此當block被一個對象持有,同時這個對象又在Block中被使用時就會出現強引用。解決方式就是在Block中只用弱引用,代碼以下  

__weak typeof(self)weakSelf = self;
    void (^block)(void) = ^{
        NSLog(@"%@", weakSelf);
    }

  還有一種方式是主動打破循環引用,將調用的block置爲nil

  8.使用synthesize和dynamic分別有什麼做用

  @synthesize的做用:爲Property指定生成要生成的成員變量名,並生成getter和setter方法。用途:對於只讀屬性,若是同時從新setter和getter方法,就須要使用synthesize來手動合成成員變量,代碼以下

@interface Person : NSObject

@property (nonatomic, assign, readonly) NSInteger age;

@end

@implementation Person
@synthesize age = _age;
- (void)setAge:(NSInteger)age
{
    _age = age;
}

- (NSInteger)age
{
    return _age;
}
@end

 

  @dynamic的做用:告訴編譯器,不用爲指定的Property生成getter和setter。使用方式:當咱們在分類中使用Property爲類擴展屬性時,編譯器默認不會爲此property生成getter和setter,這時就須要用dynamic告訴編譯器,本身合成了,代碼以下

  

@interface Person (Extension)

@property (strong, nonatomic) NSString *name;

@end

@implementation Person (Extension)
@dynamic name;

- (void)test
{
    NSLog(@"%@", self.name);
}

@end

  9. runtime如何經過Selector找到對應的IMP地址?(分別考慮類方法和實例方法)

  這就要從類的結構來講了,先來看下面這張官方給出的

  10. initialize和load的區別

  initialize會在Class第一次收到消息時調用,父類會比子類先調用,若是子類沒有從新實現initialize方法,此方法會在子類接受消息時被屢次調用。load方法會在類被加載到運行時環境中時調用,在整個運行時期間都只會調用一次。

  11. 如何申明私有變量和私有方法?以及外部如何調用

  申明私有變量總的來講有3種方式,一種是在@interface中利用@private 關鍵字來申明,第二種方式是在@implementation聲明。私有方法聲明的話,就只可以在@implementation中聲明瞭。訪問私有變量能夠經過提供getter和setter方法,KVO中key使用成員變量名也能夠訪問,調用私有方法只能經過暴露方法,或者是利用runtime,以及NSObject提供的方法performSelector系列的方法。

  12.如下代碼運行結果

@interface Student : Person

@end

@implementation Student

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}

@end

  運行結果都爲Student,緣由是self表示此方法從本身的方法表開始找,super則表示方法先從父類的方法表裏找,由於class方法兩個類都沒有實現,最終方法的都是會NSObject中找到,因此結果都是調用的類名Student。

 14. 用代碼實現一個冒泡算法(明天來)

 15. readwrite,readonly, assign,retain, copy, nonatomic,strong的做用

  readwrite表示此屬性能夠讀寫,會自動生成getter與setter

  readonly表示此屬性只可讀,外部只能方法getter方法

  assign在MRC中用於表示引用計數不用加一,以及用於除類以外的聲明。在ARC中用於除了類以外的聲明

  retain在MRC中表示引用計數加一,在ARC中表示強引用

  copy在MRC中不會影響調用copy方法的對象的引用計數,在ARC中表示在setter方法中會調用傳入對象的copy方法,經常使用於須要不可變對象的屬性NSString、NSArray等

  atomic表示屬性讀寫的原子性,然而並不能保證線程安全

  nonatomic則不保證線程安全

  16. 請寫出UIViewController的生命週期

   init->viewDidLoad->ViewWillAppear->ViewDidappear->viewWillDisappear->viewDidDisappear->dealloc

  17. 請寫出一個單例實現

  利用dispatch_once,其餘的實現方式能夠重寫allocWithZone方法

+ (instancetype)sharedInstance
{
    static Student *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

  18.UIView相關 

 

  19. 當前維護的App的崩潰率是多少?怎麼追蹤並解決的?線上崩潰如何解決的?

  崩潰率這個就不談了,開發過程當中遇到的崩潰問題主要查看崩潰的棧信息,利用異常斷點來解決。線上崩潰的話,就是利用dSYMS文件來符號化蘋果的崩潰日誌來解決了

  20. 什麼是事件響應鏈?當用戶與iPhone的觸屏產生互動時?都發生了什麼?事件是如何傳遞的?

  講響應者鏈條前,須要知道iOS中事件響應是基於UIResponder對象的及子類,包括UIResponder的子類UIView、UIViewController、UIWindow、UIApplication,當iOS App接收到觸摸事件時,UIKit會自動的找到最合適的第一響應者。沒有處理的事件會沿着當前激活狀態的響應者鏈條傳遞下去。

  以下圖所示,這是app中默認的事件響應鏈條,若是在UILabel上觸發了爲處理的事件,那麼這個事件會傳遞給label的父視圖UIView,而後是UIWindow對象。對於根視圖而言,事件會先傳遞給UIViewController,而後纔是window。若是UIWindow也沒有處理,那就會傳遞給UIApplication,application也未處理的話,若是application的代理對象是UIResponder子類而且沒有出如今以前的響應者鏈條中。

  當用戶觸摸屏幕時,UIKit會根據默認規則來找到第一事件響應者,此規則是基於hit-testing來決定的。UIKit會在touch發生的view的視圖層級中比較touch location與View 的bounds。 hitTest:withEvent:方法會遍歷整個視圖層級找到最深層次的包含這次觸摸的子視圖,這個子視圖就會是第一事件響應者。

  

  21.RunLoop是什麼? 使用RunLoop的目的是什麼?什麼時候使用?使用要注意些什麼?

  RunLoop是一個事件處理的循環,這個循環會不停的從一個地方收到事件,收到事件就作相應的處理,只有收到退出事件時,這個循環纔會退出。

  在iOS的開發中,主線程的RunLoop會自動建立,輔助線程的RunLoop只有在主動獲取時纔會被建立。

  RunLoop由RunLoopMode構成,RunLoopMode又由Timer/Source/Observer構成。RunLoop同時只可以運行在一個Mode下,app主線程的 RunLoop 裏有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。當TableView處於滑動過程當中時,RunLoop的mode爲UITrackingRunLoopMode,其他時間Mode爲:kCFRunLoopDefaultMode。

  在app開發中默認用到RunLoop的地方有:NSTimer、NSObject提供的performSelectorOnMainThread:withObject:waitUntilDone:方法。當咱們使用NSTimer在指定時間執行時,實際上是在RunLoop中添加這個timer,並在到達指定的時間點後執行回調。NSObject執行perform等方法的時候一樣是在指定時間來執行那個回調,只是在執行perform方法是當前線程必需要存在RunLoop才行,否則無效。

  系統中使用RunLoop的地方有AutoReleasePool,RunLoop會在每一次進入RunLoop時建立自動釋放池,而後在RunLoop進入waiting狀態時釋放舊的釋放池並從新建立自動釋放池,最後在ExitRunLoop時釋放自動釋放池。

  在平常的iOS開發中,默認建立的Timer是添加在kCFRunLoopDefaultMode模式下的,因此在滑動TableView時,RunLoop會切換爲UITrackingRunLoopMode,timer會被暫停。要想Timer可以在UITrackingRunLoopMode下正常運行有兩種辦法,一是將Timer分別添加到以上兩種Mode中,二是將 Timer 加入到頂層的 RunLoop 的 「commonModeItems」 中。」commonModeItems」 被 RunLoop 自動更新到全部具備」Common」屬性的 Mode 裏去。

  22.說說你對線程和進程的理解?

  總的來講,進程是程序分配資源的最小單元,線程是程序運行的最小單元,進程能夠有多個線程組成。

  23.對大量數據列表有什麼優化方案?

   優化1. 利用UITableView重用cell的機制

   優化2. 分批次異步加載數據

   優化3. 緩存高度

   優化4. 將耗時操做放在異步線程來作

  24.平時工做中使用的動畫庫有哪些?

  faceBook的pop動畫、

  25.objc實現多重繼承

  oc不支持多重繼承,只支持多層繼承。要變相的實現多重繼承,能夠利用protocol來實現

  26.數組查找平衡點

  

int findBalancePoint(int a[], int n)
{
    if (n == 1) {
        return -1;
    }
    int leftSum = 0;
    int rightSum = 0;
    for (int i = 0, j = n - 1; ; i++, j--) {
        leftSum += a[i];
        rightSum += a[j];
        if (i < j) {//繼續加
            continue;
        }else{
            if (leftSum == rightSum) {//相等
                if (i == j) {//奇數個
                    return i + 1;
                }else{//偶數個
                    return n;
                }
            }else{//不等
                return -1;
            }
        }
    }
    return -1;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int a[] = {-7, 1, 5, 2, -4, 3, 0};
        int b[] = {1, 2, 3, 3, 2, 1};
        
        int found1 = findBalancePoint(a, 7);
        int found2 = findBalancePoint(b, 6);
        NSLog(@"%i %i", found1, found2);
        
    }
    return 0;
}
相關文章
相關標籤/搜索