備戰(zhàn)2020——iOS面試之筆試題

作者:flowerflower
推薦閱讀:備戰(zhàn)2020——iOS全新面試題總結(jié)

目錄
1、設(shè)計模式是什么? 你知道哪些設(shè)計模式,并簡要敘述?
2、MVC 和 MVVM 的區(qū)別?
3、#import跟 #include 有什么區(qū)別,@class呢,#import<> 跟 #import“”有什么區(qū)別?
4、frame 和 bounds 有什么不同?
5、Objective-C的類可以多重繼承么?沒有的話用什么代替?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?
6、@property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個類中的?
7、@property中有哪些屬性關(guān)鍵字以及作用?
8、delegate 和 notification 的區(qū)別
9、什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?
10、self.跟self->什么區(qū)別?
11、用@property聲明的 NSString / NSArray / NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問題?
12、淺拷貝和深拷貝的區(qū)別?
13、這個寫法會出什么問題:@property (nonatomic, copy) NSMutableArray *arr;?
14、一個objc對象的isa的指針指向什么?有什么作用?
15、Objective-C 如何對內(nèi)存管理的,說說你的看法和解決方法?
16、iOS UIViewController的完整生命周期?
17、以下代碼運行結(jié)果如何?
18、Object-C有私有方法嗎?私有變量呢?
19、關(guān)鍵字const什么含義?
20、用偽代碼寫一個線程安全的單例模式
21、category 和 extension 的區(qū)別
22、tableView的重用機制?
23、iOS 內(nèi)存的使用和優(yōu)化的注意事項?
24、iOS 你在項目中是怎么優(yōu)化內(nèi)存的?
25、寫一個完整的代理,包括聲明、實現(xiàn)
26、iOS 你在項目中用過 GCD 嗎?舉個例子
27、GCD 與 NSOperation 的區(qū)別
28、寫出使用GCD方式從子線程回到主線程的方法代碼
29、OC中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?
30、你是怎么封裝一個view的(有梗和故事)

作為一個開發(fā)者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群:761407670 進群密碼123,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經(jīng)驗,討論技術(shù), 大家一起交流學習成長!

另附上一份各好友收集的大廠面試題,進群可自行下載!

1、設(shè)計模式是什么? 你知道哪些設(shè)計模式,并簡要敘述?

設(shè)計模式是一套被 反復使用、多數(shù)人知曉、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)。

單例模式:單例模式確保某一個類只有一個實例,并提供一個訪問它的全劇訪問點。具體的詳情可點擊進入查看
工廠模式:工廠父類負責定義創(chuàng)建產(chǎn)品對象的公共接口,而工廠子類則負責生產(chǎn)具體的產(chǎn)品對象,即通過不停的工廠子類來創(chuàng)建不同的產(chǎn)品對象。具體的詳情可點擊進入查看
代理模式 :為某個對象提供一個代理,并由這個代理對象控制對原對象的訪問。具體的詳情可點擊進入查看
適配器模式: 將一個接口轉(zhuǎn)換成客戶希望的另一個接口,使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。適配器模式的別名是包裝器模式(Wrapper),是一種結(jié)構(gòu)型設(shè)計模式。具體的詳情可點擊進入查看
裝飾者模式: 不改變原有對象的前提下,動態(tài)地給一個對象增加一些額外的功能。具體的詳情可點擊進入查看


2、MVC 和 MVVM 的區(qū)別?

MVC
MVC(Model-View-Controller)模式結(jié)構(gòu)圖,可分為三部分:模型(Model)、視圖(View)、控制器(Controller)。其在MVC模式中所扮演的角色分別為:
Model:模型管理應用程序的數(shù)據(jù),響應有關(guān)其狀態(tài)信息(通常來自View)的請求,并響應指令以更改狀態(tài)(通常來自Controller)。
View:視圖管理數(shù)據(jù)的展示。
Controller:控制器解釋用戶的輸入,并通知模型、視圖進行狀態(tài)更新
所有通信都是單向的。
優(yōu)點:對Controller進行瘦身,將View內(nèi)部的細節(jié)封裝起來了,外界不知道View內(nèi)部的具體實現(xiàn)
缺點:View和Controller依賴于Model

