1、ios內存管理機制
iOS內存管理機制的原理是引用計數,當這塊內存被創建后,它的引用計數0->1,表示有一個對象或指針持有這塊內存,擁有這塊內存的所有權,如果這時候有另外一個對象或指針指向這塊內存,那么為了表示這個后來的對象或指針對這塊內存的所有權,引用計數1->2,之后若有一個對象或指針不再指向這塊內存時,引用計數-1,表示這個對象或指針不再擁有這塊內存的所有權,當一塊內存的引用計數變為0,表示沒有任何對象或指針持有這塊內存,系統便會立刻釋放掉這塊內存。
alloc、new :類初始化方法,開辟新的內存空間,引用計數+1;
retain :實例方法,不會開辟新的內存空間,引用計數+1;
copy : 實例方法,把一個對象復制到新的內存空間,新的內存空間引用計數+1,舊的不會;其中分為淺拷貝和深拷貝,淺拷貝只是拷貝地址,不會開辟新的內存空間;深拷貝是拷貝內容,會開辟新的內存空間;
strong :強引用; 引用計數+1;
release :實例方法,釋放對象;引用計數-1;
autorelease : 延遲釋放;autoreleasepool自動釋放池;當執行完之后引用計數-1;
還有是initWithFormat和stringWithFormat 字符串長度大于9時,引用計數+1;
assign : 弱引用 ;weak也是弱引用,兩者區別:assign不但能作用于對象還能作用于基本數據類型,但是所指向的對象銷毀時不會將當前指向對象的指針指向nil,有野指針的生成;weak只能作用于對象,不能作用于基本數據類型,所指向的對象銷毀時會將當前指向對象的指針指向nil,防止野指針的生成。
2、NSThread、GCD、NSOperation多線程
2.1、NSThread
NSThread是封裝程度最小最輕量級的,使用更靈活,但要手動管理線程的生命周期、線程同步和線程加鎖等,開銷較大;
[NSThread isMultiThreaded];//BOOL 是否開啟了多線程
[NSThread currentThread];//NSThread 獲取當前線程
[NSThread mainThread];//NSThread 獲取主線程
[NSThread sleepForTimeInterval:1];//線程睡眠1s
2.2、GCD
GCD基于C語言封裝的,遵循FIFO,Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法,GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。它讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務,一個任務可以是一個函數(function)或者是一個block。
dispatch_sync與dispatch_async//同步和異步操作
dispatch_queue_t;//主要有串行和并發兩種;
其中:
dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT)并發;
dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL)串行;
dispatch_once_t;//代碼只會被執行一次,用于單例
dispatch_after;//延遲操作
dispatch_get_main_queue;//回到主線程操作
2.2、NSOperation
NSOperation基于GCD封裝的,比GCD可控性更強;可以加入操作依賴(addDependency)、設置操作隊列最大可并發執行的操作個數(setMaxConcurrentOperationCount)、取消操作(cancel)等,需要使用兩個它的實體子類:NSBlockOperation和NSInvocationOperation,或者繼承NSOperation自定義子類;NSBlockOperation和NSInvocationOperation用法的主要區別是:前者執行指定的方法,后者執行代碼塊,相對來說后者更加靈活易用。NSOperation操作配置完成后便可調用start函數在當前線程執行,如果要異步執行避免阻塞當前線程則可以加入NSOperationQueue中異步執行,優點:不需要關心線程管理,數據同步的事情。
3、簡述KVC和KVO
KVC簡稱鍵值編碼。是對NSObjcet的擴展,分類名為 : NSKeyValueCoding
我們經常用KVC或者setter方法來觸發KVO,實現鍵值變化監聽,實現一些功能。
賦值實現原理:
1、查找是否實現setter
、_setter
方法,如果有,優先調用setter方法完成賦值(注意:set后面的鍵的第一字字母必須是大寫!!)
2、當沒找到setter方法,調用accessInstanceVariablesDirectly詢問。
如果返回YES,順序匹配變量名與 _<key>,_is<Key>,<key>,is<Key>,匹配到則設定其值
如果返回NO,結束查找。并調用 setValue:forUndefinedKey:報異常
3、如果既沒有setter也沒有實例變量時,調用 setValue:forUndefinedKey:
取值實現原理:
原理總綱
1、查找是否實現getter方法,依次匹配-get<Key>
和-<key>
和is<Key>
,如果找到,直接返回。
需要注意的是 :
如果返回的是對象指針類型,則返回結果。
如果返回的是NSNumber轉換所支持的標量類型之一,則返回一個NSNumber
否則,將返回一個NSValue
2、當沒有找到getter方法,調用accessInstanceVariablesDirectly詢問
如果返回YES, _<key>,_is<Key>,<key>,is<Key>,找到了返回對應的值
如果返回NO,結束查找。并調用 valueForUndefinedKey: 報異常
3、如果沒找到getter方法和屬性值,調用 valueForUndefinedKey: 報異常
KVC 為什么能能夠觸發 KVO
KVC 只所以能夠觸發 KVO,那是因為 在 KVC 底層有手動觸發 KVO的代碼,方法 willChangeValueForKey 和 didChangeValueForKey可得到驗證。
KVO : 鍵值觀察者 (Key-Value Observer): KVO 是觀察者模式的一種實現,觀察者A監聽被觀察者B的某個屬性,當B的屬性發生更改時,A就會收到通知,執行相應的方法。
KVO 的工作原理
1.動態子類化
當你對某個對象的屬性添加觀察者時,KVO 會在運行時動態地創建該對象的一個子類,并將這個對象的 isa指針指向這個新的子類。
這個子類重寫了被觀察屬性的 setter 方法。重寫后的 setter 方法在調用父類原始的 setter 方法之前和之后,分別調用 willChangeValueForKey: 和 didChangeValueForKey:方法.
2.willChangeValueForKey: 和 didChangeValueForKey
willChangeValueForKey:在屬性值改變之前調用,通知系統即將發生變化。
didChangeValueForKey:在屬性值改變之后調用,通知系統已經發生變化。
這兩個方法會觸發 KVO 通知,觀察者在 observeValueForKeyPath:ofObject:change:context: 方法中接收到這些通知,并進行相應的處理。
3.消息轉發
新的子類會重寫 class`方法,返回原始類的類型,使得對象看起來還是原來的類。
當對該對象發送消息時,如果該消息不是由重寫的方法處理的,消息會被轉發到原始類的實現。
線程安全:KVO 不是線程安全的。如果觀察的屬性可能在多個線程中修改,確保在訪問和修改屬性時使用合適的同步機制。
4、Block實現原理,堆上和棧上的數據如何同步
block本質上也是一個oc對象,他內部也有一個isa指針。block是封裝了函數調用以及函數調用環境的OC對象。結構體,在棧上的情況, Block中的指針只是指向棧上的__block變量, 而當Block/__block變量被copy到堆上以后, 堆上Block會持有堆上__block變量. 而堆上的Block再次被調用copy時, 只是Block的引用計數+1而已, 而__block變量如果被多個堆上Block持有也只涉及到引用記數的變化. 一旦Block/__block變量的引用計數為0, 就會自動從堆上釋放內存.這里Block/__block變量在堆上的內存管理與Objective-C對象完全一致.
5、iOS設計模式
5.1、觀察者模式
1.何為觀察者模式
當對象間存在一對多關系時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬于行為型模式。
2.如何使用觀察者模式
一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。
3.觀察者模式的優缺點
優點:觀察者和被觀察者是抽象耦合的。建立一套觸發機制。缺點:如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而僅僅只是知道觀察目標發生了變化。
5.2、代理模式
1.何為代理模式
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式。
在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。
2.如何使用代理模式
在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。
想在訪問一個類時做一些控制。
3.代理模式的優缺點
優點:
職責清晰、高擴展性、智能化。
缺點:
由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
實現代理模式需要額外的工作,有些代理模式的實現非常復雜。
5.3、單例模式
1.何為單例模式
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
單例類只能有一個實例。
單例類必須自己創建自己的唯一實例。
單例類必須給所有其他對象提供這一實例。
2.如何使用單例模式
當您想控制實例數目,節省系統資源的時候。
3.單例模式的優缺點
優點:
在內存里只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。
避免對資源的多重占用比如寫文件操作。
缺點:
沒有接口,不能繼承,與單一職責原則沖突,一個類應該只關心內部邏輯,而不關心外面怎么樣來實例化。
6、NSNotificationCenter通知中心的實現原理
NSNotificationCenter是類似一個廣播中心站,使用defaultCenter來獲取應用中的通知中心,它可以向應用任何地方發送和接收通知。在通知中心注冊觀察者,發送者使用通知中心廣播時,以NSNotification的name和object來確定需要發送給哪個觀察者。為保證觀察者能接收到通知,所以應先向通知中心注冊觀察者,接著再發送通知這樣才能在通知中心調度表中查找到相應觀察者進行通知。
-(void)postNotification:(NSNotification *)notification;
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
發送通知通過name和object來確定來標識觀察者,name和object兩個參數的規則相同即當通知設置name為kChangeNotifition時,那么只會發送給符合name為kChangeNotifition的觀察者,同理object指發送給某個特定對象通知,如果只設置了name,那么只有對應名稱的通知會觸發。如果同時設置name和object參數時就必須同時符合這兩個條件的觀察者才能接收到通知。
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
第一種方式是比較常用的添加Oberver的方式,接到通知時執行aSelector。
第二種方式是基于Block來添加觀察者,往通知中心的調度表中添加觀察者,這個觀察者包括一個queue和一個block,并且會返回這個觀察者對象。當接到通知時執行block所在的線程為添加觀察者時傳入的queue參數,queue也可以為nil,那么block就在通知所在的線程同步執行。
這里需要注意的是如果使用第二種的方式創建觀察者需要弱引用可能引起循環引用的對象,避免內存泄漏。
NSNotificatinonCenter實現原理:
NSNotificatinonCenter是使用觀察者模式來實現的用于跨層傳遞消息,用來降低耦合度。
NSNotificatinonCenter用來管理通知,將觀察者注冊到NSNotificatinonCenter的通知調度表中,然后發送通知時利用標識符name和object識別出調度表中的觀察者,然后調用相應的觀察者的方法,即傳遞消息(在Objective-C中對象調用方法,就是傳遞消息,消息有name或者selector,可以接受參數,而且可能有返回值),如果是基于block創建的通知就調用NSNotification的block。
7、推送如何實現的
1.由App向iOS設備發送一個注冊通知,用戶需要同意系統發送推送。
2.iOS應用向APNS遠程推送服務器發送App的Bundle Id和設備的UDID。
3.APNS根據設備的UDID和App的Bundle Id生成deviceToken再發回給App。
4.App再將deviceToken發送給遠程推送服務器(自己的服務器), 由服務器保存在數據庫中。
5.當自己的服務器想發送推送時, 在遠程推送服務器中輸入要發送的消息并選擇發給哪些用戶的deviceToken,由遠程推送服務器發送給APNS。
6.APNS根據deviceToken發送給對應的用戶。
8、SEL的使用和原理
SEL 類成員方法的指針
可以理解 @selector()就是取類方法的編號,他的行為基本可以等同C語言的中函數指針,只不過C語言中,可以把函數名直接賦給一個函數指針,而Object-C的類不能直接應用函數指針,這樣只能做一個@selector語法來取.
它的結果是一個SEL類型。這個類型本質是類方法的編號(函數地址)
9、簡述weak的實現原理
weak 關鍵字的作用弱引用,所引用對象的計數器不會加一,并在引用對象被釋放的時候自動被設置為 nil;
weak是有Runtime維護的weak表;
3.weak釋放為nil過程
weak被釋放為nil,需要對對象整個釋放過程了解,如下是對象釋放的整體流程:
1、調用objc_release
2、因為對象的引用計數為0,所以執行dealloc
3、在dealloc中,調用了_objc_rootDealloc函數
4、在_objc_rootDealloc中,調用了object_dispose函數
5、調用objc_destructInstance
6、最后調用objc_clear_deallocating。
對象準備釋放時,調用clearDeallocating函數。clearDeallocating函數首先根據對象地址獲取所有weak指針地址的數組,然后遍歷這個數組把其中的數據設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。
其實Weak表是一個hash(哈希)表,然后里面的key是指向對象的地址,Value是Weak指針的地址的數組
總結
weak是Runtime維護了一個hash(哈希)表,用于存儲指向某個對象的所有weak指針。weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象指針的地址)數組。
10、列表頁性能優化
10.1、如何檢測
1)Instruments中:Core Animation;
2)FPS:CADisplayLink
10.2、優化方案
1、文本、布局計算,提前計算緩存;
2、對象創建;CALayer代替UIView;
3、使用CAShapeLayer和UIBezierPath設置圓角;
4、UIBezierPath和Core Graphics框架畫出一個圓角;
11、HTTP post的body體使用form-urlencoded和multipart/form-data的區別
1)application/x-www-form-urlencoded:
窗體數據被編碼為名稱/值對,這是標準且默認的編碼格式。當action為get時候,客戶端把form數據轉換成一個字串append到url后面,用?分割。當action為post時候,瀏覽器把form數據封裝到http body中,然后發送到server。
2)multipart/form-data:
multipart表示的意思是單個消息頭包含多個消息體的解決方案。multipart媒體類型對發送非文本的各媒體類型是有用的。一般多用于文件上傳。multipart/form-data只是multipart的一種。目前常用的有以下這些類型(注:任何一種執行時無法識別的multipart子類型都被視為子類型"mixed")
12、單例的弊端
+ (instancetype)sharedInstance {
static ZZScreenshotsMonitor *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
優點:
1:一個類只被實例化一次,提供了對唯一實例的受控訪問。
2:節省系統資源
3:允許可變數目的實例。
缺點:
1:一個類只有一個對象,可能造成責任過重,在一定程度上違背了“單一職責原則”。
2:由于單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
3:濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。
13、App啟動過慢,你可能想到的因素有哪些
影響啟動性能的因素
main()函數之前耗時的影響因素
動態庫加載越多,啟動越慢。
ObjC類越多,啟動越慢
C的constructor函數越多,啟動越慢
C++靜態對象越多,啟動越慢
ObjC的+load越多,啟動越慢
main()函數之后耗時的影響因素
執行main()函數的耗時
執行applicationWillFinishLaunching的耗時
rootViewController及其childViewController的加載、view及其subviews的加載
14、TCP和UDP的區別于聯系
- TCP為傳輸控制層協議,為面向連接、可靠的、點到點的通信;
- UDP為用戶數據報協議,非連接的不可靠的點到多點的通信;
- TCP側重可靠傳輸,UDP側重快速傳輸。
15、TCP連接的三次握手
- 第一次握手:客戶端發送 syn 包(syn=j)到服務器,并進入 SYN_SEND 狀態,等待服務器確認;
- 第二次握手:服務器收到 syn 包,必須確認客戶的 SYN(ack=j+1),同時自己也發送一個 SYN 包(syn=k),即 SYN+ACK 包,此時服務器進入 SYN_RECV 狀態;
- 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入 ESTABLISHED 狀態,完成三次握手。
握手過程中傳送的包里不包含數據,三次握手完畢后,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP 連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。斷開連接時服務器和客戶端均可以主動發起斷開 TCP 連接的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是服務器和客戶端交互,最終確定斷開)
16、Scoket連接和HTTP連接的區別
- HTTP協議是基于TCP連接的,是應用層協議,主要解決如何包裝數據。Socket是對TCP/IP協議的封裝,Socket本身并不是協議,而是一個調用接口(API),通過Socket,我們才能使用TCP/IP協議。
- HTTP連接:短連接,客戶端向服務器發送一次請求,服務器響應后連接斷開,節省資源。服務器不能主動給客戶端響應(除非采用HTTP長連接技術),iPhone主要使用類NSURLConnection。
- Socket連接:長連接,客戶端跟服務器端直接使用Socket進行連接,沒有規定連接后斷開,因此客戶端和服務器段保持連接通道,雙方可以主動發送數據,一般多用于游戲.Socket默認連接超時時間是30秒,默認大小是8K(理解為一個數據包大小)
17、HTTP協議的特點,關于HTTP請求GET和POST的區別
GET和POST的區別:
- HTTP超文本傳輸協議,是短連接,是客戶端主動發送請求,服務器做出響應,服務器響應之后,鏈接斷開。HTTP是一個屬于應用層面向對象的協議,HTTP有兩類報文:請求報文和響應報文。
- HTTP請求報文:一個HTTP請求報文由請求行、請求頭部、空行和請求數據4部分組成。
- HTTP響應報文:由三部分組成:狀態行、消息報頭、響應正文。
- GET請求:參數在地址后拼接,沒有請求數據,不安全(因為所有參數都拼接在地址后面),不適合傳輸大量數據(長度有限制,為1024個字節)。
GET提交、請求的數據會附在URL之后,即把數據放置在HTTP協議頭<requestline>中。
以分割URL和傳輸數據,多個參數用&連接。如果數據是英文字母或數字,原樣發送,
如果是空格,轉換為+,如果是中文/其他字符,則直接把字符串用BASE64加密。
- POST請求:參數在請求數據區放著,相對GET請求更安全,并且數據大小沒有限制。把提交的數據放置在HTTP包的包體<request-body>中.
- GET提交的數據會在地址欄顯示出來,而POST提交,地址欄不會改變。
傳輸數據的大小:
- GET提交時,傳輸數據就會受到URL長度限制,POST由于不是通過URL傳值,理論上書不受限
安全性:
- POST的安全性要比GET的安全性高;
- 通過GET提交數據,用戶名和密碼將明文出現在URL上,比如登陸界面有可能被瀏覽器緩存。
- HTTPS:安全超文本傳輸協議(Secure Hypertext Transfer Protocol),它是一個安全通信通道,基于HTTP開發,用于客戶計算機和服務器之間交換信息,使用安全套結字層(SSI)進行信息交換,即HTTP的安全版。
18、ASIHttpRequest、AFNetWorking之間的區別
- ASIHttpRequest功能強大,主要是在MRC下實現的,是對系統CFNetwork API進行了封裝,支持HTTP協議的CFHTTP,配置比較復雜,并且ASIHttpRequest框架默認不會幫你監聽網絡改變,如果需要讓ASIHttpRequest幫你監聽網絡狀態改變,并且手動開始這個功能。
- AFNetWorking構建于NSURLConnection、NSOperation以及其他熟悉的Foundation技術之上。擁有良好的架構,豐富的API及模塊構建方式,使用起來非常輕松。它基于NSOperation封裝的,AFURLConnectionOperation子類。
- ASIHttpRequest是直接操作對象ASIHttpRequest是一個實現了NSCoding協議的NSOperation子類;AFNetWorking直接操作對象的AFHttpClient,是一個實現NSCoding和NSCopying協議的NSObject子類。
- 同步請求:ASIHttpRequest直接通過調用一個startSynchronous方法;AFNetWorking默認沒有封裝同步請求,如果開發者需要使用同步請求,則需要重寫getPath:paraments:success:failures方法,對于AFHttpRequestOperation進行同步處理。
- 性能對比:AFNetworking請求優于ASIHttpRequest;
19、XML數據解析方式各有什么不同,JSON解析有哪些框架
XML數據解析的兩種解析方式:DOM解析和SAX解析
- DOM解析必須完成DOM樹的構造,在處理規模較大的XML文檔時就很耗內存,占用資源較多,讀入整個XML文檔并構建一個駐留內存的樹結構(節點樹),通過遍歷樹結構可以檢索任意XML節點,讀取它的屬性和值,通常情況下,可以借助XPath查詢XML節點;
- SAX與DOM不同,它是事件驅動模型,解析XML文檔時每遇到一個開始或者結束標簽、屬性或者一條指令時,程序就產生一個事件進行相應的處理,一邊讀取XML文檔一邊處理,不必等整個文檔加載完才采取措施,當在讀取解析過程中遇到需要處理的對象,會發出通知進行處理。因此,SAX相對于DOM來說更適合操作大的XML文檔。
- JSON解析:性能比較好的主要是第三方的JSONKIT和iOS自帶的JSON解析類,其中自帶的JSON解析性能最高,只能用于iOS5之后。
20、網絡七層協議
- 應用層:
1.用戶接口、應用程序;
2.Application典型設備:網關;
3.典型協議、標準和應用:TELNET、FTP、HTTP
- 表示層:
1.數據表示、壓縮和加密presentation
2.典型設備:網關
3.典型協議、標準和應用:ASCLL、PICT、TIFF、JPEG|MPEG
4.表示層相當于一個東西的表示,表示的一些協議,比如圖片、聲音和視頻MPEG。
- 會話層:
1.會話的建立和結束;
2.典型設備:網關;
3.典型協議、標準和應用:RPC、SQL、NFS、X WINDOWS、ASP
- 傳輸層:
1.主要功能:端到端控制Transport;
2.典型設備:網關;
3.典型協議、標準和應用:TCP、UDP、SPX
- 網絡層:
1.主要功能:路由、尋址Network;
2.典型設備:路由器;
3.典型協議、標準和應用:IP、IPX、APPLETALK、ICMP;
- 數據鏈路層:
1.主要功能:保證無差錯的疏忽鏈路的data link;
2.典型設備:交換機、網橋、網卡;
3.典型協議、標準和應用:802.2、802.3ATM、HDLC、FRAME RELAY;
- 物理層:
1.主要功能:傳輸比特流Physical;
2.典型設備:集線器、中繼器
3.典型協議、標準和應用:V.35、EIA/TIA-232.
21、XIB與Storyboards的優缺點
優點:
- XIB:在編譯前就提供了可視化界面,可以直接拖控件,也可以直接給控件添加約束,更直觀一些,而且類文件中就少了創建控件的代碼,確實簡化不少,通常每個XIB對應一個類。
- Storyboard:在編譯前提供了可視化界面,可拖控件,可加約束,在開發時比較直觀,而且一個storyboard可以有很多的界面,每個界面對應一個類文件,通過storybard,可以直觀地看出整個App的結構。
缺點:
- XIB:需求變動時,需要修改XIB很大,有時候甚至需要重新添加約束,導致開發周期變長。XIB載入相比純代碼自然要慢一些。對于比較復雜邏輯控制不同狀態下顯示不同內容時,使用XIB是比較困難的。當多人團隊或者多團隊開發時,如果XIB文件被發動,極易導致沖突,而且解決沖突相對要困難很多。
- Storyboard:需求變動時,需要修改storyboard上對應的界面的約束,與XIB一樣可能要重新添加約束,或者添加約束會造成大量的沖突,尤其是多團隊開發。對于復雜邏輯控制不同顯示內容時,比較困難。當多人團隊或者多團隊開發時,大家會同時修改一個storyboard,導致大量沖突,解決起來相當困難。
22、HTTPS的加密原理
- 服務器端用非對稱加密(RSA)生成公鑰和私鑰
- 然后把公鑰發給客戶端, 服務器則保存私鑰
- 客戶端拿到公鑰后, 會生成一個密鑰, 這個密鑰就是將來客戶端和服務器用來通信的鑰匙
- 然后客戶端用公鑰對密鑰進行加密, 再發給服務器
- 服務器拿到客戶端發來的加密后的密鑰后, 再使用私鑰解密密鑰, 到此雙方都獲得通信的鑰匙
23、ARC的工作原理
- Automatic Reference Counting,自動引用計數,即ARC,ARC會自動幫你插入retain和release語句,ARC編譯器有兩部分,分別是前端編譯器和優化器
- 前端編譯器:前端編譯器會為“擁有的”每一個對象插入相應的release語句。如果對象的所有權修飾符是__strong,那么它就是被擁有的。如果在某個方法內創建了一個對象,前端編譯器會在方法末尾自動插入release語句以銷毀它。而類擁有的對象(實例變量/屬性)會在dealloc方法內被釋放。事實上,你并不需要寫dealloc方法或調用父類的dealloc方法,ARC會自動幫你完成一切。此外,由編譯器生成的代碼甚至會比你自己寫的release語句的性能還要好,因為編輯器可以作出一些假設。在ARC中,沒有類可以覆蓋release方法,也沒有調用它的必要。ARC會通過直接使用objc_release來優化調用過程。而對于retain也是同樣的方法。ARC會調用objc_retain來取代保留消息
- ARC優化器: 雖然前端編譯器聽起來很厲害的樣子,但代碼中有時仍會出現幾個對retain和release的重復調用。ARC優化器負責移除多余的retain和release語句,確保生成的代碼運行速度高于手動引用計數的代碼
24、描述一個ViewController的生命周期
- 當我們調用UIViewControlller的view時,
- 系統首先判斷當前的 UIViewControlller是否存在* view,如果存在直接返回view,
- 如果不存在的話,會調用loadview方法,
- 然后判斷loadview方法是否是自定義方法,
- 如果是自定義方法,就執行自定義方法,
- 如果不是自定義方法,判斷當時視圖控制器是否有* xib、stroyboard。
- 如果有xib、stroyboard 就加載xib、stroyboard。
- 如果沒有創建一個空白的view。
- 調用viewDidLoad方法。
- 最后返回view
25、內存的使用和優化的注意事項
- 重用問題:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews設置正確的reuseIdentifier,充分重用;
通過正確的設置 reuseIdentifier 來重用 Cell。
盡量減少不必要的透明 View。
盡量避免漸變效果、圖片拉伸和離屏渲染。
當不同的行的高度不一樣時,盡量緩存它們的高度值。
如果 Cell 展示的內容來自網絡,確保用異步加載的方式來獲取數據,并且緩存服務器的 response。
使用 shadowPath 來設置陰影效果。
盡量減少 subview 的數量,對于 subview 較多并且樣式多變的 Cell,可以考慮用異步繪制或重寫 drawRect。
盡量優化 - [UITableView tableView:cellForRowAtIndexPath:] 方法中的處理邏輯,如果確實要做一些處理,可以考慮做一次,緩存結果。選擇合適的數據結構來承載數據,不同的數據結構對不同操作的開銷是存在差異的。
對于 rowHeight、sectionFooterHeight、sectionHeaderHeight 盡量使用常量。- 盡量把views設置為不透明:當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結果,可提高性能;
不要使用太復雜的XIB/Storyboard:載入時就會將XIB/storyboard需要的所有資源,包括圖片全部載入內存,即使未來很久才會使用。那些相比純代碼寫的延遲加載,性能及內存就差了很多;- 選擇正確的數據結構:學會選擇對業務場景最合適的數組結構是寫出高效代碼的基礎。比如,數組: 有序的一組值。使用索引來查詢很快,使用值查詢很慢,插入/刪除很慢。字典: 存儲鍵值對,用鍵來查找比較快。集合: 無序的一組值,用值來查找很快,插入/刪除很快。
gzip/zip壓縮:當從服務端下載相關附件時,可以通過gzip/zip壓縮后再下載,使得內存更小,下載速度也更快。- 延遲加載:對于不應該使用的數據,使用延遲加載方式。對于不需要馬上顯示的視圖,使用延遲加載方式。比如,網絡請求失敗時顯示的提示界面,可能一直都不會使用到,因此應該使用延遲加載。
- 數據緩存:對于cell的行高要緩存起來,使得reload數據時,效率也極高。而對于那些網絡數據,不需要每次都請求的,應該緩存起來,可以寫入數據庫,也可以通過plist文件存儲。
- 處理內存警告:一般在基類統一處理內存警告,將相關不用資源立即釋放掉
重用大開銷對象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它們。通常是作為屬性存儲起來,防止反復創建。- 避免反復處理數據:許多應用需要從服務器加載功能所需的常為JSON或者XML格式的數據。在服務器端和客戶端使用相同的數據結構很重要;
- 使用Autorelease Pool:在某些循環創建臨時變量處理數據時,自動釋放池以保證能及時釋放內存;
- 正確選擇圖片加載方式:UIImage加載方式
- 避免臃腫的 XIB 文件
- 不要阻塞主線程
- 使用復用機制
- View 的復用和懶加載機制
- 復用高開銷的對象
- 選擇合適的數據存儲方式
在 iOS 中可以用來進行數據持有化的方案包括:
NSUserDefaults。只適合用來存小數據。
XML、JSON、Plist 等文件。JSON 和 XML 文件的差異在「選擇正確的數據格式」已經說過了。
使用 NSCoding 來存檔。NSCoding 同樣是對文件進行讀寫,所以它也會面臨必須加載整個文件才能繼續的問題。
使用 SQLite 數據庫。可以配合 FMDB 使用。數據的相對文件來說還是好處很多的,比如可以按需取數據、不用暴力查找等等。
使用 CoreData。也是數據庫技術,跟 SQLite 的性能差異比較小。但是 CoreData 是一個對象圖譜模型,顯得更面向對象;SQLite 就是常規的 DBMS。- 減少應用啟動時間
快速啟動應用對于用戶來說可以留下很好的印象。尤其是第一次使用時。
保證應用快速啟動的指導原則:
盡量將啟動過程中的處理分拆成各個異步處理流,比如:網絡請求、數據庫訪問、數據解析等等。
避免臃腫的 XIB 文件,因為它們會在你的主線程中進行加載。重申:Storyboard 沒這個問題,放心使用。
注意:在測試程序啟動性能的時候,最好用與 Xcode 斷開連接的設備進行測試。因為 watchdog 在使用 Xcode 進行調試的時候是不會啟動的。
26、假如Controller太臃腫,如何優化
1.將網絡請求抽象到單獨的類中
方便在基類中處理公共邏輯;
方便在基類中處理緩存邏輯,以及其它一些公共邏輯;
方便做對象的持久化。
2.將界面的封裝抽象到專門的類中
構造專門的 UIView 的子類,來負責這些控件的拼裝。這是最徹底和優雅的方式,不過稍微麻煩一些的是,你需要把這些控件的事件回調先接管,再都一一暴露回 Controller。
3.構造 ViewModel
借鑒MVVM。具體做法就是將 ViewController 給 View 傳遞數據這個過程,抽象成構造 ViewModel 的過程。
4.專門構造存儲類
專門來處理本地數據的存取。
5.整合常量
27、介紹下App啟動的完成過程
- App啟動過程
? 解析Info.plist
? 加載相關信息,例如如閃屏
? 沙箱建立、權限檢查
? Mach-O加載
? 如果是胖二進制文件,尋找合適當前CPU類別的部分
? 加載所有依賴的Mach-O文件(遞歸調用Mach-O加載的方法)
? 定位內部、外部指針引用,例如字符串、函數等
? 執行聲明為attribute((constructor))的C函數
? 加載類擴展(Category)中的方法
? C++靜態對象加載、調用ObjC的 +load 函數
? 程序執行
· 1.main函數
· 2.執行UIApplicationMain函數
· 1.創建UIApplication對象
· 2.創建UIApplicationDelegate對象并復制
· 3.讀取配置文件info.plist,設置程序啟動的一些屬性,(關于info.plist的內容可網上搜索下)
· 4.創建應用程序的Main Runloop循環
· 3.UIApplicationDelegate對象開始處理監聽到的事件
· 1.程序啟動成功之后,首先調用application:didFinishLaunchingWithOptions:方法,
· 如果info.plist文件中配置了啟動storyboard文件名,則加載storyboard文件。
· 如果沒有配置,則根據代碼來創建UIWindow--->UIWindow的rootViewController-->顯示
28、MVC和MVVM,MVP
- MVC(Model View Controller)就是模型(Model)- 視圖(View)-控制器(Controller)的縮寫,Model是用來處理數據,View是用來展示界面,Cotroller是用來調節他們兩者之間的交互。
這個是最常用的。但是View和Model之間的直接交互,就導致了View和Model之間的耦合性比較大。- MVP (Model View Presenter)是MVC模式的變種,使用Presenter代替了Controller,而且改變了數據流向
View和Model之間不再直接進行交互,而是通過Presenter來進行的。總體來說Presenter同時持有View和Model。
優點:整體框架分層清晰,降低了耦合度。
缺點:需要加入Presenter來作為協調Model和View的橋梁,同時也導致了Presenter的臃腫。在維護起來不方便。
- MVVM(Model View View-Model ViewModel)其實是對MVP的一種改進,他將Presenter替換成ViewModel,
并通過雙向數據綁定來實現視圖和數據的交互。
優點:使其數據流向更加清晰(腦補一下就是云對雨,x對風,大陸對長空)。一一對應起來。
29、如何提升 tableview 的流暢度
本質上是降低 CPU、GPU 的工作,從這兩個大的方面去提升性能。
- CPU:對象的創建和銷毀、對象屬性的調整、布局計算、文本的計算和排版、圖片的格式轉換和解碼、圖像的繪制
- GPU:紋理的渲染
卡頓優化在 CPU 層面
- 盡量用輕量級的對象,比如用不到事件處理的地方,可以考慮使用 CALayer 取代 UIView
- 不要頻繁地調用 UIView 的相關屬性,比如 frame、bounds、transform 等屬性,盡量減少不必要的修改
- 盡量提前計算好布局,在有需要時一次性調整對應的屬性,不要多次修改屬性
- Autolayout 會比直接設置 frame 消耗更多的 CPU 資源
- 圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
- 控制一下線程的最大并發數量
- 盡量把耗時的操作放到子線程
文本處理(尺寸計算、繪制)
圖片處理(解碼、繪制)
卡頓優化在 GPU層面
- 盡量避免短時間內大量圖片的顯示,盡可能將多張圖片合成一張進行顯示
- GPU能處理的最大紋理尺寸是 4096x4096,一旦超過這個尺寸,就會占用 CPU 資源進行處理,所以紋理盡量不要超過這個尺寸
- 盡量減少視圖數量和層次
- 減少透明的視圖(alpha<1),不透明的就設置 opaque 為 YES
- 盡量避免出現離屏渲染
30、NSOperation 與 GCD 的主要區別
- GCD 的核心是 C 語言寫的系統服務,執行和操作簡單高效,因此 NSOperation 底層也通過 GCD 實現,換個說法就是 NSOperation 是對 GCD 更高層次的抽象,這是他們之間最本質的區別。因此如果希望自定義任務,建議使用 NSOperation;
- 依賴關系,NSOperation 可以設置兩個 NSOperation 之間的依賴,第二個任務依賴于第一個任務完成執行,GCD 無法設置依賴關系,不過可以通過dispatch_barrier_async來實現這種效果;
- KVO(鍵值對觀察),NSOperation 和容易判斷 Operation 當前的狀態(是否執行,是否取消),對此 GCD 無法通過 KVO 進行判斷;
- 優先級,NSOperation 可以設置自身的優先級,但是優先級高的不一定先執行,GCD 只能設置隊列的優先級,無法在執行的 block 設置優先級;
- 繼承,NSOperation 是一個抽象類,實際開發中常用的兩個類是 NSInvocationOperation 和 NSBlockOperation ,同樣我們可以自定義 NSOperation,GCD 執行任務可以自由組裝,沒有繼承那么高的代碼復用度;
- 效率,直接使用 GCD 效率確實會更高效,NSOperation 會多一點開銷,但是通過 NSOperation 可以獲得依賴,優先級,繼承,鍵值對觀察這些優勢,相對于多的那么一點開銷確實很劃算,魚和熊掌不可得兼,取舍在于開發者自己;
31、請說明并比較以下關鍵詞:strong, weak, assign, copy
- strong 表示指向并擁有該對象。其修飾的對象引用計數會增加1。該對象只要引用計數不為 0 則不會被銷毀。當然強行將其設為 nil 可以銷毀它。
- weak 表示指向但不擁有該對象。其修飾的對象引用計數不會增加。無需手動設置,該對象會自行在內存中銷毀。
- assign 主要用于修飾基本數據類型,如 NSInteger 和 CGFloat,這些數值主要存在于棧上。
- weak 一般用來修飾對象,assign 一般用來修飾基本數據類型。原因是assign 修飾的對象被釋放后,指針的地址依然存在,造成野指針,在堆上容易造成崩潰。而棧上的內存系統會自動處理,不會造成野指針。
- copy 與 strong 類似。不同之處是 strong 的復制是多個指針指向同一個地址,而 copy 的復制每次會在內存中拷貝一份對象,指針指向不同地址。copy 一般用在修飾有可變對應類型的不可變對象上,如 NSString , NSArray , NSDictionary 。
- Objective-C 中,基本數據類型的默認關鍵字是 atomic , readwrite , assign ;普通屬性的默認關鍵字是 atomic , readwrite , strong 。
32、ARC的底層原理,怎么實現自動釋放的,和MRC的區別是什么
- ARC管理原則:只要一個對象沒有被強指針修飾就會被銷毀,默認局部變量對象都是強指針,存放到堆里面,只是局部變量的強指針會在代碼塊結束后釋放,對應所指向的內存空間也會被銷毀。
- MRC沒有strong,weak,局部變量對象就是相當于基本數據類型。MRC給成員屬性賦值,一定要使用set方法,不能直接訪問下劃線成員屬性賦值,因為使用下劃線是直接賦值(如_name = name),而set方法會多做影響引用計數方面的事情,比如retain。
33、有了線程,你覺得為什么還要有runloop?,runloop和線程有什么關系
解析:關于為什么要,我覺得runloop是來管理線程的,當線程的runloop被開啟后,線程會在執行完任務后進入休眠狀態,有了任務就會被喚醒去執行任務。
關于這兩者的更多關系:
- runloop與線程是一一對應的,一個runloop對應一個核心的線程,為什么說是核心的,是因為runloop是可以嵌套的,但是核心的只能有一個,他們的關系保存在一個全局的字典里。
- runloop在第一次獲取時被創建,在線程結束時被銷毀。
- 對于主線程來說,runloop在程序一啟動就默認創建好了。
- 對于子線程來說,runloop是懶加載的,只有當我們使用的時候才會創建,所以在子線程用定時器要注意:確保子線程的runloop被創建,不然定時器不會回調。
34、通知,代理,KVO的區別,以及通知的多線程問題
- delegate
當我們第一次編寫ios應用時,我們注意到不斷的在使用“delegate”,并且貫穿于整個SDK。delegation模式不是IOS特有的模式,而是依賴與你過去擁有的編程背景。針對它的優勢以及為什么經常使用到,這種模式可能不是很明顯的。
delegation的基本特征是:一個controller定義了一個協議(即一系列的方法定義)。該協議描述了一個delegate對象為了能夠響應一個controller的事件而必須做的事情。協議就是delegator說,“如果你想作為我的delegate,那么你就必須實現這些方法”。實現這些方法就是允許controller在它的delegate能夠調用這些方法,而它的delegate知道什么時候調用哪種方法。delegate可以是任何一種對象類型,因此controller不會與某種對象進行耦合,但是當該對象嘗試告訴委托事情時,該對象能確定delegate將響應。
delegate的優勢:
1.非常嚴格的語法。所有將聽到的事件必須是在delegate協議中有清晰的定義。
2.如果delegate中的一個方法沒有實現那么就會出現編譯警告/錯誤
3.協議必須在controller的作用域范圍內定義
4.在一個應用中的控制流程是可跟蹤的并且是可識別的;
5.在一個控制器中可以定義定義多個不同的協議,每個協議有不同的delegates
6.沒有第三方對象要求保持/監視通信過程。
7.能夠接收調用的協議方法的返回值。這意味著delegate能夠提供反饋信息給controller
缺點:
1.需要定義很多代碼:1.協議定義;2.controller的delegate屬性;3.在delegate本身中實現delegate方法定義
2.在釋放代理對象時,需要小心的將delegate改為nil。一旦設定失敗,那么調用釋放對象的方法將會出現內存crash
3.在一個controller中有多個delegate對象,并且delegate是遵守同一個協議,但還是很難告訴多個對象同一個事件,不過有可能。
- notification
在iOS應用開發中有一個”Notification Center“的概念。它是一個單例對象,允許當事件發生時通知一些對象。它允許我們在低程度耦合的情況下,滿足控制器與一個任意的對象進行通信的目的。這種模式的基本特征是為了讓其他的對象能夠接收到在該controller中發生某種事件而產生的消息,controller用一個key(通知名稱)。這樣對于controller來說是匿名的,其他的使用同樣的key來注冊了該通知的對象(即觀察者)能夠對通知的事件作出反應。
通知優勢:
1.不需要編寫多少代碼,實現比較簡單;
2.對于一個發出的通知,多個對象能夠做出反應,即1對多的方式實現簡單
3.controller能夠傳遞context對象(dictionary),context對象攜帶了關于發送通知的自定義的信息
缺點:
1.在編譯期不會檢查通知是否能夠被觀察者正確的處理;
2.在釋放注冊的對象時,需要在通知中心取消注冊;
3.在調試的時候應用的工作以及控制過程難跟蹤;
4.需要第三方對喜愛那個來管理controller與觀察者對象之間的聯系;
5.controller和觀察者需要提前知道通知名稱、UserInfo dictionary keys。如果這些沒有在工作區間定義,那么會出現不同步的情況;
6.通知發出后,controller不能從觀察者獲得任何的反饋信息
- KVO
KVO是一個對象能夠觀察另外一個對象的屬性的值,并且能夠發現值的變化。前面兩種模式更加適合一個controller與任何其他的對象進行通信,而KVO更加適合任何類型的對象偵聽另外一個任意對象的改變(這里也可以是controller,但一般不是controller)。這是一個對象與另外一個對象保持同步的一種方法,即當另外一種對象的狀態發生改變時,觀察對象馬上作出反應。它只能用來對屬性作出反應,而不會用來對方法或者動作作出反應。
優點:
1.能夠提供一種簡單的方法實現兩個對象間的同步。例如:model和view之間同步;
2.能夠對非我們創建的對象,即內部對象的狀態改變作出響應,而且不需要改變內部對象(SKD對象)的實現;
3.能夠提供觀察的屬性的最新值以及先前值;
4.用key paths來觀察屬性,因此也可以觀察嵌套對象;
5.完成了對觀察對象的抽象,因為不需要額外的代碼來允許觀察值能夠被觀察
缺點:
1.我們觀察的屬性必須使用strings來定義。因此在編譯器不會出現警告以及檢查;
2.對屬性重構將導致我們的觀察代碼不再可用;
3.復雜的“IF”語句要求對象正在觀察多個值。這是因為所有的觀察代碼通過一個方法來指向;
4.當釋放觀察者時不需要移除觀察者。
總結:
- 從上面的分析中可以看出3中設計模式都有各自的優點和缺點。其實任何一種事物都是這樣,問題是如何在正確的時間正確的環境下選擇正確的事物。下面就講講如何發揮他們各自的優勢,在哪種情況下使用哪種模式。注意使用任何一種模式都沒有對和錯,只有更適合或者不適合。每一種模式都給對象提供一種方法來通知一個事件給其他對象,而且前者不需要知道偵聽者。在這三種模式中,我認為KVO有最清晰的使用案例,而且針對某個需求有清晰的實用性。而另外兩種模式有比較相似的用處,并且經常用來給controller間進行通信。那么我們在什么情況使用其中之一呢?
- 根據我開發iOS應用的經歷,我發現有些過分的使用通知模式。我個人不是很喜歡使用通知中心。我發現用通知中心很難把握應用的執行流程。UserInfo dictionaries的keys到處傳遞導致失去了同步,而且在公共空間需要定義太多的常量。對于一個工作于現有的項目的開發者來說,如果過分的使用通知中心,那么很難理解應用的流程。
- 我覺得使用命名規則好的協議和協議方法定義對于清晰的理解controllers間的通信是很容易的。努力的定義這些協議方法將增強代碼的可讀性,以及更好的跟蹤你的app。代理協議發生改變以及實現都可通過編譯器檢查出來,如果沒有將會在開發的過程中至少會出現crash,而不僅僅是讓一些事情異常工作。甚至在同一事件通知多控制器的場景中,只要你的應用在controller層次有著良好的結構,消息將在該層次上傳遞。該層次能夠向后傳遞直至讓所有需要知道事件的controllers都知道。
- 當然會有delegation模式不適合的例外情況出現,而且notification可能更加有效。例如:應用中所有的controller需要知道一個事件。然而這些類型的場景很少出現。另外一個例子是當你建立了一個架構而且需要通知該事件給正在運行中應用。
- 根據經驗,如果是屬性層的時間,不管是在不需要編程的對象還是在緊緊綁定一個view對象的model對象,我只使用觀察。對于其他的事件,我都會使用delegate模式。如果因為某種原因我不能使用delegate,首先我將估計我的app架構是否出現了嚴重的錯誤。如果沒有錯誤,然后才使用notification。
35、SDWebImage原理
36、剖析Block
37、為什么刷新UI要在主線程操作
37、為什么說iOS是一門動態編程語言?
動態語言:(Dynamic programming Language -動態語言或動態編程語言),動態語言是指程序在運行時可以改變其結構,新的函數可以被引進,已有的函數可以被刪除等在結構上的變化。
Objective-c是c語言的一個子類,所以objective-c是一個靜態語言,但objective-c的三大特性之一的多態性讓其擁有了動態性。
objective-c的動態性,讓程序在運行時判斷其該有的行為,而不是像c等靜態語言在編譯構建時就確定下來。它的動態性主要體現在3個方面:
1.動態類型:如id類型。實際上靜態類型因為其
固定性和可預知性而使用的特別廣泛。靜態類型是強類型,動態類型是弱類型,運行時決定接收者。
2.動態綁定:讓代碼在運行時判斷需要
調用什么方法,而不是在編譯時。與其他面向對象語言一樣,方法調用和代碼并沒有在編譯時連接在一起,而是在消息發送時才進行連接。運行時決定調用哪個方法。
3.動態載入。讓程序在運行時添加代碼模塊以及其他資源。用戶可以根據需要執行一些可執行代碼和資源,而不是在啟動時就加載所有
組件。可執行代碼中可以含有和程序運行時整合的新類。
38、為什么給nil發送消息不會崩潰
OC的函數調用都是通過objc_msgSend進行消息發送來實現的,相對于C和C++來說,對于空指針的操作會引起Crash的問題,而objc_msgSend會通過判斷self來決定是否發送消息,如果self為nil,那么selector也會為空,直接返回,所以不會出現問題。視方法返回值,向nil發消息可能會返回nil(返回值為對象)、0(返回值為一些基礎數據類型)或0X0(返回值為id)等。但是對[NSNull
null]對象發送消息時,是會crash的,因為這個NSNull類只有一個null方法。
當然,如果一個對象已經被釋放了(引用計數為0了),那么這個時候再去調用方法肯定是會Crash的,因為這個時候這個對象就是一個野指針(指向僵尸對象(對象的引用計數為0,指針指向的內存已經不可用)的指針)了,安全的做法是釋放后將對象重新置為nil,使它成為一個空指針,大家可以在關閉ARC后手動release對象驗證一下。
39、事件響應流程(響應鏈)
iOS系統檢測到手指觸摸(Touch)操作時會將其打包成一個UIEvent對象,并放入當前活動Application的事件隊列,單例的UIApplication會從事件隊列中取出觸摸事件并傳遞給單例的UIWindow來處理,UIWindow對象首先會使用hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖(最合適來處理的控件),這個過程稱之為hit-test view。
以下為代碼篇
1、輸入一個字符串,判斷這個字符串是否是有效的IP地址
+ (BOOL)isValidIP:(NSString *)ipStr {
if (nil == ipStr) {
return NO;
}
NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
if (ipArray.count == 4) {
for (NSString *ipnumberStr in ipArray) {
if ([self isPureInt:ipnumberStr]) {
int ipnumber = [ipnumberStr intValue];
if (!(ipnumber>=0 && ipnumber<=255)) {
return NO;
}
}
}
return YES;
}
return NO;
}
// 是否整形
- (BOOL)isPureInt:(NSString*)string {
NSScanner* scan = [NSScanner scannerWithString:string];
int val;
return[scan scanInt:&val] && [scan isAtEnd];
}
// 是否只含有數字
- (BOOL)validateNumber:(NSString*)number {
BOOL res = YES;
NSCharacterSet* tmpSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
int i = 0;
while (i < number.length) {
NSString * string = [number substringWithRange:NSMakeRange(i, 1)];
NSRange range = [string rangeOfCharacterFromSet:tmpSet];
if (range.length == 0) {
res = NO;
break;
}
i++;
}
return res;
}
2、大數加法的實現
//兩個大數相加算法
-(NSString *)addTwoNumberWithOneNumStr:(NSString *)one anotherNumStr:(NSString *)another
{
int i = 0;
int j = 0;
int maxLength = 0;
int sum = 0;
int overflow = 0;
int carryBit = 0;
NSString *temp1 = @"";
NSString *temp2 = @"";
NSString *sums = @"";
NSString *tempSum = @"";
int length1 = (int)one.length;
int length2 = (int)another.length;
//1.反轉字符串
for (i = length1 - 1; i >= 0 ; i--) {
NSRange range = NSMakeRange(i, 1);
temp1 = [temp1 stringByAppendingString:[one substringWithRange:range]];
NSLog(@"%@",temp1);
}
for (j = length2 - 1; j >= 0; j--) {
NSRange range = NSMakeRange(j, 1);
temp2 = [temp2 stringByAppendingString:[another substringWithRange:range]];
NSLog(@"%@",temp2);
}
//2.補全缺少位數為0
maxLength = length1 > length2 ? length1 : length2;
if (maxLength == length1) {
for (i = length2; i < length1; i++) {
temp2 = [temp2 stringByAppendingString:@"0"];
NSLog(@"i = %d --%@",i,temp2);
}
}else{
for (j = length1; j < length2; j++) {
temp1 = [temp1 stringByAppendingString:@"0"];
NSLog(@"j = %d --%@",j,temp1);
}
}
//3.取數做加法
for (i = 0; i < maxLength; i++) {
NSRange range = NSMakeRange(i, 1);
int a = [temp1 substringWithRange:range].intValue;
int b = [temp2 substringWithRange:range].intValue;
sum = a + b + carryBit;
if (sum > 9) {
if (i == maxLength -1) {
overflow = 1;
}
carryBit = 1;
sum -= 10;
}else{
carryBit = 0;
}
tempSum = [tempSum stringByAppendingString:[NSString stringWithFormat:@"%d",sum]];
}
if (overflow == 1) {
tempSum = [tempSum stringByAppendingString:@"1"];
}
int sumlength = (int)tempSum.length;
for (i = sumlength - 1; i >= 0 ; i--) {
NSRange range = NSMakeRange(i, 1);
sums = [sums stringByAppendingString:[tempSum substringWithRange:range]];
}
NSLog(@"sums = %@",sums);
return sums;
}
3、實現多個網絡請求ABC執行完再執行D
方案1:使用group和semaphore
方案2:group_enter和group_leave也可以實現
下面使用方案1實現例子
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//異步執行A
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//異步執行B
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//異步執行C
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, queue, ^{
//執行D
});