題目!!!!

MVC的理解

=>MVC是一種設(shè)計(jì)模式,全程Model(模型)-View(視圖)-Controller(控制器),將界面和業(yè)務(wù)邏輯分開

Model:包含數(shù)據(jù)和業(yè)務(wù)邏輯,View是視圖,即用戶看到的,用來顯示數(shù)據(jù),Controller用來充當(dāng)二者的媒介。

類似的模式還有:MVVM


Delegate與Block的區(qū)別

二者都是回調(diào)的方式,delegate需要實(shí)現(xiàn)protocol

block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存,當(dāng)然對象的話就是加計(jì)數(shù),使用完或者block置nil后才消除。delegate只是保存了一個(gè)對象指針,直接回調(diào),沒有額外消耗。


消息通知的種類

消息通知有兩類:本地通知、推送通知,二者表現(xiàn)方式一樣,可以通過橫幅或彈出形式提醒用戶。


Delegate, Notification,KVO優(yōu)缺點(diǎn)

Delegate:語法嚴(yán)格,編譯器可以進(jìn)行語法檢查并提醒錯(cuò)誤,邏輯明確,容易跟蹤流程,一個(gè)類可以有多個(gè)協(xié)議,每個(gè)協(xié)議可以不同的方法;可以獲得反饋值,只能一對一

缺點(diǎn):代碼多

Notification:輕松實(shí)現(xiàn)一對多傳值

缺點(diǎn):編譯器不明確通知是否會(huì)被處理,注冊的通知對象要移除,通知發(fā)出后不能從觀察者獲得反饋信息

KVO:以上兩種多用于Controller與其他對象的通信,KVO適用于任何對象對另一個(gè)對象的改變,是對象之間的同步。能夠?qū)Ψ俏覀儎?chuàng)造的對象進(jìn)行監(jiān)聽。

缺點(diǎn):只能監(jiān)聽屬性,不能對方法有反應(yīng),屬性必須用string來定義,因此編譯器不會(huì)有提示。

綜上:當(dāng)處理屬性級別的消息時(shí),用KVO,其他用Delegate,如果處理的代碼很簡單,可以用通知。


手動(dòng)觸發(fā)KVO,以及KVO的底層原理

被觀察者發(fā)出? addObserver:forKeyPath:options:context:。

觀察者需要實(shí)現(xiàn)方法 observeValueForKeyPath:ofObject:change:context:

手動(dòng)觸發(fā):[person willChangeValueForKey:@"name"];[person didChangeValueForKey:@"name"];

KVO的底層實(shí)現(xiàn)原理:是用Runtime來實(shí)現(xiàn)的,當(dāng)類對象添加觀察時(shí),會(huì)動(dòng)態(tài)地派生出一個(gè)新類繼承自父類,新類重寫setter方法,并且原來類對象的isa指針(每個(gè)實(shí)例對象有個(gè)isa的指針,他指向?qū)ο蟮念悾?huì)指向新類,當(dāng)類對象改變值時(shí),執(zhí)行的是新類的setter方法,會(huì)有willChangeValueForKey、[super setValue:for:key]、didChangeValueForKey從而observeValueForKey:ofObject:change:context:被調(diào)用通知被觀察對象的值有更改。


Objective-C的Copy在那些場景下使用

NSString,NSArray,NSDictionary屬性會(huì)用copy,都對應(yīng)有可變類型,防止賦值給它的是可變的數(shù)據(jù),如果可變的數(shù)據(jù)發(fā)生了變化,那么該property也會(huì)發(fā)生變化。

block在MRC時(shí)需要使用copy,在ARC與Strong效果一樣。

可變的不能用Copy,比如NSMutableArray,因?yàn)閏opy就是復(fù)制一個(gè)不可變對象,如果增刪改會(huì)因?yàn)檎也坏椒椒ǘ罎ⅰ?/p>


Runtime的消息傳遞

消息傳遞:相較于C語言而言,C語言使用的是“靜態(tài)綁定”,函數(shù)的調(diào)用在編譯期就能知道運(yùn)行期所需要調(diào)用的函數(shù)了,編譯完成之后就按照順序執(zhí)行(面向過程就是這么任性)。而OC使用的是“動(dòng)態(tài)綁定”特性,也就是說編譯器在編譯期的時(shí)候無法決定運(yùn)行期調(diào)用哪個(gè)函數(shù),也就是說在編譯階段,OC可以調(diào)用任何函數(shù),即使該函數(shù)未實(shí)現(xiàn),但只要聲明過就不會(huì)報(bào)錯(cuò),對象在接收到消息之后,究竟該調(diào)用哪個(gè)方法完全由運(yùn)行期決定,甚至可以在程序運(yùn)行時(shí)改變。

