1、如何理解MVC設計模式
MVC是一種架構(gòu)模式,M表示MOdel,V表示視圖View,C表示控制器Controller:
Model負責存儲、定義、操作數(shù)據(jù);
View用來展示書給用戶,和用戶進行操作交互;
Controller是Model和View的協(xié)調(diào)者,Controller把Model中的數(shù)據(jù)拿過來給View用。Controller可以直接與Model和View進行通信,而View不能和Controller直接通信。View與Controller通信需要利用代理協(xié)議的方式,當有數(shù)據(jù)更新時,MOdel也要與Controller進行通信,這個時候就要用Notification和KVO,這個方式就像一個廣播一樣,MOdel發(fā)信號,Controller設置監(jiān)聽接受信號,當有數(shù)據(jù)更新時就發(fā)信號給Controller,Model和View不能直接進行通信,這樣會違背MVC設計模式。
2、Objective-C 中是否支持垃圾回收機制?
OC是支持垃圾回收機制的(Garbage collection簡稱GC),但是apple的移動終端中,是不支持GC的,Mac桌面系統(tǒng)開發(fā)中是支持的.
移動終端開發(fā)是支持ARC(Automatic Reference Counting的簡稱),ARC是在IOS5之后推出的新技術(shù),它與GC的機制是不同的。我們在編寫代碼時, 不需要向?qū)ο蟀l(fā)送release或者autorelease方法,也不可以調(diào)用delloc方法,編譯器會在合適的位置自動給用戶生成release消息(autorelease),ARC 的特點是自動引用技術(shù)簡化了內(nèi)存管理的難度.
3、鍵路徑(keyPath)、鍵值編碼(KVC)和鍵值觀察(KVO)
鍵路徑
鍵-值編碼技術(shù)用于進行這樣的查找—它是一種間接訪問對象屬性的機制。 - 鍵路徑是一個由用點作分隔符的鍵組成的字符串,用于指定一個連接在一起的對象性質(zhì)序列。第一個鍵的性質(zhì)是由先前的性質(zhì)決定的,接下來每個鍵的值也是相對于其前面的性質(zhì)。
鍵路徑使您可以以獨立于模型實現(xiàn)的方式指定相關(guān)對象的性質(zhì)。通過鍵路徑,您可以指定對象圖中的一個任意深度的路徑,使其指向相關(guān)對象的特定屬性。
鍵值編碼KVC
鍵值編碼是一種間接訪問對象的屬性使用字符串來標識屬性,而不是通過調(diào)用存取方法,直接或通過實例變量訪問的機制,非對象類型的變量將被自動封裝或者解封成對象,很多情況下會簡化程序代碼;
KVC的缺點:一旦使用 KVC 你的編譯器無法檢查出錯誤,即不會對設置的鍵、鍵路徑進行錯誤檢查,且執(zhí)行效率要低于合成存取器方法和自定的 setter 和 getter 方法。因為使用 KVC 鍵值編碼,它必須先解析字符串,然后在設置或者訪問對象的實例變量。
鍵值觀察KVO
鍵值觀察機制是一種能使得對象獲取到其他對象屬性變化的通知 ,極大的簡化了代碼。
實現(xiàn) KVO 鍵值觀察模式,被觀察的對象必須使用 KVC 鍵值編碼來修 改它的實例變量,這樣才能被觀察者觀察到。因此,KVC是KVO的基礎。
KVO中誰要監(jiān)聽誰注冊,然后對響應進行處理,使得觀察者與被觀察者完全解耦。KVO只檢測類中的屬性,并且屬性名都是通過NSString來查找,編譯器不會檢錯和補全,全部取決于自己。
4、NSNotification、Block、Delegate和KVO的區(qū)別。
代理是一種回調(diào)機制,且是一對一的關(guān)系,通知是一對多的關(guān)系,一個對向所有的觀察者提供變更通知;
效率:Delegate比NSNOtification高;
Delegate和Block一般是一對一的通信;
Delegate需要定義協(xié)議方法,代理對象實現(xiàn)協(xié)議方法,并且需要建立代理關(guān)系才可以實現(xiàn)通信;
Block:Block更加簡潔,不需要定義繁瑣的協(xié)議方法,但通信事件比較多的話,建議使用Delegate;
5、當我們調(diào)用一個靜態(tài)方法時,需要對對象進行 release 嗎?
不需要,靜態(tài)方法(類方法)創(chuàng)建一個對象時,對象已被放入自動釋放池。在自動釋放池被釋放時,很有可能被銷毀。
6、#include與#import的區(qū)別,#import 與@class 的區(qū)別
#include 和#import其效果相同,都是查詢類中定義的行為(方法);
#import不會引起交叉編譯,確保頭文件只會被導入一次;
@class 的表明,只定 義了類的名稱,而具體類的行為是未知的,一般用于.h 文件;
@class 比#import 編譯效率更高。
此外@class 和#import 的主要區(qū)別在于解決引用死鎖的問題。
7、@public、@protected、@private 它們的含義與作用
@public:對象的實例變量的作用域在任意地方都可以被訪問 ;
@protected:對象的實例變量作用域在本類和子類都可以被訪問 ;
@private:實例變量的作用域只能在本類(自身)中訪問 .
8、自動釋放池工作原理
自動釋放池是NSAutorelease類的一個實例,當向一個對象發(fā)送autorelease消息時,該對象會自動入池,待池銷毀時,將會向池中所有對象發(fā)送一條release消息,釋放對象。
[pool release]、 [pool drain]表示的是池本身不會銷毀,而是池子中的臨時對象都被發(fā)送release,從而將對象銷毀。
9、UITableViewCell上有個UILabel,顯示NSTimer實現(xiàn)的秒表時間,手指滾動cell過程中,label是否刷新,為什么?
這是否刷新取決于timer加入到Run Loop中的Mode是什么。Mode主要是用來指定事件在運行循環(huán)中的優(yōu)先級的,分為:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認,空閑狀態(tài)
UITrackingRunLoopMode:ScrollView滑動時會切換到該Mode
UIInitializationRunLoopMode:run loop啟動時,會切換到該mode
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋果公開提供的Mode有兩個:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
在編程中:如果我們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環(huán)中的時候, ScrollView滾動過程中會因為mode的切換,而導致NSTimer將不再被調(diào)度。當我們滾動的時候,也希望不調(diào)度,那就應該使用默認模式。但是,如果希望在滾動時,定時器也要回調(diào),那就應該使用common mode。
10、解決cell重用的問題
UITableView通過重用單元格來達到節(jié)省內(nèi)存的目的,通過為每個單元格指定一個重用標示(reuseidentifier),即指定了單元格的種類,以及當單元格滾出屏幕時,允許恢復單元格以便復用。對于不同種類的單元格使用不同的ID,對于簡單的表格,一個標示符就夠了。
如一個TableView中有10個單元格,但屏幕最多顯示4個,實際上iPhone只為其分配4個單元格的內(nèi)存,沒有分配10個,當滾動單元格時,屏幕內(nèi)顯示的單元格重復使用這4個內(nèi)存。實際上分配的cell的個數(shù)為屏幕最大顯示數(shù),當有新的cell進入屏幕時,會隨機調(diào)用已經(jīng)滾出屏幕的Cell所占的內(nèi)存,這就是Cell的重用。
對于多變的自定義Cell,這種重用機制會導致內(nèi)容出錯,為解決這種出錯的方法,把原來的
1
2
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:defineString]
修改為:UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexPath];
這樣就解決掉cell重用機制導致的問題。
11、有a、b、c、d 4個異步請求,如何判斷a、b、c、d都完成執(zhí)行?如果需要a、b、c、d順序執(zhí)行,該如何實現(xiàn)?
對于這四個異步請求,要判斷都執(zhí)行完成最簡單的方式就是通過GCD的group來實現(xiàn):
1
2
3
4
5
6
7
8
9
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*任務a */ });
dispatch_group_async(group, queue, ^{ /*任務b */ });
dispatch_group_async(group, queue, ^{ /*任務c */ });
dispatch_group_async(group, queue, ^{ /*任務d */ });
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
// 在a、b、c、d異步執(zhí)行完成后,會回調(diào)這里
});
當然,我們還可以使用非常老套的方法來處理,通過四個變量來標識a、b、c、d四個任務是否完成,然后在runloop中讓其等待,當完成時才退出runloop。但是這樣做會讓后面的代碼得不到執(zhí)行,直到Run loop執(zhí)行完畢。
解釋:要求順序執(zhí)行,那么可以將任務放到串行隊列中,自然就是按順序來異步執(zhí)行了。
12、使用block有什么好處?使用NSTimer寫出一個使用block顯示(在UILabel上)秒表的代碼。
代碼緊湊,傳值、回調(diào)都很方便,省去了寫代理的很多代碼。
NSTimer封裝成的block,具體實現(xiàn)。
實現(xiàn)方法:
1
2
3
4
5
6
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
repeats:YES
callback:^() {
weakSelf.secondsLabel.text = ...
}
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
13、定時器與線程的區(qū)別
定時器;可以執(zhí)行多次,默認在主線程中。
線程:只能執(zhí)行一次。
14、TCP和UDP的區(qū)別于聯(lián)系
TCP為傳輸控制層協(xié)議,為面向連接、可靠的、點到點的通信;
UDP為用戶數(shù)據(jù)報協(xié)議,非連接的不可靠的點到多點的通信;
TCP側(cè)重可靠傳輸,UDP側(cè)重快速傳輸。
15、如何進行真機調(diào)試?
1.首先需要用鑰匙串創(chuàng)建一個鑰匙(key);
2.將鑰匙串上傳到官網(wǎng),獲取iOS Development證書;
3.創(chuàng)建App ID即我們應用程序中的Boundle ID;
4.添加Device ID即UDID;
5.通過勾選前面所創(chuàng)建的證書:App ID、Device ID;
6.生成mobileprovision文件;
7.先決條件:申請開發(fā)者賬號 99美刀
APP發(fā)布的上架流程
1.登錄應用發(fā)布網(wǎng)站添加應用信息;
2.下載安裝發(fā)布證書;
3.選擇發(fā)布證書,使用Archive編譯發(fā)布包,用Xcode將代碼(發(fā)布包)上傳到服務器;
4.等待審核通過;
5.生成IPA:菜單欄->Product->Archive.
16、對NSUserDefaults的理解
NSUserDefaults:系統(tǒng)提供的一種存儲數(shù)據(jù)的方式,主要用于保存少量的數(shù)據(jù),默認存儲到library下的Preferences文件夾。
17、SDWebImage原理
調(diào)用類別的方法:
從內(nèi)存中(字典)找圖片(當這個圖片在本次程序加載過),找到直接使用;
從沙盒中找,找到直接使用,緩存到內(nèi)存。
從網(wǎng)絡上獲取,使用,緩存到內(nèi)存,緩存到沙盒。
18、LayoutSubViews在什么時候被調(diào)用?
當View本身的frame改變時,會調(diào)用這個方法。
19、自定義實現(xiàn)UITabbarController的原理
運用字典,點擊五個按鈕的一個可以從字典里選擇一個控制器對象,將其View顯示到主控制器視圖上。
20、View和View之間傳值方式
對象的property屬性傳值;
方法參數(shù)傳值;
NSUserDefault傳值;(單例)
塊傳值。
21、對于Run Loop的理解
RunLoop,是多線程的法寶,即一個線程一次只能執(zhí)行一個任務,執(zhí)行完任務后就會退出線程。主線程執(zhí)行完即時任務時會繼續(xù)等待接收事件而不退出。非主線程通常來說就是為了執(zhí)行某一任務的,執(zhí)行完畢就需要歸還資源,因此默認是不運行RunLoop的;
每一個線程都有其對應的RunLoop,只是默認只有主線程的RunLoop是啟動的,其它子線程的RunLoop默認是不啟動的,若要啟動則需要手動啟動;
在一個單獨的線程中,如果需要在處理完某個任務后不退出,繼續(xù)等待接收事件,則需要啟用RunLoop;
NSRunLoop提供了一個添加NSTimer的方法,可以指定Mode,如果要讓任何情況下都回調(diào),則需要設置Mode為Common模式;
實質(zhì)上,對于子線程的runloop默認是不存在的,因為蘋果采用了懶加載的方式。如果我們沒有手動調(diào)用[NSRunLoop currentRunLoop]的話,就不會去查詢是否存在當前線程的RunLoop,也就不會去加載,更不會創(chuàng)建。
22、SQLite中常用的SQL語句
創(chuàng)建表:creat table 表名 (字段名 字段數(shù)據(jù)類型 是否為主鍵, 字段名 字段數(shù)據(jù)類型, 字段名 字段數(shù)據(jù)類型...);
增: insert into 表名 (字段1, 字段2...) values (值1, 值2...);
刪: delete from 表名 where 字段 = 值;
23、內(nèi)存的使用和優(yōu)化的注意事項
重用問題:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews設置正確的reuseIdentifier,充分重用;
盡量把views設置為不透明:當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結(jié)果,可提高性能;
不要使用太復雜的XIB/Storyboard:載入時就會將XIB/storyboard需要的所有資源,包括圖片全部載入內(nèi)存,即使未來很久才會使用。那些相比純代碼寫的延遲加載,性能及內(nèi)存就差了很多;
選擇正確的數(shù)據(jù)結(jié)構(gòu):學會選擇對業(yè)務場景最合適的數(shù)組結(jié)構(gòu)是寫出高效代碼的基礎。比如,數(shù)組: 有序的一組值。使用索引來查詢很快,使用值查詢很慢,插入/刪除很慢。字典: 存儲鍵值對,用鍵來查找比較快。集合: 無序的一組值,用值來查找很快,插入/刪除很快。
gzip/zip壓縮:當從服務端下載相關(guān)附件時,可以通過gzip/zip壓縮后再下載,使得內(nèi)存更小,下載速度也更快。
延遲加載:對于不應該使用的數(shù)據(jù),使用延遲加載方式。對于不需要馬上顯示的視圖,使用延遲加載方式。比如,網(wǎng)絡請求失敗時顯示的提示界面,可能一直都不會使用到,因此應該使用延遲加載。
數(shù)據(jù)緩存:對于cell的行高要緩存起來,使得reload數(shù)據(jù)時,效率也極高。而對于那些網(wǎng)絡數(shù)據(jù),不需要每次都請求的,應該緩存起來,可以寫入數(shù)據(jù)庫,也可以通過plist文件存儲。
處理內(nèi)存警告:一般在基類統(tǒng)一處理內(nèi)存警告,將相關(guān)不用資源立即釋放掉
重用大開銷對象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它們。通常是作為屬性存儲起來,防止反復創(chuàng)建。
避免反復處理數(shù)據(jù):許多應用需要從服務器加載功能所需的常為JSON或者XML格式的數(shù)據(jù)。在服務器端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要;
使用Autorelease Pool:在某些循環(huán)創(chuàng)建臨時變量處理數(shù)據(jù)時,自動釋放池以保證能及時釋放內(nèi)存;
正確選擇圖片加載方式:詳情閱讀細讀UIImage加載方式
24、UIViewController的完整生命周期
-[ViewController initWithNibName:bundle:];
-[ViewController init];
-[ViewController loadView];
-[ViewController viewDidLoad];
-[ViewController viewWillDisappear:];
-[ViewController viewWillAppear:];
-[ViewController viewDidAppear:];
-[ViewController viewDidDisappear:];
25、UIImageView添加圓角
最直接的方法就是使用如下屬性設置:
imgView.layer.cornerRadius = 10;
// 這一行代碼是很消耗性能的
imgView.clipsToBounds = YES;
**這是離屏渲染(off-screen-rendering),消耗性能的**
給UIImage添加生成圓角圖片的擴展API:這是on-screen-rendering
- (UIImage *)imageWithCornerRadius:(CGFloat)radius {
CGRect rect = (CGRect){0.f, 0.f, self.size};
UIGraphicsBeginImageContextWithOptions(self.size, NO, UIScreen.mainScreen.scale);
CGContextAddPath(UIGraphicsGetCurrentContext(),
[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
CGContextClip(UIGraphicsGetCurrentContext());
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}