MVVM
MVVM(Model View View-Model)就是為了解決過于臃腫的問題。MVVM的思想是將Controller中UI控制邏輯與業(yè)務邏輯進行分離,并抽離出一個View-Model來完成UI控制的邏輯。而Controller只需要負責業(yè)務邏輯即可

唯一的區(qū)別是,View-Model可以調(diào)用Model定義的方法,從Model中獲取數(shù)據(jù)以用于View,并對數(shù)據(jù)進行預處理,使View可以直接使用。View又可以向View-Model發(fā)出用戶的操作命令,從而更改Model。MVVM實現(xiàn)了一種雙向綁定機制。

優(yōu)點:降低了View和Model之間的耦合;分離了業(yè)務邏輯和視圖邏輯。
缺點:View和Model雙向綁定導致bug難以定位,兩者中的任何一方出現(xiàn)問題,另一方也會出現(xiàn)問題;增加了膠水代碼。

3、#import跟 #include 有什么區(qū)別,@class呢,#import<> 跟 #import“”有什么區(qū)別?

1>. #import是Objective-C導入頭文件的關(guān)鍵字,#include是C/C++導入頭文件的關(guān)鍵字,使用#import頭文件會自動只導入一次,不會重復導入。
2>.@class告訴編譯器某個類的聲明,當執(zhí)行時,才去查看類的實現(xiàn)文件,可以解決頭文件的相互包含。
3>. #import<>用來包含系統(tǒng)的頭文件,#import””用來包含用戶頭文件。


4、frame 和 bounds 有什么不同?

frame:該view在父view坐標系統(tǒng)中的位置和大小。(參照點是父view的坐標系統(tǒng))

bounds:該view在本身坐標系統(tǒng)中的位置和大小。(參照點是本身坐標系統(tǒng))


5、Objective-C的類可以多重繼承么?沒有的話用什么代替?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?

OC不可以多繼承,OC是單繼承。有時可以用分類和協(xié)議來代替多繼承
可以實現(xiàn)多個接口(協(xié)議)
Category是類別;一般情況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其他類與原有類的關(guān)系。


6、@property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個類中的?

@property 的本質(zhì)是:@property = ivar + getter + setter

“屬性” (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)
“屬性” (property)作為 Objective-C 的一項特性,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量。實例變量一般通過“存取方法”(access method)來訪問。其中,“獲取方法” (getter)用于讀取變量值,而“設(shè)置方法” (setter)用于寫入變量值。


7、@property中有哪些屬性關(guān)鍵字以及作用?

nonatomic :非原子操作。決定編譯器生成的setter和getter方法是否是原子操作,一般使用nonatomic,效率高。
atomic:多線程安全,但是性能低
strong:持有特性。setter方法將傳入?yún)?shù)先保留,再賦值,傳入?yún)?shù)的retaincount會+1。
copy :拷貝特性。setter方法將傳入對象復制一份,需要完全一份新的變量時。
assign:用于基本數(shù)據(jù)類型
readwrite:可讀可寫特性。需要生成getter方法和setter方法
readonly:只讀特性。只會生成getter方法,不會生成setter方法,不希望屬性在類外改變。
retain:相當于ARC中的strong


8、delegate 和 notification 的區(qū)別

二者都用于傳遞消息,不同之處主要在于一個是一對一的,另一個是一對多的
notification:不需要兩者之間有聯(lián)系,實現(xiàn)一對多消息的轉(zhuǎn)發(fā)
delegate:需要兩者之間必須建立聯(lián)系,不然沒法調(diào)用代理的方法


9、什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?

1>.在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性。
2>.自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak(因為父控件的subViews數(shù)組已經(jīng)對它有一個強引用)。

不同點:
assign 可以用非 OC 對象,而 weak 必須用于 OC 對象。
weak 表明該屬性定義了一種“非擁有關(guān)系”。在屬性所指的對象銷毀時,屬性值會自動清空(nil)。


10、self.跟self->什么區(qū)別?

1>. self.是調(diào)用get方法或者set放
2>. self是當前本身,是一個指向當前對象的指針
3>. self->是直接訪問成員變量


11、用@property聲明的 NSString / NSArray / NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問題?

用 @property 聲明 NSString、NSArray、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因為他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作(就是把可變的賦值給不可變的),為確保對象中的字符串值不會無意間變動,應該在設(shè)置新屬性值時拷貝一份。