id returnValue = [someObject messageName:paramater];

編譯器會(huì)將方法轉(zhuǎn)換成objc_msgSend函數(shù)

id returnValue = objc_msgSend(someObject, @selector(messageName:), paramater);

消息傳遞的過程:當(dāng)給一個(gè)對象發(fā)送消息的時(shí)候,消息傳遞函數(shù)首先根據(jù)對象的isa指針找到類的結(jié)構(gòu),然后在其分發(fā)表中尋找對應(yīng)的selector,如果找到的話就調(diào)用對應(yīng)的實(shí)現(xiàn);如果找不到則會(huì)根據(jù)super class指針去其父類尋找,如果父類還找不到,會(huì)接著去父類的父類中尋找,直到NSObject類為止。為了加速消息傳遞的過程,運(yùn)行時(shí)系統(tǒng)會(huì)緩存用到過的selector,每個(gè)類都有一個(gè)單獨(dú)的cache,它可以緩存繼承或自己定義的方法。在根據(jù)selector名字搜索分發(fā)表之前,消息路由會(huì)首先檢查someObject的類的cache是否已經(jīng)緩存對應(yīng)的selector,如果有的話就直接調(diào)用對應(yīng)的實(shí)現(xiàn)。


Runtime消息轉(zhuǎn)發(fā)

當(dāng)向某個(gè)對象發(fā)送一條消息時(shí),若該對象的方法列表以及它相應(yīng)繼承鏈上的方法列表都無法找到以該消息selector作為key的方法實(shí)現(xiàn)時(shí),則會(huì)觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制。轉(zhuǎn)發(fā)有三次機(jī)會(huì):

1、動(dòng)態(tài)解析:+ (BOOL)resolveInstanceMethod:(SEL)sel;如果在這個(gè)函數(shù)中動(dòng)態(tài)添加方法(class_addMethod),則運(yùn)行這個(gè)方法并且添加到緩存中以便下次調(diào)用,如果沒有則進(jìn)入第二步

2、消息接受對象更改:- (id)forwardingTargetForSelector:(SEL)aSelector;該方法可以返回一個(gè)能處理該選擇子的其他對象,運(yùn)行時(shí)系統(tǒng)會(huì)根據(jù)返回的對象進(jìn)行查找,若找到則跳轉(zhuǎn)到相應(yīng)方法的實(shí)現(xiàn),則消息轉(zhuǎn)發(fā)結(jié)束。如果返回為nil,則進(jìn)入第三步

3、完整的改變:- (void)forwardInvocation:(NSInvocation *)anInvocation;該方法可以改變消息調(diào)用對象,運(yùn)行時(shí)系統(tǒng)根據(jù)所改變的調(diào)用對象,向調(diào)用目標(biāo)方法列表中查詢對應(yīng)方法的實(shí)現(xiàn)并實(shí)現(xiàn)跳轉(zhuǎn),這種方式和第二步的操作非常相似。當(dāng)然你也可以修改方法的selector,亦或者向所調(diào)用方法中追加一個(gè)參數(shù)等來跳轉(zhuǎn)到相關(guān)方法的實(shí)現(xiàn)。如果依舊無法處理,則進(jìn)入第四步

4、無法處理:- (void)doesNotRecognizeSelector:(SEL)aSelector;


load與initialize的區(qū)別

Load:load方法在這個(gè)文件被程序裝載時(shí)調(diào)用。只要是在Compile Sources中出現(xiàn)的文件總是會(huì)被裝載,這與這個(gè)類是否被用到無關(guān),因此load方法總是在main函數(shù)之前調(diào)用。這個(gè)方法會(huì)自動(dòng)調(diào)用父類的load,不需要手動(dòng)實(shí)現(xiàn),比較常用的是Method Swizzle放在load里實(shí)現(xiàn)。

InitializeI:這個(gè)方法在第一次給某個(gè)類發(fā)送消息時(shí)調(diào)用(比如實(shí)例化一個(gè)對象),并且只會(huì)調(diào)用一次。initialize方法實(shí)際上是一種惰性調(diào)用,也就是說如果一個(gè)類一直沒被用到,那它的initialize方法也不會(huì)被調(diào)用,initialize方法主要用來對一些不方便在編譯期初始化的對象進(jìn)行賦值。比如NSMutableArray這種類型的實(shí)例化依賴于runtime的消息發(fā)送,所以顯然無法在編譯器初始化。

