(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覆盖宿主类方法,消息方法查找过程当中是根据选择器名称来查找,一旦找到就会反馈。分类方法会被优先实现。
最后编译的方法会最终生效。