設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡要敘述?
設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的事情。
- MVC模式:Model View Control,把模型 視圖 控制器 層進(jìn)行解耦合編寫。
- MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務(wù)邏輯 層進(jìn)行解耦和編寫。
- 單例模式:通過static關(guān)鍵詞,聲明全局變量。在整個(gè)進(jìn)程運(yùn)行期間只會(huì)被賦值一次。
- 觀察者模式:KVO是典型的通知模式,觀察某個(gè)屬性的狀態(tài),狀態(tài)發(fā)生變化時(shí)通知觀察者。
- 委托模式:代理+協(xié)議的組合。實(shí)現(xiàn)1對1的反向傳值操作。
- 工廠模式:通過一個(gè)類方法,批量的根據(jù)已有模板生產(chǎn)對象。
MVC 和 MVVM 的區(qū)別
- MVVM是對胖模型進(jìn)行的拆分,其本質(zhì)是給控制器減負(fù),將一些弱業(yè)務(wù)邏輯放到VM中去處理。
- MVC是一切設(shè)計(jì)的基礎(chǔ),所有新的設(shè)計(jì)模式都是基于MVC進(jìn)行的改進(jìn)。
frame 和 bounds 有什么不同?
frame指的是:該view在父view坐標(biāo)系統(tǒng)中的位置和大小。(參照點(diǎn)是父view的坐標(biāo)系統(tǒng))
bounds指的是:該view在本身坐標(biāo)系統(tǒng)中的位置和大小。(參照點(diǎn)是本身坐標(biāo)系統(tǒng))
Objective-C 中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么?
線程創(chuàng)建有三種方法:
- 使用NSThread創(chuàng)建
- 使用GCD的dispatch
- 使用子類化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼,方法是performSelectorOnMainThread,如果想延時(shí)執(zhí)行代碼可以performSelector:onThread:withObject:waitUntilDone:
Category(類別)、 Extension(擴(kuò)展)和繼承的區(qū)別
區(qū)別:
- 分類有名字,類擴(kuò)展沒有分類名字,是一種特殊的分類。
- 分類只能擴(kuò)展方法(屬性僅僅是聲明,并沒真正實(shí)現(xiàn)),類擴(kuò)展可以擴(kuò)展屬性、成員變量和方法。
- 繼承可以增加,修改或者刪除方法,并且可以增加屬性。
如何對iOS設(shè)備進(jìn)行性能測試?
- Profile-> Instruments ->Time Profiler
開發(fā)項(xiàng)目時(shí)你是怎么檢查內(nèi)存泄露?
- 靜態(tài)分析 analyze。
- instruments工具里面有個(gè)leak可以動(dòng)態(tài)分析。
什么是block?
閉包(block):閉包就是獲取其它函數(shù)局部變量的匿名函數(shù)。
- block反向傳值
- 在控制器間傳值可以使用代理或者block,使用block相對來說簡潔。
block的注意點(diǎn)
- 在block內(nèi)部使用外部指針且會(huì)造成循環(huán)引用情況下,需要用__week修飾外部指針: __weak typeof(self) weakSelf = self;
- 在block內(nèi)部如果調(diào)用了延時(shí)函數(shù)還使用弱指針會(huì)取不到該指針,因?yàn)橐呀?jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強(qiáng)引用一下。 __strong typeof(self) strongSelf = weakSelf;
- 如果需要在block內(nèi)部改變外部棧區(qū)變量的話,需要在用__block修飾外部變量。
你一般是怎么用Instruments的?
- Time Profiler: 性能分析
- Zombies:檢查是否訪問了僵尸對象,但是這個(gè)工具只能從上往下檢查,不智能。
- Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個(gè)來檢查。
- Leaks:檢查內(nèi)存,看是否有內(nèi)存泄露。
iOS中常用的數(shù)據(jù)存儲(chǔ)方式有哪些?
數(shù)據(jù)存儲(chǔ)有四種方案:NSUserDefault、KeyChain、file、DB。
其中File有三種方式:plist、Archive(歸檔)
DB包括:SQLite、FMDB、CoreData
GCD 與 NSOperation 的區(qū)別:
GCD 和 NSOperation 都是用于實(shí)現(xiàn)多線程
GCD 基于C語言的底層API,GCD主要與block結(jié)合使用,代碼簡潔高效。
NSOperation 屬于Objective-C類,是基于GCD更高一層的封裝。復(fù)雜任務(wù)一般用NSOperation實(shí)現(xiàn)。
寫出使用GCD方式從子線程回到主線程的方法代碼
dispatch_sync(dispatch_get_main_queue(), ^{ });
如何用GCD同步若干個(gè)異步調(diào)用?(如根據(jù)若干個(gè)url異步加載多張圖片,然后在都下載完成后合成一張整圖)
// 使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,就會(huì)執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。
// 創(chuàng)建隊(duì)列組 dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
// 當(dāng)并發(fā)隊(duì)列組中的任務(wù)執(zhí)行完畢后才會(huì)執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 合并圖片 });
什么是 RunLoop
從字面上講就是運(yùn)行循環(huán),它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)。
一個(gè)線程對應(yīng)一個(gè)RunLoop,基本作用就是保持程序的持續(xù)運(yùn)行,處理app中的各種事件。通過runloop,有事運(yùn)行,沒事就休息,可以節(jié)省cpu資源,提高程序性能。
什么是 Runtime
Runtime又叫運(yùn)行時(shí),是一套底層的C語言API,其為iOS內(nèi)部的核心之一,我們平時(shí)編寫的OC代碼,底層都是基于它來實(shí)現(xiàn)的。
Runtime實(shí)現(xiàn)的機(jī)制是什么,怎么用,一般用于干嘛?
- 使用時(shí)需要導(dǎo)入的頭文件 <objc/message.h> <objc/runtime.h>
- Runtime 運(yùn)行時(shí)機(jī)制,它是一套C語言庫。
- 實(shí)際上我們編寫的所有OC代碼,最終都是轉(zhuǎn)成了runtime庫的東西。
比如:
類轉(zhuǎn)成了 Runtime 庫里面的結(jié)構(gòu)體等數(shù)據(jù)類型
方法轉(zhuǎn)成了 Runtime 庫里面的C語言函數(shù)
平時(shí)調(diào)方法都是轉(zhuǎn)成了 objc_msgSend 函數(shù)(所以說OC有個(gè)消息發(fā)送機(jī)制)
OC是動(dòng)態(tài)語言,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。
[stu show]; 在objc動(dòng)態(tài)編譯時(shí),會(huì)被轉(zhuǎn)意為:objc_msgSend(stu, @selector(show));
- 因此,可以說 Runtime 是OC的底層實(shí)現(xiàn),是OC的幕后執(zhí)行者。
有了Runtime庫,能做什么事情呢?
Runtime庫里面包含了跟類、成員變量、方法相關(guān)的API。
比如:
- 獲取類里面的所有成員變量。
- 為類動(dòng)態(tài)添加成員變量。
- 動(dòng)態(tài)改變類的方法實(shí)現(xiàn)。
- 為類動(dòng)態(tài)添加新的方法等。
因此,有了Runtime,想怎么改就怎么改。
什么是 TCP / UDP ?
- TCP:傳輸控制協(xié)議。
TCP 是面向連接的,建立連接需要經(jīng)歷三次握手,是可靠的傳輸層協(xié)議。 - UDP:用戶數(shù)據(jù)協(xié)議。
UDP 是面向無連接的,數(shù)據(jù)傳輸是不可靠的,它只管發(fā),不管收不收得到。 簡單的說,TCP注重?cái)?shù)據(jù)安全,而UDP數(shù)據(jù)傳輸快點(diǎn),但安全性一般。
tableView的重用機(jī)制?
UITableView 通過重用單元格來達(dá)到節(jié)省內(nèi)存的目的: 通過為每個(gè)單元格指定一個(gè)重用標(biāo)識(shí)符,即指定了單元格的種類,當(dāng)屏幕上的單元格滑出屏幕時(shí),系統(tǒng)會(huì)把這個(gè)單元格添加到重用隊(duì)列中,等待被重用,當(dāng)有新單元格從屏幕外滑入屏幕內(nèi)時(shí),從重用隊(duì)列中找看有沒有可以重用的單元格,如果有,就拿過來用,如果沒有就創(chuàng)建一個(gè)來使用。
你是怎么封裝一個(gè)view的
- 可以通過純代碼或者xib的方式來封裝子控件
- 建立一個(gè)跟view相關(guān)的模型,然后將模型數(shù)據(jù)傳給view,通過模型上的數(shù)據(jù)給view的子控件賦值
/** * 純代碼初始化控件時(shí)一定會(huì)走這個(gè)方法 */
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
[self setupUI];
}
return self;
}
/** * 通過xib初始化控件時(shí)一定會(huì)走這個(gè)方法 */
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[self setupUI];
}
return self;
}
- (void)setupUI {
// 初始化代碼
}
第三方框架
AFNetworking 底層原理分析
AFNetworking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類:
- AFHTTPRequestOperationManager:內(nèi)部封裝的是 NSURLConnection, 負(fù)責(zé)發(fā)送網(wǎng)絡(luò)請求, 使用最多的一個(gè)類。(3.0廢棄)
- AFHTTPSessionManager:內(nèi)部封裝是 NSURLSession, 負(fù)責(zé)發(fā)送網(wǎng)絡(luò)請求,使用最多的一個(gè)類。
- AFNetworkReachabilityManager:實(shí)時(shí)監(jiān)測網(wǎng)絡(luò)狀態(tài)的工具類。當(dāng)前的網(wǎng)絡(luò)環(huán)境發(fā)生改變之后,這個(gè)工具類就可以檢測到
- AFSecurityPolicy:網(wǎng)絡(luò)安全的工具類, 主要是針對 HTTPS 服務(wù)。
- AFURLRequestSerialization:序列化工具類,基類。上傳的數(shù)據(jù)轉(zhuǎn)換成JSON格式 (AFJSONRequestSerializer).使用不多。
- AFURLResponseSerialization:反序列化工具類;基類.使用比較多
- AFJSONResponseSerializer; JSON解析器,默認(rèn)的解析器.
- AFHTTPResponseSerializer; 萬能解析器; JSON和XML之外的數(shù)據(jù)類型,直接返回二進(jìn) 制數(shù)據(jù).對服務(wù)器返回的數(shù)據(jù)不做任何處理.
- AFXMLParserResponseSerializer; XML解析器;
描述下SDWebImage里面給UIImageView加載圖片的邏輯
SDWebImage 中為 UIImageView 提供了UIImageView+WebCache.h,
這個(gè)分類中有一個(gè)最常用的接口
sd_setImageWithURL:placeholderImage:會(huì)在真實(shí)圖片出現(xiàn)前會(huì)先顯示占位圖片,當(dāng)真實(shí)圖片被加載出來后再替換占位圖片。
加載圖片的過程大致如下:
- 首先會(huì)在 SDWebImageCache 中尋找圖片是否有對應(yīng)的緩存, 它會(huì)以url 作為數(shù)據(jù)的索引先在內(nèi)存中尋找是否有對應(yīng)的緩存
- 如果緩存未找到就會(huì)利用通過MD5處理過的key來繼續(xù)在磁盤中查詢對應(yīng)的數(shù)據(jù), 如果找到了, 就會(huì)把磁盤中的數(shù)據(jù)加載到內(nèi)存中,并將圖片顯示出來
- 如果在內(nèi)存和磁盤緩存中都沒有找到,就會(huì)向遠(yuǎn)程服務(wù)器發(fā)送請求,開始下載圖片
- 下載后的圖片會(huì)加入緩存中,并寫入磁盤中
- 整個(gè)獲取圖片的過程都是在子線程中執(zhí)行,獲取到圖片后回到主線程將圖片顯示出來
SDWebImage原理: 調(diào)用類別的方法: 1. 從內(nèi)存(字典)中找圖片(當(dāng)這個(gè)圖片在本次使用程序的過程中已經(jīng)被加載過),找到直接使用。 2. 從沙盒中找(當(dāng)這個(gè)圖片在之前使用程序的過程中被加載過),找到使用,緩存到內(nèi)存中。 3. 從網(wǎng)絡(luò)上獲取,使用,緩存到內(nèi)存,緩存到沙盒。
編碼格式(優(yōu)化細(xì)節(jié))
在 Objective-C 中,enum 建議使用 NS_ENUM 和 NS_OPTIONS 宏來定義枚舉類型。
//定義一個(gè)枚舉(比較嚴(yán)密)
typedef NS_ENUM(NSInteger, BRUserGender) {
BRUserGenderUnknown, // 未知
BRUserGenderMale, // 男性
BRUserGenderFemale, // 女性
BRUserGenderNeuter // 無性
};
@interface BRUser : NSObject<NSCopying>
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) BRUserGender gender;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;
@end
避免使用C語言中的基本數(shù)據(jù)類型,建議使用 Foundation 數(shù)據(jù)類型
對應(yīng)關(guān)系如下:
int -> NSInteger unsigned -> NSUInteger float -> CGFloat 動(dòng)畫時(shí)間 -> NSTimeInterval
如何實(shí)行cell的動(dòng)態(tài)的行高
如果希望每條數(shù)據(jù)顯示自身的行高,必須設(shè)置兩個(gè)屬性,
- 預(yù)估行高
設(shè)置預(yù)估行高 tableView.estimatedRowHeight = 200。 - 自定義行高
設(shè)置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
如果要讓自定義行高有效,必須讓容器視圖有一個(gè)自下而上的約束。
用StoryBoard開發(fā)界面有什么弊端?如何避免?
使用簡單邏輯頁面的跳轉(zhuǎn)是可以使用sb的,開發(fā)比較塊。但是SB對于邏輯項(xiàng)目比較復(fù)雜的時(shí)候,開發(fā)起來比較慢。
不適合多人合作開發(fā);也不利于版本的梗系和后期的維護(hù)。
使用sb在項(xiàng)目變異編譯的時(shí)候,也都會(huì)直接加載到內(nèi)存中,造成內(nèi)存的浪費(fèi)。 可以使用xib來代替,編輯復(fù)雜邏輯界面時(shí)候可以使用純碼編寫。
Swift
類(class)和結(jié)構(gòu)體(struct)有什么區(qū)別?
Swift 中,類是引用類型,結(jié)構(gòu)體是值類型。值類型在傳遞和賦值時(shí)將進(jìn)行復(fù)制,而引用類型則只會(huì)使用引用對象的一個(gè)"指向"。所以他們兩者之間的區(qū)別就是兩個(gè)類型的區(qū)別。
請說明并比較以下關(guān)鍵詞:Open, Public, Internal, File-private, Private
Swift 有五個(gè)級別的訪問控制權(quán)限,從高到底依次為比如 Open, Public, Internal, File-private, Private。
基本原則:高級別的變量不允許被定義為低級別變量的成員變量。
比如一個(gè) private 的 class 中不能含有 public 的 String。反之,低級別的變量卻可以定義在高級別的變量中。比如 public 的 class 中可以含有 private 的 Int。
- Open 具備最高的訪問權(quán)限。其修飾的類和方法可以在任意 Module 中被訪問和重寫;它是 Swift 3 中新添加的訪問權(quán)限。
- Public 的權(quán)限僅次于 Open。與 Open 唯一的區(qū)別在于它修飾的對象可以在任意 Module 中被訪問,但不能重寫。
- Internal 是默認(rèn)的權(quán)限。它表示只能在當(dāng)前定義的 Module 中訪問和重寫,它可以被一個(gè) Module 中的多個(gè)文件訪問,但不可以被其他的 Module 中被訪問。
- File-private 也是 Swift 3 新添加的權(quán)限。其被修飾的對象只能在當(dāng)前文件中被使用。例如它可以被一個(gè)文件中的 class,extension,struct 共同使用。
- Private 是最低的訪問權(quán)限。它的對象只能在定義的作用域內(nèi)使用。離開了這個(gè)作用域,即使是同一個(gè)文件中的其他作用域,也無法訪問。
請說明并比較以下關(guān)鍵詞:strong, weak, unowned
Swift 的內(nèi)存管理機(jī)制與 Objective-C一樣為 ARC(Automatic Reference Counting)。
它的基本原理是,一個(gè)對象在沒有任何強(qiáng)引用指向它時(shí),其占用的內(nèi)存會(huì)被回收。反之,只要有任何一個(gè)強(qiáng)引用指向該對象,它就會(huì)一直存在于內(nèi)存中。
- strong 代表著強(qiáng)引用,是默認(rèn)屬性。
當(dāng)一個(gè)對象被聲明為 strong 時(shí),就表示父層級對該對象有一個(gè)強(qiáng)引用的指向。此時(shí)該對象的引用計(jì)數(shù)會(huì)增加1。 - weak 代表著弱引用。
當(dāng)對象被聲明為 weak 時(shí),父層級對此對象沒有指向,該對象的引用計(jì)數(shù)不會(huì)增加1。它在對象釋放后弱引用也隨即消失。繼續(xù)訪問該對象,程序會(huì)得到 nil,不虧崩潰 - unowned 與弱引用本質(zhì)上一樣。
唯一不同的是,對象在釋放后,依然有一個(gè)無效的引用指向?qū)ο?,它不?Optional 也不指向 nil。如果繼續(xù)訪問該對象,程序就會(huì)崩潰。
加分回答:
weak 和 unowned 的引入是為了解決由 strong 帶來的循環(huán)引用問題。
簡單來說,就是當(dāng)兩個(gè)對象互相有一個(gè)強(qiáng)指向去指向?qū)Ψ?,這樣導(dǎo)致兩個(gè)對象在內(nèi)存中無法釋放。
weak 和 unowned 的使用場景有如下差別:
- 當(dāng)訪問對象時(shí)該對象可能已經(jīng)被釋放了,則用 weak。比如 delegate 的修飾。
- 當(dāng)訪問對象確定不可能被釋放,則用 unowned。比如 self 的引用。
- 實(shí)際上為了安全起見,很多公司規(guī)定任何時(shí)候都使用 weak 去修飾。
在Swift和Objective-C的混編項(xiàng)目中,如何在Swift文件中調(diào)用Objective-C文件中已經(jīng)定義的方法?如何在Objective-C文件中調(diào)用Swift文件中定義的方法?
- Swift中若要使用Objective-C代碼,可以在ProjectName-Bridging-Header.h里添加Objective-C的頭文件名稱,Swift文件中即可調(diào)用相應(yīng)的Objective-C代碼。
一般情況Xcode會(huì)在Swift項(xiàng)目中第一次創(chuàng)建Objective-C文件時(shí)自動(dòng)創(chuàng)建ProjectName-Bridging-Header.h文件。 - Objective-C中若要調(diào)用Swift代碼,可以導(dǎo)入Swift生成的頭函數(shù)ProjectName-Swift.h來實(shí)現(xiàn)。
- Swift文件中若要規(guī)定固定的方法或?qū)傩员┞督oObjective-C使用,可以在方法或?qū)傩郧凹由螥objc來聲明。如果該類是NSObject子類,那么Swift會(huì)在非private的方法或?qū)傩郧白詣?dòng)加上@objc。
實(shí)現(xiàn)一個(gè)函數(shù)。求一個(gè)整型二維數(shù)組中所有元素之和
func sumPairs(_ nums: [[Int]]) -> Int {
return nums.flatMap { $0 }.reduce(0) { $0 + $1 }
}
Swift 有函數(shù)式編程的思想。其中 flatMap, map, reduce, filter 是其代表的方法。
本題中考察了 flatMap 的降維思路,以及 reduce 的基本使用。相比于一般的 for 循環(huán),這樣的寫法要更加得簡潔漂亮。
項(xiàng)目
- 有已經(jīng)上線的項(xiàng)目么?
- 項(xiàng)目里哪個(gè)部分是你完成的?(找一個(gè)亮點(diǎn)問一下如何實(shí)現(xiàn)的)
- 開發(fā)過程中遇到過什么困難,是如何解決的?
學(xué)習(xí)
- 遇到一個(gè)問題完全不能理解的時(shí)候,是如何幫助自己理解的?舉個(gè)例子?
- 有看書的習(xí)慣么?最近看的一本是什么書?有什么心得?
- 有沒有使用一些筆記軟件?會(huì)在多平臺(tái)同步以及多渠道采集么?(如果沒有,問一下是如何復(fù)習(xí)知識(shí)的)
- 有沒有使用清單類,日歷類的軟件?(如果沒有,問一下是如何安排,計(jì)劃任務(wù)的)
- 平??床┛兔??有沒有自己寫過?(如果寫,有哪些收獲?如果沒有寫,問一下不寫的原因)