load和initialize方法都會(huì)在實(shí)例化對象之前調(diào)用,以main函數(shù)為分水嶺,前者在main函數(shù)之前調(diào)用,后者在之后調(diào)用。這兩個(gè)方法會(huì)被自動(dòng)調(diào)用,不能手動(dòng)調(diào)用它們。load方法通常用來進(jìn)行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量。


isMemberOfClass和isKindOfClass的區(qū)別

-(BOOL) isKindOfClass: classObj判斷是否是這個(gè)類或者這個(gè)類的子類的實(shí)例

-(BOOL) isMemberOfClass: classObj 判斷是否是這個(gè)類的實(shí)例


數(shù)據(jù)持久化方案有哪些?

plist文件(屬性列表):plist文件是將某些特定的類(NSArray;NSMutableArray;NSDictionary;NSData;NSString;NSNumber;NSDate;)通過XML文件的方式保存在目錄中。存儲時(shí)使用writeToFile: atomically:方法。 其中atomically表示是否需要先寫入一個(gè)輔助文件,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫入文件方法,一般都寫YES。讀取時(shí)使用arrayWithContentsOfFile:方法。

preference(偏好設(shè)置):NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的,一般不要在偏好設(shè)置中保存其他數(shù)據(jù)。

如果沒有調(diào)用synchronize方法,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用synchronize方法。

偏好設(shè)置會(huì)將所有數(shù)據(jù)保存到同一個(gè)文件中。即preference目錄下的一個(gè)以此應(yīng)用包名來命名的plist文件。

NSKeyedArchiver(歸檔):遵循NSCoding協(xié)議的對象都可以通過它實(shí)現(xiàn)序列化

[NSKeyedArchiver archiveRootObject:person toFile:file];//歸檔

Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];//解檔

SQLite 3:SQLite3的使用還是比較麻煩的,因?yàn)槎际切ヽ語言的函數(shù),理解起來有些困難。不過在一般開發(fā)過程中,使用的都是第三方開源庫 FMDB,封裝了這些基本的c語言方法,使得我們在使用時(shí)更加容易理解,提高開發(fā)效率。

integer : 整數(shù)

real : 實(shí)數(shù)(浮點(diǎn)數(shù))

text : 文本字符串

blob : 二進(jìn)制數(shù)據(jù),比如文件,圖片之類的

CoreData:SQLite的封裝,即能夠?qū)C對象轉(zhuǎn)化成數(shù)據(jù),保存在SQLite數(shù)據(jù)庫文件中,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成OC對象。在此數(shù)據(jù)操作期間,我們不需要編寫任何SQL語句


ViewController生命周期,詳細(xì)介紹一下loadView

視圖加載

通過Storyboard加載:這是蘋果推薦的方式,也是未來的趨勢。通過這種方式創(chuàng)建UIViewController對象的話,首先生成UIStoryboard類型的對象,然后調(diào)用這個(gè)對象的instantiateViewControllerWithIdentifier:方法

通過Nib文件加載:Nib文件其實(shí)就是xib文件,Storyboard相當(dāng)于是聚合了多個(gè)nib文件,并且添加了對不同的UIViewController之間的segue和relationship的管理。但總的實(shí)現(xiàn)原理非常類似,通過這種方式加載視圖,需要調(diào)用UIViewController類的initWithNibName:bundle:方法

通過loadview方法加載:這就是通過代碼加載。這需要我們在loadView方法中,通過編程創(chuàng)建自己的視圖層次,并且把把根視圖賦值給UIViewController的view屬性。

loadView:在UIViewController對象的view屬性被訪問到且為空的時(shí)候調(diào)用。因此這個(gè)方法在視圖控制器的生命周期內(nèi)可能會(huì)被多次調(diào)用。在創(chuàng)建view的過程中,首先會(huì)根據(jù)nibName去找對應(yīng)的Nib文件然后加載。如果nibName為空,或找不到對應(yīng)的Nib文件,則會(huì)創(chuàng)建一個(gè)空視圖(這種情況一般是純代碼,也就是為什么說代碼構(gòu)建View的時(shí)候,要重寫loadView方法)。注意在重寫loadView方法的時(shí)候,不要調(diào)用父類的方法

-[ViewController initWithCoder:]或-[ViewController initWithNibName:Bundle]:首先從歸檔文件中加載UIViewController對象。即使是純代碼,也會(huì)把nil作為參數(shù)傳給后者。

-[UIView awakeFromNib]:作為第一個(gè)方法的助手,方便處理一些額外的設(shè)置。