1>. 因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本。
2>. 如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性。
總結(jié):使用copy的目的是,防止把可變類型的對象賦值給不可變類型的對象時,可變類型對象的值發(fā)送變化會無意間篡改不可變類型對象原來的值。


12、淺拷貝和深拷貝的區(qū)別?

淺拷貝:對一個對象地址的拷貝。源對象和副本對象是同一對象
深拷貝:對一個對象的拷貝。源對象和副本對象是不同的兩個對象


13、這個寫法會出什么問題:@property (nonatomic, copy) NSMutableArray *arr;?

問題:添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因為找不到對應的方法而崩潰。

-[__NSArrayI removeObjectAtIndex:]: 
unrecognized selector sent to instance 0x7fcd1bc30460

copy后返回的是不可變對象(即 arr 是 NSArray 類型,NSArray 類型對象不能調(diào)用 NSMutableArray 類型對象的方法)
原因: copy 就是復制一個不可變 NSArray 的對象,不能對 NSArray 對象進行添加/修改。


14、一個objc對象的isa的指針指向什么?有什么作用?

指向他的類對象,從而可以找到對象上的方法


15、Objective-C 如何對內(nèi)存管理的,說說你的看法和解決方法?

Objective-C的內(nèi)存管理主要有三種方式ARC(自動內(nèi)存計數(shù))、手動內(nèi)存計數(shù)、內(nèi)存池。
1>. 自動內(nèi)存計數(shù)ARC:由Xcode自動在App編譯階段,在代碼中添加內(nèi)存管理代碼。
2>. 手動內(nèi)存計數(shù)MRC:遵循內(nèi)存誰申請、誰釋放;誰添加,誰釋放的原則。
3>. 內(nèi)存釋放池Release Pool:把需要釋放的內(nèi)存統(tǒng)一放在一個池子中,當池子被抽干后(drain),池子中所有的內(nèi)存空間也被自動釋放掉。內(nèi)存池的釋放操作分為自動和手動。自動釋放受runloop機制影響。


16、iOS UIViewController的完整生命周期?

按照執(zhí)行順序排列:

1>. initWithCoder:通過nib文件初始化時觸發(fā)。
2>. awakeFromNib:nib文件被加載的時候,會發(fā)生一個awakeFromNib的消息到nib文件中的每個對象。
3>. loadView:開始加載視圖控制器自帶的view。
4>. viewDidLoad:視圖控制器的view被加載完成。
5>. viewWillAppear:視圖控制器的view將要顯示在window上。
6>. updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
7>. viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置。
8>. viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置。
9>. viewDidAppear:視圖控制器的view已經(jīng)展示到window上。
10>. viewWillDisappear:視圖控制器的view將要從window上消失。
11>.viewDidDisappear:視圖控制器的view已經(jīng)從window上消失。


17、以下代碼運行結(jié)果如何?
- (void)viewDidLoad {

[super viewDidLoad];

NSLog(@"1");

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"2");
});

NSLog(@"3");
}

只輸出:1。(主線程死鎖,因為viewDidLoad方法默認開了一條主線程,然后又執(zhí)行dispatch_sync(dispatch_get_main_queue(), ^{...});會導致你等我我等你,結(jié)果導致死鎖。


18、Object-C有私有方法嗎?私有變量呢?

1>.OC沒有類似@private的修飾詞來修飾方法,只要寫在.h文件中,就是公共方法
2>. 如果你不在.h文件中聲明,只在.m文件中實現(xiàn),或在.m文件的Class Extension里聲明,那么基本上和私有方法差不多,可以使用類擴展(Extension)來增加私有方法和私有變量
3>. 使用private修飾的全局變量是私有變量


19、關(guān)鍵字const什么含義?

const int a;
int const a;
const int *a;
int const *a;
int * const a;
int const * const a;

1>. 前兩個的作用是一樣:a 是一個常整型數(shù)
2>. 第三、四個意味著 a 是一個指向常整型數(shù)的指針(整型數(shù)是不可修改的,但指針可以)
3>. 第五個的意思:a 是一個指向整型數(shù)的常指針(指針指向的整型數(shù)是可以修改的,但指針是不可修改的)
4>. 最后一個意味著:a 是一個指向常整型數(shù)的常指針(指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)


20、用偽代碼寫一個線程安全的單例模式


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

+ (id)allocWithZone:(struct _NSZone *)zone {

      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
});
return instance;
}
- (id)copyWithZone:(NSZone *)zone {
return instance;
}

