(1)分類是在運行 聲明私有方法、聲明私有屬性、聲明私有成員變量時纔將分類添加到宿主類的。能夠作到在既不子類化,也不侵入一個類的源碼的狀況下,爲原有的類添加實例方法、類方法、協議、屬性,還能夠經過關聯對象添加成員變量。 (2) 類擴展是在編譯階段與該類同時編譯的,是類的一部分。通常聲明的方法只能在宿主類的 @implementation 中實現,因此就沒法對系統類使用類擴展。 但與 分類不一樣的是,類擴展不但能夠聲明方法,還能夠聲明成員變量(實例變量)。設計模式
分類: (1)把類的不一樣實現方法分開到不一樣的文件裏。 (2)聲明私有方法。 (3)模擬多繼承。 (4)將 framework 私有方法公開化。 類擴展 (1)聲明私有方法 (2)聲明私有屬性 (3)聲明私有成員變量數組
struct category_t { //分類的名稱 const char * name; //分類的宿主類 classref_t cls; //實例方法列表 struct method_list_t * instanceMethods; //類方法列表 struct method_list_t * classMethods; //協議 struct protocol_list_t * prorocols; //實例屬性 struct protocol_list_t * instanceProperties; // method_list_t * methodsForMeta(bool isMeta) { if (isMeta) return classMethods; else return instanceMethods; } // protocol_list_t * propertiesForMeta(bool isMeta) { if (isMeta) return nil; else return instanceProperties; } } static void remethodizeClass(Class cls) { category_list *cats; bool isMeta; runtimeLock.assertWriting(); //咱們分析分類當中實例方法添加的邏輯 isMeta = cls->isMetaClass(); // 獲取cls中未完成整合的全部分類 if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) { if (PrintConnecting) { _objc_inform("CLASS: attaching categories to class '%s' %s", cls->nameForLogging(), isMeta ? "(meta)" : ""); } // 將分類cats拼接到cls上 attachCategories(cls, cats, true /*flush caches*/); free(cats); } } static void attachCategories(Class cls, category_list *cats, bool flush_caches) { if (!cats) return; if (PrintReplacedMethods) printReplacements(cls, cats); //咱們分析分類當中實例方法添加的邏輯 bool isMeta = cls->isMetaClass(); // 二維數組 [[method_t,method_t,...],[method_t],[method_t,method_t,method_t],...] method_list_t **mlists = (method_list_t **) malloc(cats->count * sizeof(*mlists)); property_list_t **proplists = (property_list_t **) malloc(cats->count * sizeof(*proplists)); protocol_list_t **protolists = (protocol_list_t **) malloc(cats->count * sizeof(*protolists)); // Count backwards through cats to get newest categories first int mcount = 0; int propcount = 0; int protocount = 0; int i = cats->count; // 宿主類分類的總數 bool fromBundle = NO; while (i--) { // 這裏是倒敘遍歷,最早訪問最後編譯的分類 //獲取一個類 auto& entry = cats->list[i]; //獲取該分類的方法列表 method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { //最後編譯的分類最早添加到分類數組中 mlists[mcount++] = mlist; fromBundle |= entry.hi->isBundle(); } // 屬性列表添加規則同方法列表添加規則同樣 property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); if (proplist) { proplists[propcount++] = proplist; } // 協議列表添加規則同方法列表添加規則同樣 protocol_list_t *protolist = entry.cat->protocols; if (protolist) { protolists[protocount++] = protolist; } } //獲取宿主類當中的rw數據,其中包含宿主類的方法列表信息 auto rw = cls->data(); //針對分類中有關內存管理相關的方法狀況 prepareMethodLists(cls, mlists, mcount, NO, fromBundle); rw->methods.attachLists(mlists, mcount); free(mlists); if (flush_caches && mcount > 0) flushCaches(cls); rw->properties.attachLists(proplists, propcount); free(proplists); rw->protocols.attachLists(protolists, protocount); free(protolists); }複製代碼
id objc_getAssociatedObject(id object, const void *key) { return _object_get_associative_reference(object, (void *)key); } void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) { _object_set_associative_reference(object, (void *)key, value, policy); } void objc_removeAssociatedObjects(id object) { if (object && object->hasAssociatedObjects()) { _object_remove_assocations(object); } } void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { // retain the new value (if any) outside the lock. ObjcAssociation old_association(0, nil); id new_value = value ? acquireValue(value, policy) : nil; { //關聯對象管理類,C++ AssociationsManager manager; //全局容器 AssociationsHashMap &associations(manager.associations()); disguised_ptr_t disguised_object = DISGUISE(object); if (new_value) { // 根據對象指針查找對應的一個 ObjectAssociationMap結構的map AssociationsHashMap::iterator i = associations.find(disguised_object); // if (i != associations.end()) { ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { old_association = j->second; j->second = ObjcAssociation(policy, new_value); } else { (*refs)[key] = ObjcAssociation(policy, new_value); } } else { // create the new association (first time). ObjectAssociationMap *refs = new ObjectAssociationMap; associations[disguised_object] = refs; (*refs)[key] = ObjcAssociation(policy, new_value); object->setHasAssociatedObjects(); } } else { //沒有建立過容器,就要先建立 AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { old_association = j->second; refs->erase(j); } } } } // release the old value (outside of the lock). if (old_association.hasValue()) ReleaseValue()(old_association); } 複製代碼
一、判斷有沒有指定key的set方法,若是有set方法,就會調用set方法,給該屬性賦值。 二、若是沒有set方法,判斷有沒有跟key值相同且帶有下劃線的成員屬性(bash
key,判斷有沒有跟key相同名稱的屬性。若是有,直接給該屬性進行賦值。 四、若是都沒有,就會調用 valueforUndefinedKey 和setValue:forUndefinedKey:方法。數據結構
由於KVC能夠經過硬編碼字符串控制私有私有變量,因此是會破壞面向對象的方法。ide
不能,須要手動添加KVO。成員變量直接修改需手動添加KVO纔會生效。只有使用setter方法 和 setValue:forKey改變值KVO纔會生效。 解決方法是能夠在實現方法裏添加willChangeValueForKey
和didChangeValueForKey
。函數
Apple文檔中有一句話: In order to understand key-value observing, you must first understand key-value coding。KVO在觀察一個對象時,會動態被拷貝一個新類,新類重寫setter方法時跟着KVC的搜索方式進行。當觀察對象時會用到KVC的valueForKey
去獲取。佈局
當你觀察一個對象時,會動態被拷貝一個新的類。這個類繼承自該對象所處的類,並重寫了被觀察屬性的 setter
方法。天然會負責在調用原 setter
方法以前和以後,通知全部觀察對象值的更改。最後把這個對象的 isa
指針 ( isa 指針告訴 Runtime 系統這個對象的類是什麼 ) 指向這個新類,對象就神奇的變成了新類的實例。不只如此,Apple 還重寫了 -class
方法,企圖欺騙咱們這個類沒有變,就是本來那個類。ui
KVO是觀察者設計模式的一種實現,使用了的isa混寫技術。一個任意類型的對象對另外的對象進行監聽,當被監聽的對象一旦發生改變,觀察者立刻響應。可是也只能對屬性的改變時響應,而不會對方法或動做的改變發生響應。編碼
淺拷貝只是複製原對象的指針,並將新的對象指向原對象的地址。深拷貝是複製了一份新的內存地址。 只有不可變對象的copy操做纔是淺拷貝,其餘類型的操做都是深拷貝。 只有可變對象的mutableCopy操做才能複製出可變對象,其餘類型的操做都是複製出不可變對象。 @property (copy) NSMutableArray * array
這段代碼會產生什麼異常? 由於對於copy一個可變對象,產生的是不可變對象。可是因爲聲明的是可變類型,因此當對這個對象進行添加或刪除時,就會報找不到對象的錯誤。spa
代理方強引用者委託方,而委託方弱引用代理方。因此經過weak能夠規避循環引用。
一、委託方要求代理方須要實現的接口,這部分接口是協議。 二、代理方要按照協議實現方法。 三、委託方調用代理方聽從的協議方法。 四、代理方返回處理結果給委託方。
DELEGATE,以@protocol形式體現,是一種代理設計模式。能夠有返回值,通常是一對一傳遞的。 NSNOTIFICATION,是使用觀察者模式來實現的,用於跨層傳遞消息。傳遞方式是一對多的。 BLOCK將函數及其描述上下文封裝起來的對象。他不是一種設計模式。
由於 類擴展 是在編譯階段與該類同時編譯的,就是類的一部分。那麼就能夠在編譯階段爲類添加成員變量。 而 分類是:能夠在運行時階段動態地爲已有類添加新行爲。分類是在運行時期間決定的。而成員變量在編譯階段肯定好了,若是在運行時階段添加成員變量的話,就會破壞原有類的內存佈局,從而形成可怕的後果,因此 分類沒法添加成員變量。 若是須要爲分類添加成員變量,那麼可使用運行時setObject的方法進行。
分類方法會經過memmove和memcpy覆蓋宿主類方法,消息方法查找過程當中是根據選擇器名稱來查找,一旦找到就會反饋。分類方法會被優先實現。
最後編譯的方法會最終生效。