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.若是有多个同样的分类方法,哪一个会生效?

相关文章
相关标签/搜索