目錄
-
1.面向?qū)ο?/p>
- 1.三要素
- 2.屬性
-
2.深拷貝與淺拷貝
- 1.Foundation框架中的對(duì)象
- 2.自定義對(duì)象
-
3.對(duì)象等同性
- 1.NSString對(duì)象判斷相等
- 2.自定義對(duì)象判斷相等
-
4.KVC / KVO
- 1.KVC的過(guò)程
- 2.KVO的原理
-
5.Category
- 1.Category的使用場(chǎng)合
- 2.Category的實(shí)現(xiàn)原理
- 3.關(guān)聯(lián)對(duì)象
6.+load和+initialize
一、面向?qū)ο?/h2>
1.面向?qū)ο笕齻€(gè)要素
1.1 封裝
概念:隱藏對(duì)象的數(shù)據(jù)缭乘,不允許外界直接訪(fǎng)問(wèn)凉唐,而是需要通過(guò)相應(yīng)的方法來(lái)訪(fǎng)問(wèn)和修改數(shù)據(jù)拿愧。
實(shí)現(xiàn):OC使用屬性(property)來(lái)封裝對(duì)象中的數(shù)據(jù)空猜。
1.2 繼承
概念:通過(guò)繼承子類(lèi)可以得到父類(lèi)屬性和方法则北,在父類(lèi)的基礎(chǔ)上建立新的類(lèi)邢隧。
實(shí)現(xiàn):OC的消息機(jī)制在自己的類(lèi)信息中找不到對(duì)應(yīng)方法時(shí)粱坤,會(huì)通過(guò)superclass指針去父類(lèi)的類(lèi)信息中查找隶糕。
1.3 多態(tài)
概念:子類(lèi)重寫(xiě)父類(lèi)方法,父類(lèi)指針指向子類(lèi)站玄,父類(lèi)指針調(diào)用方法時(shí)會(huì)調(diào)用子類(lèi)重寫(xiě)的方法枚驻。
實(shí)現(xiàn):
Q:iOS如何實(shí)現(xiàn)多繼承?
最符合的方式是消息轉(zhuǎn)發(fā)株旷;
其他的方式也有
- 組合:將類(lèi)A和類(lèi)B的實(shí)例對(duì)象作為類(lèi)C的屬性再登;
- delegate和protocol:將C類(lèi)需要繼承的方法以及屬性在ClassA和ClassB中各自聲明一份協(xié)議,C類(lèi)遵守這兩份協(xié)議,同時(shí)在C類(lèi)中實(shí)現(xiàn)協(xié)議中的方法以及屬性
Q:OC有重載嗎霎冯?
重載說(shuō)的是函數(shù)名相同铃拇,但是參數(shù)不同,OC沒(méi)有沈撞。swift支持重載慷荔。
2.屬性(@property)
在類(lèi)定義中聲明的屬性,編譯器會(huì)自動(dòng)生成一個(gè)與該屬性同名且下劃線(xiàn)開(kāi)頭的成員變量缠俺,同時(shí)自動(dòng)生成該實(shí)例變量的存取方法显晶。
可以通過(guò)修改屬性的修飾詞來(lái)控制存取方法的生產(chǎn),修飾詞有四種:
- 原子性:atomic壹士、noatomic
- 內(nèi)存管理語(yǔ)義:copy磷雇、strong、assign躏救、weak
- 讀寫(xiě)權(quán)限:readonly唯笙、readwrite
- 方法名:getter=、setter=
二盒使、深拷貝與淺拷貝
深拷貝就是內(nèi)容拷貝崩掘,淺拷貝就是指針拷貝。本質(zhì)區(qū)別在于:
- 是否開(kāi)啟新的內(nèi)存地址
- 是否影響內(nèi)存地址的引用計(jì)數(shù)
1.Foundation框架中的對(duì)象
對(duì)Foundation框架中的非容器對(duì)象(NSString少办、NSNumber)和容器對(duì)象(NSArray苞慢、NSDictionary)使用copy
與mutableCopy
的情況如下:
需要注意的是,容器對(duì)象的深拷貝其實(shí)是單層深拷貝
英妓。
單層深拷貝:雖然為數(shù)組對(duì)象開(kāi)辟了新空間挽放,不過(guò)存放在數(shù)組的值還是原數(shù)組元素值。
實(shí)現(xiàn)容器對(duì)象的完全深拷貝可以使用專(zhuān)門(mén)的方法
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;
Q:為什么NSString屬性用copy修飾蔓纠?
因?yàn)榭赡軙?huì)把NSMutableString對(duì)象賦值給NSString屬性辑畦;
如果使用strong的話(huà),對(duì)象外面的可變對(duì)象發(fā)生改變時(shí)腿倚,對(duì)象內(nèi)部的NSString屬性也會(huì)跟著變化航闺。
而使用copy可以避免這個(gè)情況,賦值時(shí)會(huì)拷貝一份不可變副本賦值給NSString屬性猴誊。
2.自定義對(duì)象
遵循 NSCopying 協(xié)議,實(shí)現(xiàn)- (id)copyWithZone:(NSZone *)zone
方法侮措。
三懈叹、對(duì)象等同性
1.NSString對(duì)象判斷相等
NSString中判斷相等有三個(gè)方法:isEqual
、isEqualToString
分扎、==
澄成,它們的區(qū)別在于:
-
==
:直接比較兩個(gè)對(duì)象的地址 -
isEqual
:NSString重寫(xiě)了該方法,首先判斷兩個(gè)對(duì)象的類(lèi),然后判斷兩個(gè)對(duì)象的內(nèi)容墨状。 -
isEqualToString
:判斷兩個(gè)字符串內(nèi)容是否相同卫漫。
因?yàn)樯倭藱z測(cè)參數(shù)類(lèi)型,所以比isEqual快肾砂。
事實(shí)上isEqual在檢測(cè)完參數(shù)類(lèi)型后列赎,就調(diào)用了該函數(shù)。
2.自定義對(duì)象判斷相等
重寫(xiě)isEqual
方法:
- 首先镐确,直接判斷兩個(gè)指針是否相等包吝;
- 接下來(lái),判斷是否都屬于一個(gè)類(lèi)源葫。
- 最后诗越,檢測(cè)每個(gè)屬性值是否相等。
另外:重寫(xiě)了isEqual方法息堂,那么也必須重寫(xiě)hash
方法嚷狞。因?yàn)槿绻麅蓚€(gè)對(duì)象相等,那么它們也必須有相同的哈希值荣堰。不過(guò)反之則不成立床未。
@interface HJPerson : NSObject
@property (nonatomic, copy) NSString* firstName;
@property (nonatomic, copy) NSString* lastName;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation HJPerson
- (BOOL)isEqual:(id)other{
if (self == other) return YES;
if ([self class] != [other class]) return NO;
HJPerson* otherPerson = (HJPerson*)other;
if (![self.firstName isEqualToString:otherPerson.firstName]) return NO;
if (![self.lastName isEqualToString:otherPerson.lastName]) return NO;
if (self.age != otherPerson.age) return NO;
return YES;
}
- (NSUInteger)hash{
NSUInteger firstNameHash = [self.firstName hash];
NSUInteger lastNameHash = [self.lastName hash];
NSUInteger ageHash = self.age;
return firstNameHash ^ lastNameHash ^ ageHash;
}
@end
四 、KVC / KVO
1.KVC的過(guò)程
(1)按照命名規(guī)范持隧,先查找相關(guān)的方法即硼,如果沒(méi)有找到;
(2)按照命名規(guī)范屡拨,找相關(guān)的成員變量只酥,如果沒(méi)有找到;
(3)調(diào)用setValue:forUndefinedKey呀狼;
(4)如果都沒(méi)有找到相關(guān)的方法實(shí)現(xiàn)就拋出異常裂允。
2.KVO實(shí)現(xiàn)機(jī)制
一個(gè)類(lèi)添加觀察者后,runtime創(chuàng)建了一個(gè)該類(lèi)的派生類(lèi)哥艇!實(shí)例對(duì)象的isa指向這個(gè)派生類(lèi)绝编。如果該類(lèi)有setter方法,在派生類(lèi)中會(huì)重寫(xiě)了setter方法貌踏。
重寫(xiě)的setter函數(shù)的實(shí)現(xiàn)大概是這樣:
然后在didChangeValueForKey中調(diào)用觀察者的observeValueForKeyPath十饥。
Q:如何手動(dòng)觸發(fā)KVO?
(1)關(guān)掉屬性的自動(dòng)觸發(fā)
(2)在setter方法中祖乳,手動(dòng)調(diào)用willChangeValueForKey和didChangeValueForKey
五逗堵、Category
1.Category的使用場(chǎng)合
- 將類(lèi)拆解為幾個(gè)模塊
- 為現(xiàn)有的類(lèi)添加一些方法
2.Category的實(shí)現(xiàn)原理
Category的底層結(jié)構(gòu)其實(shí)是struct _category_t 結(jié)構(gòu)體,里面存儲(chǔ)著Category的方法眷昆、屬性蜒秤、協(xié)議等信息汁咏。在運(yùn)行時(shí)、runtime初始化的時(shí)候作媚,會(huì)將category的數(shù)據(jù)攘滩,合并到類(lèi)信息中。
Q:Category和Extension的區(qū)別是什么纸泡?
- Extension的數(shù)據(jù)在編譯時(shí)就合并到類(lèi)信息中了漂问。
- Category的數(shù)據(jù)在運(yùn)行時(shí)才會(huì)合并到類(lèi)信息中。
Q:類(lèi)和分類(lèi)中的方法名相同弟灼,會(huì)調(diào)用哪個(gè)方法级解?
對(duì)于類(lèi)中和分類(lèi)中的同名方法,會(huì)優(yōu)先調(diào)用分類(lèi)的方法田绑,因?yàn)榉诸?lèi)的方法會(huì)放在原來(lái)的方法前面勤哗。
對(duì)于多個(gè)分類(lèi)中的同名方法,則需要看編譯順序掩驱。最后面編譯的分類(lèi)芒划,其方法會(huì)放在前面。
注:核心源代碼attachCategories()欧穴、attachLists()民逼。
3.關(guān)聯(lián)對(duì)象
Category不能添加成員變量,關(guān)聯(lián)對(duì)象用于給Category“添加成員變量”涮帘。
關(guān)聯(lián)對(duì)象的API:
添加關(guān)聯(lián)對(duì)象:void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)獲得關(guān)聯(lián)對(duì)象:id objc_getAssociatedObject(id object, const void * key)
移除所有的關(guān)聯(lián)對(duì)象:void objc_removeAssociatedObjects(id object)
關(guān)聯(lián)對(duì)象的原理:
關(guān)聯(lián)對(duì)象是使用哈希表實(shí)現(xiàn)的拼苍,將被關(guān)聯(lián)的對(duì)象作為key,映射到一個(gè)子哈希表上调缨。然后再通過(guò)變量名找到值疮鲫。而不是將變量放在被關(guān)聯(lián)的對(duì)象中。
Q:Category能否添加成員變量弦叶?為什么俊犯?
不能。因?yàn)镃ategory的底層數(shù)據(jù)結(jié)構(gòu)伤哺,它的結(jié)構(gòu)體中沒(méi)有成員變量只有屬性燕侠。
六、+load和+initialize
1.+load方法
+load方法會(huì)在這個(gè)類(lèi)加載到內(nèi)存中被調(diào)用立莉【钔或者說(shuō)在runtime加載類(lèi)、分類(lèi)的時(shí)候調(diào)用蜓耻。
注意:
- 就算在mian.h中沒(méi)有導(dǎo)入這個(gè)類(lèi)或者沒(méi)有使用這個(gè)類(lèi)茫舶,+load方法也會(huì)調(diào)用。
- +load方法在被runtime自動(dòng)調(diào)用時(shí)媒熊,是直接取出+load方法奇适,而不是經(jīng)過(guò)objc_msgSend,所以不遵循繼承規(guī)則芦鳍,類(lèi)和分類(lèi)的load方法都會(huì)被調(diào)用嚷往。
MJPerson +load
MJStudent +load
MJPerson (Test1) +load
MJPerson (Test2) +load
MJStudent (Test1) +load
MJStudent (Test2) +load
2.+initialize方法
+initialize方法是在類(lèi)第一次發(fā)送消息的時(shí)候調(diào)用。