21、category(類別) 和 extension(擴展) 的區(qū)別

1>. 類別有名字,類擴展沒有分類名字,是一種特殊的分類。
2>. 類別只能擴展方法(屬性僅僅是聲明,并沒真正實現(xiàn)),類擴展可以擴展屬性、成員變量和方法。
3>. 繼承可以增加,修改或者刪除方法,并且可以增加屬性。


22、tableView的重用機制?

UITableView 通過重用單元格來達到節(jié)省內(nèi)存的目的: 通過為每個單元格指定一個重用標識符,即指定了單元格的種類,當屏幕上的單元格滑出屏幕時,系統(tǒng)會把這個單元格添加到重用隊列中,等待被重用,當有新單元格從屏幕外滑入屏幕內(nèi)時,從重用隊列中找看有沒有可以重用的單元格,如果有,就拿過來用,如果沒有就創(chuàng)建一個來使用


23、iOS 內(nèi)存的使用和優(yōu)化的注意事項?

重用問題:UITableViewCellsUICollectionViewCells、UITableViewHeaderFooterViews。設(shè)置正確的reuseIdentifier,充分重用
1>不要使用太復雜的XIB/Storyboard載入時就會將XIB/storyboard需要的所有資源,包括圖片全部載入內(nèi)存。
盡量把views設(shè)置為不透明:當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結(jié)果,可提高性能
選擇正確的數(shù)據(jù)結(jié)構(gòu):學會選擇對業(yè)務場景最合適的數(shù)組結(jié)構(gòu)是寫出高效代碼的基礎(chǔ)。
gzip/zip壓縮:當從服務端下載相關(guān)附件時,可以通過gzip/zip壓縮后再下載,使得內(nèi)存更小,下載速度也更快。
延遲加載:對于不應該使用的數(shù)據(jù),使用延遲加載方式。對于不需要馬上顯示的視圖,使用延遲加載方式。比如,網(wǎng)絡(luò)請求失敗時顯示的提示界面,可能一直都不會使用到,因此應該使用延遲加載。
數(shù)據(jù)緩存:對于cell的行高要緩存起來,使得reload數(shù)據(jù)時,效率也極高。
而對于那些網(wǎng)絡(luò)數(shù)據(jù),不需要每次都請求的,應該緩存起來??梢詫懭霐?shù)據(jù)庫,也可以通過plist文件存儲
處理內(nèi)存警告:一般在基類統(tǒng)一處理內(nèi)存警告,將相關(guān)不用資源立即釋放掉


24、iOS 你在項目中是怎么優(yōu)化內(nèi)存的?

這個問題有時候筆試中也有,有時候有些面試官會在面試中問你這個問題

1>.避免龐大的Xib(Xib比frame消耗更多的CPU資源)
2>.不要阻塞主線程,盡量把耗時的操作放到子線程
3>.重用和延遲加載
4>.盡量減少視圖數(shù)量和層次
5>.優(yōu)化TableView,為了使TableVIew有更好的滾動性能可采取以下措施:

  • 正確使用ruseIdentifier來重用cells
  • 采用懶加載即延遲加載的方式加載cell上的控件
  • 當TableView滑動的時候不加載
  • 緩存cell的高度。在呈現(xiàn)cell前,把cell的高度計算好緩存起來,避免每次加載cell的時候都要計算
  • 盡量使用不透明的UI控件

25、寫一個完整的代理,包括聲明、實現(xiàn)


// 創(chuàng)建
@protocol PersonDelagate
@required
-(void)eat:(NSString *)foodName;
@optional
-(void)run;
@end

// 聲明 .h
@interface Person: NSObject<PersonDelagate>
@end

// 實現(xiàn) .m
@implementation Person

- (void)eat:(NSString *)foodName {
NSLog(@"吃:%@", foodName);
}

- (void)run {
   NSLog(@"run");
}
@end


26、iOS 你在項目中用過 GCD 嗎?舉個例子