-[ViewController loadView]:創(chuàng)建或加載一個(gè)view并把它賦值給UIViewController的view屬性

-[ViewController viewDidLoad]:此時(shí)整個(gè)視圖層次(view hierarchy)已經(jīng)被放到內(nèi)存中,可以移除一些視圖,修改約束,加載數(shù)據(jù)等

-[ViewController viewWillAppear:]:視圖加載完成,并即將顯示在屏幕上,還沒有設(shè)置動(dòng)畫,可以改變當(dāng)前屏幕方向或狀態(tài)欄的風(fēng)格等。

-[ViewController viewWillLayoutSubviews]:即將開始子視圖位置布局

-[ViewController viewDidLayoutSubviews]:用于通知視圖的位置布局已經(jīng)完成

-[ViewController viewDidAppear:]:視圖已經(jīng)展示在屏幕上,可以對視圖做一些關(guān)于展示效果方面的修改。

-[ViewController viewWillDisappear:]:視圖即將消失

-[ViewController viewDidDisappear:]:視圖已經(jīng)消失


Runloop

程序啟動(dòng)時(shí),Runloop隨著主線程的啟動(dòng)而啟動(dòng),每個(gè)線程都有一個(gè)runloop,但是只有主線程的會(huì)自動(dòng)啟動(dòng)。對于輔助線程,我們?nèi)匀恍枰袛嗍欠裥枰獑?dòng)Run Loop。比如我們使用一個(gè)線程去處理一個(gè)預(yù)先定義的長時(shí)間的任務(wù),我們應(yīng)當(dāng)避免啟動(dòng)Run Loop。下面是官方Document提供的使用Run Loop的幾個(gè)場景:

需要使用Port-Based Input Source或者Custom Input Source和其他線程通訊時(shí)

需要在線程中使用Timer

需要在線程中使用上文中提到的selector相關(guān)方法

需要讓線程執(zhí)行周期性的工作

Runloop的本質(zhì)是一直運(yùn)行著的循環(huán),線程中的循環(huán),用來接收循環(huán)中的事件并且安排線程工作,并且在沒有工作的時(shí)候,讓線程進(jìn)入休眠狀態(tài)。

接收事件:

1、Input source用來投遞異步消息,通常消息來自另外的線程或者程序。在接收到消息并調(diào)用程序指定方法時(shí),線程中對應(yīng)的NSRunLoop對象會(huì)通過執(zhí)行runUntilDate:方法來退出。Input source有兩個(gè)不同的種類:Port-Based Sources和Custom Input Sources

2、Timer source用來投遞timer事件(Schedule或者Repeat)中的同步消息。在處理消息時(shí),并不會(huì)退出Run Loop。Timer在選擇使用一次后,在執(zhí)行完成時(shí),會(huì)從Run Loop中移除。選擇循環(huán)時(shí),會(huì)一直保存在當(dāng)前Run Loop中,直到調(diào)用invalidated方法。

3、Run Loop還有一個(gè)觀察者Observer的概念,可以往Run Loop中加入自己的觀察者以便監(jiān)控Run Loop的運(yùn)行過程,Run Loop Observer則在Run Loop本身進(jìn)入某個(gè)狀態(tài)時(shí)得到通知:Run Loop 進(jìn)入的時(shí)候,Run Loop 處理一個(gè)Timer的時(shí)候,Run Loop 處理一個(gè)Input Source的時(shí)候,Run Loop 進(jìn)入睡眠的時(shí)候,Run Loop 被喚醒的時(shí)候,在喚醒它的事件被處理之前,Run Loop 停止的時(shí)候

Runloop Mode,更改mode只能重新開啟runloop,在設(shè)置Run Loop Mode后,你的Run Loop會(huì)自動(dòng)過濾和其他Mode相關(guān)的事件源,而只監(jiān)視和當(dāng)前設(shè)置Mode相關(guān)的源(通知相關(guān)的觀察者)。大多數(shù)時(shí)候,Run Loop都是運(yùn)行在系統(tǒng)定義的默認(rèn)模式上。

1) NSDefaultRunLoopMode: 大多數(shù)工作中默認(rèn)的運(yùn)行方式。

2) NSConnectionReplyMode: 使用這個(gè)Mode去監(jiān)聽NSConnection對象的狀態(tài),我們很少需要自己使用這個(gè)Mode。

3) NSModalPanelRunLoopMode: 使用這個(gè)Mode在Model Panel情況下去區(qū)分事件(OS X開發(fā)中會(huì)遇到)。

