2019Objective_C 語言特性 分類 擴(kuò)展 代理 通知 KVO KVC 屬性關(guān)堅持

一、分類

  • 1.分類的作用?
    1. 聲明私有方法
    2. 分解體積龐大的類文件 把 framework 的私有方法公開
  • 2.分類的特點(diǎn)
    1.運(yùn)行時決議,
    2.可以為系統(tǒng)類添加分類
    說的詳細(xì)點(diǎn)兒 在運(yùn)行時時期, 將 Category 中的實例方法列表、協(xié)議列表、屬性列表添加到主類中后(將 Category 中 的方法在方法列表中的位置在主類的同名方法之前之前), 然后會遞歸的調(diào)用所有類的 load 方法, 這一切都在 main 函數(shù)之前執(zhí)行的
  • 3.分類可以添加哪些內(nèi)容
    實例方法 類方法 協(xié)議 屬性(添加了 setter 和 getter 方法, 并沒有實例變量, 需要用關(guān)聯(lián)對象技術(shù))
  • 4.如果工程里有兩個分類 A 和 B, 兩個分裂中有一個同名方法, 哪個方法最終實現(xiàn)
    取決于分類的編譯書序, 最后編譯的那個分類的同名方法最終生效.而之前的都會被覆蓋掉(這里并不是真正的覆蓋, 因為其余方法仍然存在, 只是訪問不到, 因為在動態(tài)添加類的方法的時候是倒序遍歷方法列表的, 而最后編譯的分類的方法會放在方法列表的簽名, 方法的時候會被優(yōu)先方訪問到, 同理如果聲明了一個和原類方法同名的方法, 也會被覆蓋掉原類的方法)
  • 5.如果聲明了兩個同名的分類會怎么樣?
    會報錯, 所以第三方的分類, 一般都帶有命名前綴
  • 6.分類能添加成員變量嗎?
    不能向編譯后的類中添加成員變量, 但是可以向動態(tài)的類中添加成員變量, 利用的是關(guān)聯(lián)對象技術(shù). objc_setAssociatedObject來模擬實現(xiàn)成員變量, 但其實是關(guān)聯(lián)內(nèi)容
    所有關(guān)聯(lián)對象的關(guān)聯(lián)內(nèi)容都放在一個同一個全局容器哈希表中, 由一個 AssociationsManager統(tǒng)一管理.

二、擴(kuò)展

  • 1.一般用擴(kuò)展做什么?

    1. 聲明私有屬性
    2. 聲明私有成員變量
    3. 聲明方法什么都不做
  • 2.擴(kuò)展的特點(diǎn)

    1. 編譯時決議
    2. 只能以聲明的形式存在, 多數(shù)情況下寄生在宿主類的.m 中, 不能為系統(tǒng)類添加擴(kuò)展

三、代理

image.png

代理是一種設(shè)計模式, 以@protocol 形式體現(xiàn), 一般是一對一傳遞, 一般以 weak 關(guān)鍵詞以規(guī)避循環(huán)引用

四、通知

通知是用觀察者模式來實現(xiàn)的, 用于跨層傳遞信息的機(jī)制傳遞方式一是一對多

  • 如何實現(xiàn)通知機(jī)制?


    image.png

五、KVO

KVO 是觀察者模式的另一實現(xiàn)
使用了 isa 混寫(isa-swizzling)來實現(xiàn)的 KVO


image.png

使用 setter 方法改變值 KVO 會生效, 使用 setValue:forKey 即KVC 改變值 KVO 也會生效, 因為 KVC 會調(diào)用 setter 方法

- (void)setValue:(id)value{
   [self willChangeValueForKey:@"key"];   
 
   [super setValue:value];

   [self didChangeValueForKey:@"key"];
}
  • 那么通過直接賦值成員變量會觸發(fā) KVO 嗎?
    不會, 因為不會調(diào)用 setter 方法, 需要手動加上
    willChangeValueForKey和didiChangeValueForKey 方法來手動出發(fā) KVO 才行

六、KVC

-(void)setValue:(id)value forKey:(NSString *)key;
-(id)valueForKey:(NSString *)key;