用過。比如 網(wǎng)絡(luò)請求數(shù)據(jù)成功之后刷新列表

   [HTTPRequest POST:kbazaarUrl parameter:nil success:^(id resposeObject) { 
       /**網(wǎng)絡(luò)請求成功之后處理數(shù)據(jù)*/
        if (Success) {
              //處理數(shù)據(jù) ... 
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.tableView reloadData];  
            });
        }else{
            [weakSelf failEndRefreshStatus:0];
            [MBProgressHUD LY_ShowError:resposeObject[@"msg"] time:2.0];
        }
    } failure:^(NSError *error) {
        [MBProgressHUD LY_ShowError:kNoNetworkTips time:2.0];
    }];


27、GCD 與 NSOperation 的區(qū)別

NSOperation:相對于GCD來說,更加強大??梢越ooperation之間添加依賴關(guān)系、取消一個正在執(zhí)行的operation、暫停和恢復operationQueue等

GCD: 是一種更輕量級的,以FIFO(先進先出,后進后出)的順序執(zhí)行并發(fā)任務。使用GCD我們并不用關(guān)心任務的調(diào)度情況,而是系統(tǒng)會自動幫我們處理。但是GCD的短板也是非常明顯的,比如我們想要給任務之間添加依賴關(guān)系、取消或者暫停一個正在執(zhí)行的任務時就會變得束手無策。


28、寫出使用GCD方式從子線程回到主線程的方法代碼

dispatch_sync(dispatch_get_main_queue(), ^{ });

29、OC中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?

// 創(chuàng)建線程的方法

- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]

- [self performSelectorInBackground:nil withObject:nil];

- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];

- dispatch_async(dispatch_get_global_queue(0, 0), ^{});

- [[NSOperationQueue new] addOperation:nil];

// 主線程中執(zhí)行代碼的方法

- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];

- dispatch_async(dispatch_get_main_queue(), ^{});

- [[NSOperationQueue mainQueue] addOperation:nil];

30、你是怎么封裝一個view的

筆者就曾遇到過這個問題。當時這樣,原題目是:怎么用純代碼或者xib實現(xiàn)一個View的組件化。看到這個題目的時候我就懵逼了,就好像考試的過程中碰到不會的題目一樣,這里直接是0分。后來面試的過程中,我就用面試官此題怎解。霹靂嘩啦給我說了一大堆,最后我問是不是封裝一個View?他回答說:是的! 封裝任意一個View.之后就問我實現(xiàn)思路要實現(xiàn)什么方法。然后我就說實現(xiàn)initWIthFrame:然后創(chuàng)建需要的控件,最后直接通過添加個類方法show出來即可。因為我也不知道這個任意的View到底是個什么View,就將TA假象成一個彈框好了。結(jié)果面試官繼續(xù)追問過好幾次還要實現(xiàn)什么方法呢??? 我沉思了好久好一會(明明就差不多是這樣子左右,為啥他還繼續(xù)追問呢)。。。面試官可能感受到了空中傳來一陣尷尬氣氛之后,說不知道沒關(guān)系。。。之后又問了一個類似QQ消息點擊tabBar上面的item然后列表的小紅點有類似西紅柿爆炸的效果。經(jīng)過上一個騷問題,我已經(jīng)知道面試已經(jīng)涼涼了(而且聽說接手的項目是之前廢棄半年的棋牌項目,現(xiàn)在重新啟動) 我直接說不知道。對于此面試官來說,我可能是個菜雞吧。我覺得身心受到了嚴重的打擊??赡苓€是不夠努力吧!

1>. 可以通過純代碼或者xib的方式來封裝子控件
2>. 建立一個跟view相關(guān)的模型,然后將模型數(shù)據(jù)傳給view,通過模型上的數(shù)據(jù)給view的子控件賦值

- (instancetype)initWithFrame:(CGRect)frame {

  if(self = [super initWithFrame:frame]) {

          [self setupUI];
 }
return self;

}
/*** 通過xib初始化控件時一定會走這個方法*/

- (id)initWithCoder:(NSCoder *)aDecoder {

    if(self = [super initWithCoder:aDecoder]) {
        [self setupUI];
}

return self;

}

- (void)setupUI {
// 初始化代碼
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容