4) UITrackingRunLoopMode: 使用這個(gè)Mode去跟蹤來自用戶交互的事件(比如UITableView上下滑動(dòng))。

5) GSEventReceiveRunLoopMode: 用來接受系統(tǒng)事件,內(nèi)部的Run Loop Mode。

6) NSRunLoopCommonModes:這是一個(gè)偽模式,其為一組run loop mode的集合。如果將Input source加入此模式,意味著關(guān)聯(lián)Input source到Common Modes中包含的所有模式下。在iOS系統(tǒng)中NSRunLoopCommonMode包含NSDefaultRunLoopMode、NSTaskDeathCheckMode、UITrackingRunLoopMode.可使用CFRunLoopAddCommonMode方法向Common Modes中添加自定義mode。

Run Loop運(yùn)行時(shí)只能以一種固定的Mode運(yùn)行,只會(huì)監(jiān)控這個(gè)Mode下添加的Timer source和Input source。如果這個(gè)Mode下沒有添加事件源,Run Loop會(huì)立刻返回。

Run Loop不能在運(yùn)行在NSRunLoopCommonModes,因?yàn)镹SRunLoopCommonModes是個(gè)Mode集合,而不是一個(gè)具體的Mode。我們可以在添加事件源的時(shí)候使用NSRunLoopCommonModes,只要Run Loop運(yùn)行在NSRunLoopCommonModes中任何一個(gè)Mode,這個(gè)事件源都可以被觸發(fā)。


Objective-C中對象等同性怎么做?

obj1 == obj2這個(gè)是直接比較兩個(gè)對象的指針是否相等,而不是對象本身,若想要檢測對象的”對象的等同性“,請?zhí)峁眎sEqual:“與hash方法,NSObject類對這兩個(gè)方法的默認(rèn)實(shí)現(xiàn)是:當(dāng)且僅當(dāng)“指針值”(pointer value)完全相等時(shí),這兩個(gè)對象才相等,自定義對象時(shí),則必須復(fù)寫這些方法,如果isEqual:方法判定兩個(gè)對象相等,那么其hash也必須返回同一個(gè)值。但是,如果兩個(gè)對象的hash方法返回同一個(gè)值,那么“isEqual:”方法未必會(huì)認(rèn)為兩者相等。也就是相同的對象必須具有相同的hash碼,但是兩個(gè)hash碼相同的對象卻未必相同。


Autoreleasepool的使用場景,Autoreleasepool什么時(shí)候釋放?

在沒有手加Autorelease Pool的情況下,Autorelease對象是在當(dāng)前的runloop迭代結(jié)束時(shí)釋放的,而它能夠釋放的原因是系統(tǒng)在每個(gè)runloop迭代中都加入了自動(dòng)釋放池Push和Pop

ARC下,我們使用@autoreleasepool{}來使用一個(gè)AutoreleasePool,編譯器轉(zhuǎn)化成

void *context = objc_autoreleasePoolPush();

// {}中的代碼

objc_autoreleasePoolPop(context);


Objective-C字典中Key、Value有什么特別的要求

1、key不可以重復(fù),value可以重復(fù)

2、key和value不能為空,如果要加入空值,用NSNull

3、value必須是對象


怎么判斷Cell是否在屏幕中?

tableView有兩個(gè)屬性

@property(nonatomic,readonly)NSArray<__kindofUITableViewCell*> *visibleCells;

@property(nonatomic,readonly,nullable)NSArray *indexPathsForVisibleRows;


Strong和weak的底層了解嗎?

MRC與ARC下blcok的區(qū)別

聊一下block的strong weak dance-參考

讀過那些開源代碼?請介紹其中一個(gè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.內(nèi)存管理 2.單例的理解 3.post和get的區(qū)別 4.md5和base64是什么,有什么區(qū)別 5.簡單談?wù)?..
    coder_Wg閱讀 1,303評論 1 6
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 1、基礎(chǔ)篇 1、1 屬性和成員變量的區(qū)別 ? 1、11 涉及到的問題是類的分類中是否可以添加屬性。 肯定是可以添...
    奮斗的螻蟻閱讀 384評論 0 9
  • Objective-C是一門動(dòng)態(tài)的語言 ① 什么是動(dòng)態(tài)語言? 動(dòng)態(tài)語言,是指程序在運(yùn)行時(shí)可以改變其結(jié)構(gòu):新的函數(shù)可...
    小李龍彪閱讀 385評論 0 0
  • 一、筆試常用基礎(chǔ)問題 1.#import 和 #include 的區(qū)別 @class? @class一般用于頭文件...
    鄭莫軒閱讀 1,369評論 0 11