KVC 是指在 iOS 開發(fā)中, 可以允許開發(fā)者通過 Key 名直接訪問對象的屬性, 或者給對象的屬性賦值, 而不需要調(diào)用明確的存取方法, 這樣就可以在運(yùn)行時動態(tài)地訪問和修改對象的屬性 而不是在編譯時確定, 這也是iOS 開發(fā)中的黑魔法之一, 很多高級的 iOS 開發(fā)技巧都是基于 KVC 實現(xiàn)的
當(dāng)調(diào)用setValue: forKey:@"name"的代碼時, 底層的實現(xiàn)機(jī)制如下:

  • 程序優(yōu)先調(diào)用 setkey:屬性值方法, 代碼通過 setter 方法完成設(shè)置, 注意, 這里的 key是指成員變量名字, 首字母大小寫要符合 KVC 的命名規(guī)則, 下同
  • 如果沒有找到 setName 方法, KVC 機(jī)制會檢查+(BOOL) accessInstanceVariablesDirectly方法有沒有返回 YES, 默認(rèn)方法返回 YES, 如果你重寫了改方法返回了 NO 的話, 那么在這一步 KVC 會執(zhí)行 setValue:forUndefinedKey方法, 不過, 一般開發(fā)者不會這么做, 所以 KVC 機(jī)制會搜索該類里面的有沒有名為 key 的成員變量, 無論該變量是在類接口定義還是在類實現(xiàn)定義, 也無論用了什么樣的訪問修飾符, 只在存在以key 命名的變量, KVC 都可以對該成員變量賦值
  • 如果該類即沒有 setKey 方法, 也沒有 key 成員變量, KVC 機(jī)制會搜索_iskey 的成員變量
  • 和上面一樣, 如果該類即沒有 setKey 方法, 也沒有_key 和_isKey 成員變量, KVC 機(jī)制hi 繼續(xù)在搜索 key 和 isKey 的成員變量, 再給它們賦值
  • 如果上面列出的方法或者成員變量都不存在, 系統(tǒng)將會執(zhí)行該對象的 setValue:forUndefinedKey 方法, 默認(rèn)是拋出異常
    即如果沒有找到 SetKey方法的話, 會按照_key _iskey key iskey 的順序搜索成員變量并進(jìn)行賦值操作

如果開發(fā)者想讓這個類禁用 KVC, 那么重寫+(BOOL) accessInstanceVariablesDirectly讓其返回 NO 即可, 這樣的話如果 KVC 沒有找到 setKey 屬性名時, 會直接用 setValue:forUndefinedKey方法

當(dāng)調(diào)用valueForKey:@”name“的代碼時,KVC對key的搜索方式不同于setValue:屬性值 forKey:@”name“,其搜索方式如下:

  • 首先按get<Key> ,<key>,is<Key>的順序方法查找 getter 方法, 找到的話會直接調(diào)用, 如果是 BOOL 或者 int 等值類型, 會將其包裝成一個 NSNumber 對象
  • 如果上面的getter沒有找到, KVC則會查找countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes格式的方法。如果countOf<Key>方法和另外兩個方法中的一個被找到,那么就會返回一個可以響應(yīng)NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子類),調(diào)用這個代理集合的方法,或者說給這個代理集合發(fā)送屬于NSArray的方法,就會以countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes這幾個方法組合的形式調(diào)用。還有一個可選的get<Key>:range:方法。所以你想重新定義KVC的一些功能,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標(biāo)準(zhǔn)命名方法,包括方法簽名。
  • 如果上面的方法沒有找到,那么會同時查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>格式的方法。如果這三個方法都找到,那么就返回一個可以響應(yīng)NSSet所的方法的代理集合,和上面一樣,給這個代理集合發(fā)NSSet的消息,就會以countOf<Key>,enumeratorOf<Key>,memberOf<Key>組合的形式調(diào)用。
  • 如果還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為),那么和先前的設(shè)值一樣,會按_<key>,_is<Key>,<key>,is<Key>的順序搜索成員變量名,這里不推薦這么做,因為這樣直接訪問實例變量破壞了封裝性,使代碼更脆弱。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話,那么會直接調(diào)用valueForUndefinedKey:方法,默認(rèn)是拋出異常。

七、屬性關(guān)鍵字

  • 1.讀寫權(quán)限: readwrite(默認(rèn)) readonly
  • 2.原子性:atomic(默認(rèn)), nonatomic.atomic是讀寫線程安全的, 但是效率低,而且也不是絕對的安全的. 比如如果修飾的是數(shù)組, 那么對數(shù)組的讀寫是安全的, 但如果是操作數(shù)組進(jìn)行添加移除其中的對象的話, 那就不保證安全了
  • 3.引用計數(shù)
    • retain/strong
    • assign: 修飾基本數(shù)據(jù)類型, 修飾對象類型時, 不改變其引用計數(shù)器, 會產(chǎn)生懸垂指針 修飾的對象在釋放后, assign指針仍然指向原對象內(nèi)存地址, 如果使用assign指針繼續(xù)訪問原來的對象的話, 就可能會導(dǎo)致內(nèi)存泄露或者程序異常
    • weak:不改變被修飾對象的引用計數(shù), 所指對象在被釋放后, weak 指針會自動置為 nil
    • copy:分為深拷貝和淺拷貝
      淺拷貝:對內(nèi)存地址的復(fù)制, 讓目標(biāo)對象指針和原對象指向同一片內(nèi)存空間會增加引用計數(shù)
      深拷貝: 對對象內(nèi)容的復(fù)制 開辟新的內(nèi)存空間


      image.png

可變對象的 copy 和 mutableCopy 都是深拷貝
不可變對象的 copy 是淺拷貝 , mutableCopy 是深拷貝
copy 方法返回的都是不可變對象

- @property (nonatomic, copy) NSMutableArray * array;這樣寫有什么影響?

因為 copy 方法返回的都是不可變對象, 所以 array 對象實際上不可變的, 如果對其進(jìn)行操作如添加或者移除對象, 則會造成 crash.

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

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

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