UPDATE iOSer

Objective-C語言相關

1. 分類與類擴展有什麼區別?

(1)分類是在運行 聲明私有方法、聲明私有屬性、聲明私有成員變量時纔將分類添加到宿主類的。能夠作到在既不子類化,也不侵入一個類的源碼的狀況下,爲原有的類添加實例方法、類方法、協議、屬性,還能夠經過關聯對象添加成員變量。 (2) 類擴展是在編譯階段與該類同時編譯的,是類的一部分。通常聲明的方法只能在宿主類的 @implementation 中實現,因此就沒法對系統類使用類擴展。 但與 分類不一樣的是,類擴展不但能夠聲明方法,還能夠聲明成員變量(實例變量)。設計模式

2. 分類和累擴展的做用有什麼?

分類: (1)把類的不一樣實現方法分開到不一樣的文件裏。 (2)聲明私有方法。 (3)模擬多繼承。 (4)將 framework 私有方法公開化。 類擴展 (1)聲明私有方法 (2)聲明私有屬性 (3)聲明私有成員變量數組

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,判斷有沒有跟key相同名稱的屬性。若是有,直接給該屬性進行賦值。 四、若是都沒有,就會調用 valueforUndefinedKey 和setValue:forUndefinedKey:方法。數據結構

17. valueForKey的實現流程

由於KVC能夠經過硬編碼字符串控制私有私有變量,因此是會破壞面向對象的方法。ide

16. 經過鍵值編碼技術是否會破壞面向對象的方法?

不能,須要手動添加KVO。成員變量直接修改需手動添加KVO纔會生效。只有使用setter方法 和 setValue:forKey改變值KVO纔會生效。 解決方法是能夠在實現方法裏添加willChangeValueForKeydidChangeValueForKey函數

15. 直接對成員變量賦值是否能觸發KVO呢?

Apple文檔中有一句話: In order to understand key-value observing, you must first understand key-value coding。KVO在觀察一個對象時,會動態被拷貝一個新類,新類重寫setter方法時跟着KVC的搜索方式進行。當觀察對象時會用到KVC的valueForKey去獲取。佈局

14. KVO與KVC之間有什麼關係?

當你觀察一個對象時,會動態被拷貝一個新的類。這個類繼承自該對象所處的類,並重寫了被觀察屬性的 setter 方法。天然會負責在調用原 setter方法以前和以後,通知全部觀察對象值的更改。最後把這個對象的 isa 指針 ( isa 指針告訴 Runtime 系統這個對象的類是什麼 ) 指向這個新類,對象就神奇的變成了新類的實例。不只如此,Apple 還重寫了 -class 方法,企圖欺騙咱們這個類沒有變,就是本來那個類。ui

13. isa混寫技術是如何實現的?KVO的實現原理如何?

KVO是觀察者設計模式的一種實現,使用了的isa混寫技術。一個任意類型的對象對另外的對象進行監聽,當被監聽的對象一旦發生改變,觀察者立刻響應。可是也只能對屬性的改變時響應,而不會對方法或動做的改變發生響應。編碼

12. 什麼是KVO?

淺拷貝只是複製原對象的指針,並將新的對象指向原對象的地址。深拷貝是複製了一份新的內存地址。 只有不可變對象的copy操做纔是淺拷貝,其餘類型的操做都是深拷貝。 只有可變對象的mutableCopy操做才能複製出可變對象,其餘類型的操做都是複製出不可變對象。 @property (copy) NSMutableArray * array這段代碼會產生什麼異常? 由於對於copy一個可變對象,產生的是不可變對象。可是因爲聲明的是可變類型,因此當對這個對象進行添加或刪除時,就會報找不到對象的錯誤。spa

11. 淺拷貝和深拷貝有什麼區別?

代理方強引用者委託方,而委託方弱引用代理方。因此經過weak能夠規避循環引用。

10. 代理方和委託方以什麼樣的關係存在?

一、委託方要求代理方須要實現的接口,這部分接口是協議。 二、代理方要按照協議實現方法。 三、委託方調用代理方聽從的協議方法。 四、代理方返回處理結果給委託方。

9. 代理的調用流程?

DELEGATE,以@protocol形式體現,是一種代理設計模式。能夠有返回值,通常是一對一傳遞的。 NSNOTIFICATION,是使用觀察者模式來實現的,用於跨層傳遞消息。傳遞方式是一對多的。 BLOCK將函數及其描述上下文封裝起來的對象。他不是一種設計模式。

8. DELEGATE、BLOCK、NOTIFICATION之間有什麼區別?

7. 關聯對象的實現源碼如何

由於 類擴展 是在編譯階段與該類同時編譯的,就是類的一部分。那麼就能夠在編譯階段爲類添加成員變量。 而 分類是:能夠在運行時階段動態地爲已有類添加新行爲。分類是在運行時期間決定的。而成員變量在編譯階段肯定好了,若是在運行時階段添加成員變量的話,就會破壞原有類的內存佈局,從而形成可怕的後果,因此 分類沒法添加成員變量。 若是須要爲分類添加成員變量,那麼可使用運行時setObject的方法進行。

6. 爲何 Category(分類)不能像 Extension(擴展)同樣添加成員變量(實例變量)?

分類方法會經過memmove和memcpy覆蓋宿主類方法,消息方法查找過程當中是根據選擇器名稱來查找,一旦找到就會反饋。分類方法會被優先實現。

5. 分類方法與宿主類同名的方法哪一個會被調用到?

最後編譯的方法會最終生效。

4.若是有多個同樣的分類方法,哪一個會生效?

相關文章
相關標籤/搜索