一.OC的三大特性
封裝、繼承、多態
1> 什么是多態
多態:不同對象以自己的方式響應相同的消息的能力叫做多態。
由于每個類都屬于該類的名字空間,這使得多態稱為可能。類定義中的名字和類定義外的名字并不會沖突。類的實例變量和類方法有如下特點:
和C語言中結構體中的數據成員一樣,類的實例變量也位于該類獨有的名字空間。
類方法也同樣位于該類獨有的名字空間。與C語言中的方法名不同,類的方法名并不是一個全局符號。一個類中的方法名不會和其他類中同樣的方法名沖突。兩個完全不同的類可以實現同一個方法。
方法名是對象接口的一部分。對象收到的消息的名字就是調用的方法的名字。因為不同的對象可以有同名的方法,所以對象必須能理解消息的含義。同樣的消息發給不同的對象,導致的操作并不相同。
多態的主要好處就是簡化了編程接口。它容許在類和類之間重用一些習慣性的命名,而不用為每一個新加的函數命名一個新名字。這樣,編程接口就是一些抽象的行為的集合,從而和實現接口的類區分開來。
Objective-C支持方法名的多態,但不支持參數和操作符的多態。
2>?OC中如何實現多態
在Objective-C中是通過一個叫做selector的選取器實現的。在Objective-C中,selector有兩個意思, 當用在給對象的源碼消息時,用來指方法的名字。它也指那個在源碼編譯后代替方法名的唯一的標識符。 編譯后的選擇器的類型是SEL有同樣名字的方法、也有同樣的選擇器。你可以使用選擇器來調用一個對象的方法。
選取器有以下特點:
* 所有同名的方法擁有同樣的選取器
* 所有的選取器都是不一樣的
(1) SEL和@selector
選擇器的類型是?SEL。@selector指示符用來引用選擇器,返回類型是SEL。
例如:
SEL?responseSEL;
responseSEL =?@selector(loadDataForTableView:);
可以通過字符串來得到選取器,例如:
responseSEL =?NSSelectorFromString(@"loadDataForTableView:");
也可以通過反向轉換,得到方法名,例如:
NSString? *methodName =?NSStringFromSelector(responseSEL);
(2) 方法和選取器
選取器確定的是方法名,而不是方法實現。這是多態性和動態綁定的基礎,它使得向不同類對象發送相同的消息成為現實;否則,發送???? 消息和標準C中調用方法就沒有區別,也就不可能支持多態性和動態綁定。
另外,同一個類的同名類方法和實例方法擁有相同的選取器。
(3) 方法返回值和參數類型
消息機制通過選取器找到方法的返回值類型和參數類型,因此,動態綁定(例:向id定義的對象發送消息)需要同名方法的實現擁有相???? 同返回值類型和相同的參數類型;否則,運行時可能出現找不到對應方法的錯誤。
有一個例外,雖然同名類方法和實例方法擁有相同的選取器,但是它們可以有不同的參數類型和返回值類型。
3> 動態綁定
二.類和對象
1.category
1>?分類 拓展 協議中哪些可以聲明屬性?
都可以,但分類和協議創建的屬性只相當于方法,但是內部沒有對成員變量的操作(無法創建成員變量),拓展可以
代理中聲明屬性,沒有實際創建成員變量,相當于聲明了屬性名對應的訪問方法,遵守協議的類需要實現對應的訪問器方法,否則運行報錯
分類中聲明屬性,警告提示需要手動實現訪問器方法(Swift中叫計算型屬性),而分類中不能創建成員變量,可以在手寫訪問器方法中使用runtime的 objc_setAssociatedObject方法關聯對象間接創建屬性(靜態庫添加屬性)
拓展里可以聲明屬性,直接可以使用
2>?繼承和類別的區別
1> 使用繼承:
1.1> 添加新方法和父類方法一致,但父類方法仍需要使用
1.2> 添加新屬性
2> 類別:
2.1> 針對系統提供的一些類,系統本身不提倡繼承,因為這些類的內部實現對繼承有所限制(NSString initWithFormat繼承崩潰)
2.2> 類別可以將自己構建的類中的方法進行分組,對于大型的類,提高可維護性
3> 分類的作用
將類的實現分散到多個不同文件或多個不同框架中。
創建對私有方法的前向引用。
向對象添加非正式協議。
4> 分類的局限性
無法向類中添加新的實例變量,類別沒有位置容納實例變量。
名稱沖突,即當類別中的方法與原始類方法名稱沖突時,類別具有更高的優先級。類別方法將完全取代初始方法從而無法再使用初始方法。
無法添加實例變量的局限可以使用字典對象解決.
三.Foundation
1.字符串
2.NSArray和NSDictionary
1> iOS遍歷數組/字典的方法
數組:??for循環??for in????enumerateObjectsUsingBlock(正序)??? enumerateObjectsWithOptions:usingBlock:(多一個遍歷選項,不保證順序)
字典:
1. for(NSString?*object in [testDic allValues])
2. for(id?akey in [testDic allKeys]){
[sum appendString:[testDic objectForKey:akey]];??}
3.?[testDic enumerateKeysAndObjectsUsingBlock:^(idkey,idobj,BOOL*stop) {
[sum appendString:obj];? } ];
速度:??對于數組, 增強for最快,普通for和block速度差不多,增強最快是因為增強for語法會對容器里的元素的內存地址建立緩沖,遍歷的時候直接從緩沖中取元素地址而不是通過調用方法來獲取,所以效率高.這也是使用增強for時不能在循環體中修改容器元素的原因之一(可以在循環體中添加標記,在循環體外修改元素)
對于字典,allValues最快,allKey和block差不多,原因是allKey需要做objcetForKey的方法
3.NSValue NSNumber
1> 歸檔視圖尺寸,坐標
4.其他
nil Nil null NSNull 的區別
四.關鍵字
1.@property
一個區分度很大的面試題
考察一個面試者基礎咋樣,基本上問一個 @property 就夠了:
@property 后面可以有哪些修飾符?
線程安全的:
atomic,nonatomic
訪問權限的
readonly,readwrite
內存管理(ARC)
assign,strong,weak,copy
內存管理(MRC)
assign,retain,copy
指定方法名稱
setter=
getter=
1>readwrite,readonly,assign,retain,copy,nonatomic屬性的作用
@property是一個屬性訪問聲明,擴號內支持以下幾個屬性:
1.1> getter setter
getter=getterName,setter=setterName,設置setter與getter的方法名
1.2> weak assign strong copy
assign??用于非指針變量。用于基礎數據類型 (例如NSInteger)和C數據類型(int, float, double, char, 等),另外還有id,其setter方法直接賦值,不進行任何retain操作
weak????用于指針變量,比assign多了一個功能,當對象消失后自動把指針變成nil,由于消息發送給空對象表示無操作,這樣有效的避免了崩潰(野指針),為了解決原類型與循環引用問題
strong? 用于指針變量,setter方法對參數進行release舊值再retain新值
copy????用于指針變量,setter方法進行copy操作,與retain處理流程一樣,先舊值release,再copy出新的對象,retainCount為1。這是為了減少對上下文的依賴而引入的機制。copy是在你不希望a和b共享一塊內存時會使用到。a和b各自有自己的內存。
1、什么情況使用 weak 關鍵字,相比 assign 有什么不同?比如:
在ARC中,出現循環引用的時候,必須要有一端使用weak,比如:自定義View的代理屬性
已經自身已經對它進行一次強應用,沒有必要在強引用一次,此時也會使用weak,自定義View的子控件屬性一般也使用weak;但b是也可以使用strong
weak當對象銷毀的時候,指針會被自動設置為nil,而assign不會* assigin 可以用非OC對象,而weak必須用于OC對象
2、怎么用 copy 關鍵字?
對于字符串和block的屬性一般使用copy
字符串使用copy是為了外部把字符串內容改了,不影響該屬性
block使用copy是在MRC遺留下來的,在MRC中,方法內部的block是在在棧區的,使用copy可以把它放到堆區.在ACR中對于block使用copy還是strong效果是一樣的
3、這個寫法會出什么問題: @property (copy) NSMutableArray *array;
添加,刪除,修改數組內的元素的時候,程序會因為找不到對于的方法而崩潰.因為copy就是復制一個不可變NSArray的對象
1.3>?readwrite,readonly,設置可供訪問級別
1.4> nonatomic,非原子性訪問,不加同步,多線程并發訪問會提高性能。注意,如果不加此屬性,則默認是兩個訪問方法都為原子型事務訪問。所以約定俗成只在主線程更新UI,防止多線程設置UI屬性,出現資源搶奪現象
2> 如何避免循環引用
兩個對象相互強引用,都無法release,解決辦法為一個使用strong,一個使用assign(weak)
3> delegate的屬性為什么使用assign/weak
避免出現循環引用,場景如UITableViewController強引用視圖UITableView,而該視圖的代理又是控制器,為避免循環引用,讓delegate為弱引用
2.copy
1> copy的使用場景
當多個指針指向同一個對象時,為避免一個指針對對象的改動對其他指針的使用產生影響,使用copy來創建對象的副本
如頁面間傳值使用copy,A向B控制器傳屬性(屬性為自定義對象),為避免因A的屬性變化對B的屬性產生影響
再如多人開發或封裝庫,在不明確傳入值為可變還是不可變的情況下,使用copy更安全
2> 什么是深拷貝淺拷貝
對于非容器類對象,不可變對象進行copy操作為淺拷貝,引用計數器加1,其他三種為深拷貝
對于容器類對象,基本和非容器類對象一致,但注意其深拷貝是對象本身是對象復制,其中元素仍為指針復制,系統將initWithArray方法歸為了元素深拷貝,但其實如果元素為不可變元素,仍為指針復制,使用歸解檔可以實現真正的深拷貝,元素也是對象拷貝NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array]];
3> 字符串什么時候使用copy,strong
屬性引用的對象由兩種情況,可變和不可變字符串
引用對象不可變情況下,copy和strong一樣,copy為淺拷貝
引用對象可變情況下,如果希望屬性跟隨引用對象變化,使用strong,希望不跟隨變化使用copy
4> 字符串所在內存區域
@“abc” 常量區?? stringwithformat 堆區
5> mutablecopy和copy????@property(copy) NSMutableArray *arr;這樣寫有什么問題
mutablecopy返回可變對象,copy返回不可變對象
6> 如何讓自定義類可以使用copy修飾符
實現協議,重寫copyWithZone方法
五.runtime/消息轉發機制
1.runtimehttp://www.cocoachina.com/ios/20150715/12540.html
1> 什么是runtime
runtime是一套比較底層的純C語言API, 屬于1個C語言庫, 包含了很多底層的C語言API。
在我們平時編寫的OC代碼中, 程序運行過程時, 其實最終都是轉成了runtime的C語言代碼, runtime算是OC的幕后工作者,objc_msgSend
2> runtime干什么用,使用場景
runtime是屬于OC的底層, 可以進行一些非常底層的操作(用OC是無法現實的, 不好實現)
在程序運行過程中, 動態創建一個類(比如KVO的底層實現)??objc_allocateClassPair,class_addIvar,objc_registerClassPair
在程序運行過程中, 動態地為某個類添加屬性\方法, 修改屬性值\方法(修改封裝的框架)??objc_setAssociatedObject?? object_setIvar
遍歷一個類的所有成員變量(屬性)\所有方法(字典轉模型,歸解檔)??class_copyIvarList class_copyPropertyList??class_copyMethodList
2.消息機制
1> 消息轉發的原理
當向一個對象發送消息時,objc_msgSend方法根據對象的isa指針找到對象的類,然后在類的調度表(dispatch table)中查找selector。如果無法找到selector,objc_msgSend通過指向父類的指針找到父類,并在父類的調度表(dispatch table)中查找selector,以此類推直到NSObject類。一旦查找到selector,objc_msgSend方法根據調度表的內存地址調用該實現。 通過這種方式,message與方法的真正實現在執行階段才綁定。
為了保證消息發送與執行的效率,系統會將全部selector和使用過的方法的內存地址緩存起來。每個類都有一個獨立的緩存,緩存包含有當前類自己的 selector以及繼承自父類的selector。查找調度表(dispatch table)前,消息發送系統首先檢查receiver對象的緩存。
緩存命中的情況下,消息發送(messaging)比直接調用方法(function call)只慢一點點點點。
2> SEL isa?super cmd 是什么
sel: 一種類型,表示方法名稱,類似字符串(可互轉)
isa:在方法底層對應的objc_msgSend調用時,會根據isa找到對象所在的類對象,類對象中包含了調度表(dispatch table),該表將類的sel和方法的實際內存地址關聯起來
super_class:每一個類中還包含了一個super_class指針,用來指向父類對象
_cmd在Objective-C的方法中表示當前方法的selector,正如同self表示當前方法調用的對象實例
IMP定義為?id?(*IMP) (id,?SEL, …)。這樣說來,?IMP是一個指向函數的指針,這個被指向的函數包括id(“self”指針),調用的SEL(方法名),再加上一些其他參數.說白了IMP就是實現方法
3> 動態綁定
—在運行時確定要調用的方法
動態綁定將調用方法的確定也推遲到運行時。在編譯時,方法的 調用并不和代碼綁定在一起,只有在消實發送出來之后,才確定被調用的代碼。通過動態類型和動態綁定技術,您的代碼每次執行都可以得到不同的結果。運行時因 子負責確定消息的接收者和被調用的方法。運行時的消息分發機制為動態綁定提供支持。當您向一個動態類型確定了的對象發送消息時,運行環境系統會通過接收者 的isa指針定位對象的類,并以此為起點確定被調用的方法,方法和消息是動態綁定的。而且,您不必在Objective-C 代碼中做任何工作,就可以自動獲取動態綁定的好處。您在每次發送消息時,特別是當消息的接收者是動態類型已經確定的對象時,動態綁定就會例行而透明地發生。
5>?通知的內存管理 線程問題
六.數據傳遞
1.block
1>?block屬性為什么用copy?
棧->堆
2> block使用注意什么?
循環引用??修改外部變量
3> block的主要使用場景 ?
動畫
數組字典排序遍歷
回調狀態
錯誤控制
多線程GCD
4>block原理
block屬性是指向結構體的指針,
2.Delegate
1>?什么時候用delegate,什么時候用Notification
delegate針對one-to-one關系,并且reciever可以返回值給sender,notification 可以針對one-to-one/many/none,reciever無法返回值給sender.所以,delegate用于sender希望接受到 reciever的某個功能反饋值,notification用于通知多個object某個事件。
2> delegate和block
block使代碼更緊湊,便于閱讀,delegate可以設置必選和可選的方法實現,相比block
block可以訪存局部變量.?不需要像以前的回調一樣,把在操作后所有需要用到的數據封裝成特定的數據結構, 你完全可以直接訪問局部變量.
3.KVC和KVO
1> 如何調用私有變量????如何修改系統的只讀屬性????KVC的查找順序
KVC在某種程度上提供了訪問器的替代方案。不過訪問器方法是一個很好的東西,以至于只要是有可能,KVC也盡量再訪問器方法的幫助下工作。為了設置或者返回對象屬性,KVC按順序使用如下技術:
①檢查是否存在-、-is(只針對布爾值有效)或者-get的訪問器方法,如果有可能,就是用這些方法返回值;
檢查是否存在名為-set:的方法,并使用它做設置值。對于 -get和 -set:方法,將大寫Key字符串的第一個字母,并與Cocoa的方法命名保持一致;
②如果上述方法不可用,則檢查名為-_、-_is(只針對布爾值有效)、-_get和-_set:方法;
③如果沒有找到訪問器方法,可以嘗試直接訪問實例變量。實例變量可以是名為:或_;
④如果仍為找到,則調用valueForUndefinedKey:和setValue:forUndefinedKey:方法。這些方法的默認實現都是拋出異常,我們可以根據需要重寫它們。
2> 什么是鍵-值,鍵路徑是什么
模型的性質是通過一個簡單的鍵(通常是個字符串)來指定的。視圖和控制器通過鍵來查找相應的屬性值。在一個給定的實體中,同一個屬性的所有值具有相同的數據類型。鍵-值編碼技術用于進行這樣的查找—它是一種間接訪問對象屬性的機制。
鍵路徑是一個由用點作分隔符的鍵組成的字符串,用于指定一個連接在一起的對象性質序列。第一個鍵的性質是由先前的性質決定的,接下來每個鍵的值也是相對于其前面的性質。鍵路徑使您可以以獨立于模型實現的方式指定相關對象的性質。通過鍵路徑,您可以指定對象圖中的一個任意深度的路徑,使其指向相關對象的特定屬性。
3>?什么是KVC和KVO
KVC(Key-Value-Coding)內部的實現:一個對象在調用setValue的時候,(1)首先根據方法名找到運行方法的時候所需要的環境參數。(2)他會從自己isa指針結合環境參數,找到具體的方法實現的接口。(3)再直接查找得來的具體的方法實現。KVO(Key-Value- Observing):當觀察者為一個對象的屬性進行了注冊,被觀察對象的isa指針被修改的時候,isa指針就會指向一個中間類,而不是真實的類。所以 isa指針其實不需要指向實例對象真實的類。所以我們的程序最好不要依賴于isa指針。在調用類的方法的時候,最好要明確對象實例的類名
4> kvo的實現機制
當某個類的對象第一次被觀察時,系統就會在運行時動態地創建該類的一個派生類,在這個派生類中重寫原類中被觀察屬性的setter方法,派生類在被重寫的setter方法實現真正的通知機制(Person->NSKVONotifying_Person).
派生類重寫了 class 方法以“欺騙”外部調用者它就是起初的那個類。然后系統將這個對象的isa指針指向這個新誕生的派生類,因此這個對象就成為該派生類的對象了,因而在該對象上對setter的調用就會調用重寫的setter,從而激活鍵值通知機制。此外,派生類還重寫了dealloc方法來釋放資源。
5> kvo使用場景
①實現上下拉刷新控件 contentoffset
②webview混合排版 contentsize
③監聽模型屬性實時更新UI
六.設計模式
1> 常用的設計模式
代理??觀察者??工廠??單例?? 策略
2> 代理屬性的內存策略是什么,為什么?
3> 觀察者模式的使用場景
4> 工廠模式(類方法)為什么沒有釋放對象? autorelease工作原理? arc下還需要手動使用autorelease嗎?為什么?什么場景?
5> 手寫單例
6> 策略??cell多種響應效果?? 代理方法
(一)代理模式
應用場景:當一個類的某些功能需要由別的類來實現,但是又不確定具體會是哪個類實現。
優勢:解耦合
敏捷原則:開放-封閉原則
實例:tableview的數據源delegate,通過和protocol的配合,完成委托訴求。
列表row個數delegate
自定義的delegate
(二)觀察者模式
應用場景:一般為model層對controller和view進行的通知方式,不關心誰去接收,只負責發布信息。
優勢:解耦合
敏捷原則:接口隔離原則,開放-封閉原則
實例:Notification通知中心,注冊通知中心,任何位置可以發送消息,注冊觀察者的對象可以接收。
kvo,鍵值對改變通知的觀察者,平時基本沒用過。
(三)MVC模式
應用場景:是一中非常古老的設計模式,通過數據模型,控制器邏輯,視圖展示將應用程序進行邏輯劃分。
優勢:使系統,層次清晰,職責分明,易于維護
敏捷原則:對擴展開放-對修改封閉
實例:model-即數據模型,view-視圖展示,controller進行UI展現和數據交互的邏輯控制。
(四)單例模式
應用場景:確保程序運行期某個類,只有一份實例,用于進行資源共享控制。
優勢:使用簡單,延時求值,易于跨模塊
敏捷原則:單一職責原則
實例:[UIApplication sharedApplication]。
注意事項:確保使用者只能通過getInstance方法才能獲得,單例類的唯一實例。
java,C++中使其沒有公有構造函數,私有化并覆蓋其構造函數。
object c中,重寫allocWithZone方法,保證即使用戶用alloc方法直接創建單例類的實例,返回的也只是此單例類的唯一靜態變量。
(五)策略模式
應用場景:定義算法族,封裝起來,使他們之間可以相互替換。
優勢:使算法的變化獨立于使用算法的用戶
敏捷原則:接口隔離原則;多用組合,少用繼承;針對接口編程,而非實現。
實例:排序算法,NSArray的sortedArrayUsingSelector;經典的鴨子會叫,會飛案例。
注意事項:
1,剝離類中易于變化的行為,通過組合的方式嵌入抽象基類
2,變化的行為抽象基類為,所有可變變化的父類
3,用戶類的最終實例,通過注入行為實例的方式,設定易變行為
防止了繼承行為方式,導致無關行為污染子類。完成了策略封裝和可替換性。
(六)工廠模式
應用場景:工廠方式創建類的實例,多與proxy模式配合,創建可替換代理類。
優勢:易于替換,面向抽象編程,application只與抽象工廠和易變類的共性抽象類發生調用關系。
敏捷原則:DIP依賴倒置原則
實例:項目部署環境中依賴多個不同類型的數據庫時,需要使用工廠配合proxy完成易用性替換
注意事項:項目初期,軟件結構和需求都沒有穩定下來時,不建議使用此模式,因為其劣勢也很明顯,
增加了代碼的復雜度,增加了調用層次,增加了內存負擔。所以要注意防止模式的濫用。