深入理解OC面向?qū)ο?/h2>

目錄

  • 1.面向?qū)ο?/p>

    • 1.三要素
    • 2.屬性
  • 2.深拷貝與淺拷貝

    • 1.Foundation框架中的對(duì)象
    • 2.自定義對(duì)象
  • 3.對(duì)象等同性

    • 1.NSString對(duì)象判斷相等
    • 2.自定義對(duì)象判斷相等
  • 4.KVC / KVO

    • 1.KVC的過程
    • 2.KVO的原理
  • 5.Category

    • 1.Category的使用場合
    • 2.Category的實(shí)現(xiàn)原理
    • 3.關(guān)聯(lián)對(duì)象
  • 6.+load和+initialize

一、面向?qū)ο?/h2>

1.面向?qū)ο笕齻€(gè)要素

1.1 封裝

概念:隱藏對(duì)象的數(shù)據(jù),不允許外界直接訪問,而是需要通過相應(yīng)的方法來訪問和修改數(shù)據(jù)。
實(shí)現(xiàn):OC使用屬性(property)來封裝對(duì)象中的數(shù)據(jù)。

1.2 繼承

概念:通過繼承子類可以得到父類屬性和方法,在父類的基礎(chǔ)上建立新的類。
實(shí)現(xiàn):OC的消息機(jī)制在自己的類信息中找不到對(duì)應(yīng)方法時(shí),會(huì)通過superclass指針去父類的類信息中查找。

1.3 多態(tài)

概念:子類重寫父類方法,父類指針指向子類,父類指針調(diào)用方法時(shí)會(huì)調(diào)用子類重寫的方法。
實(shí)現(xiàn):

Q:iOS如何實(shí)現(xiàn)多繼承?

最符合的方式是消息轉(zhuǎn)發(fā);

其他的方式也有

  • 組合:將類A和類B的實(shí)例對(duì)象作為類C的屬性;
  • delegate和protocol:將C類需要繼承的方法以及屬性在ClassA和ClassB中各自聲明一份協(xié)議,C類遵守這兩份協(xié)議,同時(shí)在C類中實(shí)現(xiàn)協(xié)議中的方法以及屬性

Q:OC有重載嗎?

重載說的是函數(shù)名相同,但是參數(shù)不同,OC沒有。swift支持重載。

2.屬性(@property)

在類定義中聲明的屬性,編譯器會(huì)自動(dòng)生成一個(gè)與該屬性同名且下劃線開頭的成員變量,同時(shí)自動(dòng)生成該實(shí)例變量的存取方法。

可以通過修改屬性的修飾詞來控制存取方法的生產(chǎn),修飾詞有四種:

  • 原子性:atomic、noatomic
  • 內(nèi)存管理語義:copy、strong、assign、weak
  • 讀寫權(quán)限:readonly、readwrite
  • 方法名:getter=、setter=

二、深拷貝與淺拷貝

深拷貝就是內(nèi)容拷貝,淺拷貝就是指針拷貝。本質(zhì)區(qū)別在于:

  • 是否開啟新的內(nèi)存地址
  • 是否影響內(nèi)存地址的引用計(jì)數(shù)

1.Foundation框架中的對(duì)象

對(duì)Foundation框架中的非容器對(duì)象(NSString、NSNumber)和容器對(duì)象(NSArray、NSDictionary)使用copymutableCopy的情況如下:

需要注意的是,容器對(duì)象的深拷貝其實(shí)是單層深拷貝。
單層深拷貝:雖然為數(shù)組對(duì)象開辟了新空間,不過存放在數(shù)組的值還是原數(shù)組元素值。

實(shí)現(xiàn)容器對(duì)象的完全深拷貝可以使用專門的方法
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;

Q:為什么NSString屬性用copy修飾?
因?yàn)榭赡軙?huì)把NSMutableString對(duì)象賦值給NSString屬性;
如果使用strong的話,對(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重寫了該方法,首先判斷兩個(gè)對(duì)象的類,然后判斷兩個(gè)對(duì)象的內(nèi)容。
  • isEqualToString:判斷兩個(gè)字符串內(nèi)容是否相同。
    因?yàn)樯倭藱z測參數(shù)類型,所以比isEqual快。
    事實(shí)上isEqual在檢測完參數(shù)類型后,就調(diào)用了該函數(shù)。

2.自定義對(duì)象判斷相等

重寫isEqual方法:

  • 首先,直接判斷兩個(gè)指針是否相等;
  • 接下來,判斷是否都屬于一個(gè)類。
  • 最后,檢測每個(gè)屬性值是否相等。

另外:重寫了isEqual方法,那么也必須重寫hash方法。因?yàn)槿绻麅蓚€(gè)對(duì)象相等,那么它們也必須有相同的哈希值。不過反之則不成立。

@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的過程

(1)按照命名規(guī)范,先查找相關(guān)的方法,如果沒有找到;
(2)按照命名規(guī)范,找相關(guān)的成員變量,如果沒有找到;
(3)調(diào)用setValue:forUndefinedKey;
(4)如果都沒有找到相關(guān)的方法實(shí)現(xiàn)就拋出異常。


2.KVO實(shí)現(xiàn)機(jī)制

一個(gè)類添加觀察者后,runtime創(chuàng)建了一個(gè)該類的派生類!實(shí)例對(duì)象的isa指向這個(gè)派生類。如果該類有setter方法,在派生類中會(huì)重寫了setter方法。

重寫的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的使用場合

  • 將類拆解為幾個(gè)模塊
  • 為現(xiàn)有的類添加一些方法

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ù),合并到類信息中。

Q:Category和Extension的區(qū)別是什么?

  • Extension的數(shù)據(jù)在編譯時(shí)就合并到類信息中了。
  • Category的數(shù)據(jù)在運(yùn)行時(shí)才會(huì)合并到類信息中。

Q:類和分類中的方法名相同,會(huì)調(diào)用哪個(gè)方法?

  • 對(duì)于類中和分類中的同名方法,會(huì)優(yōu)先調(diào)用分類的方法,因?yàn)榉诸惖姆椒〞?huì)放在原來的方法前面。

  • 對(duì)于多個(gè)分類中的同名方法,則需要看編譯順序。最后面編譯的分類,其方法會(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ān)聯(lián)的對(duì)象中。

Q:Category能否添加成員變量?為什么?

不能。因?yàn)镃ategory的底層數(shù)據(jù)結(jié)構(gòu),它的結(jié)構(gòu)體中沒有成員變量只有屬性。

六、+load和+initialize

1.+load方法

+load方法會(huì)在這個(gè)類加載到內(nèi)存中被調(diào)用?;蛘哒f在runtime加載類、分類的時(shí)候調(diào)用。

注意:

  • 就算在mian.h中沒有導(dǎo)入這個(gè)類或者沒有使用這個(gè)類,+load方法也會(huì)調(diào)用。
  • +load方法在被runtime自動(dòng)調(diào)用時(shí),是直接取出+load方法,而不是經(jīng)過objc_msgSend,所以不遵循繼承規(guī)則,類和分類的load方法都會(huì)被調(diào)用
MJPerson +load
MJStudent +load
MJPerson (Test1) +load
MJPerson (Test2) +load
MJStudent (Test1) +load
MJStudent (Test2) +load

2.+initialize方法

+initialize方法是在類第一次發(fā)送消息的時(shí)候調(diào)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容