要想知道区别,首先要知道用途及特点
一、分类
1、分类常怎么用?
a.把内容臃肿的类文件用分类的方法分解成一个或者几个类。
b.framework私有方法公开化(重写私有方法)
c.声明私有方法
2、分类的特点(跟扩展的区别)
a.运行时决议
b.可以为系统类添加分类
3、分类可以添加什么
a.实例方法
b.类方法
c.协议
d.属性,但不能添加实例变量,需要用到runtime关联对象的方法。

如以下源码(代码取自官方runtime demo):
struct category_t {
const char *name;//分类名字
classref_t cls;
struct method_list_t *instanceMethods;// 实例方法列表
struct method_list_t *classMethods;// 类方法列表
struct protocol_list_t *protocols;// 协议列表
struct property_list_t *instanceProperties;// 属性列表

method_list_t *methodsForMeta(bool isMeta) {
    if (isMeta) return classMethods;
    else return instanceMethods;
}

property_list_t *propertiesForMeta(bool isMeta) {
    if (isMeta) return nil; // classProperties;
    else return instanceProperties;
}

};

Tip1:假如一个类有好几个分类,并且这些分类里面有重名的方法,哪个方法会生效?为什么分类是运行时决议?
因为runtime进行时是倒序访问编译的分类的,也就是编译的越早越先访问,所以最后编译的那个分类的同名方法才会生效。
源码如下:
static void attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);

bool isMeta = cls->isMetaClass();

// fixme rearrange to remove these intermediate allocations
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);
    if (proplist) {
        proplists[propcount++] = proplist;
    }

    protocol_list_t *protolist = entry.cat->protocols;
    if (protolist) {
        protolists[protocount++] = protolist;
    }
}

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);

}

Tip2:分类添加的方法为什么能“覆盖”原来类的方法?
因为在tip拼接类方法的时候是把分类的方法拼接到方法列表的最前面,方法选择器在查找方法的时候查到同名方法就会返回,所以会实现分类里面的方法。因此原来类的方法并不是被覆盖。源码如下:
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;

if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        array()->count = newCount;

        memmove(array()->lists + addedCount, array()->lists,
                oldCount * sizeof(array()->lists[0]));
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
    else if (!list  &&  addedCount == 1) {
        // 0 lists -> 1 list
        list = addedLists[0];
    } 
    else {
        // 1 list -> many lists
        List* oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
}
3、通过关联对象的方法给分类添加实例变量,其实用的是runtime的方法。
用法如下:
static NSString *const kDefaultImageViewKey = @"defaultImageView";
- (UIImageView *)defaultImageView {
UIImageView *imgView = objc_getAssociatedObject(self, &kDefaultImageViewKey);
if (!imgView) {
    imgView = [[UIImageView alloc] init];
    imgView.image = [UIImage imageNamed:@"JMDefaultPage_order"];
    imgView.contentMode = UIViewContentModeScaleAspectFill;
    objc_setAssociatedObject(self, &kDefaultImageViewKey, imgView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return imgView;

}
其底层实现是通过的runtime的方法,如下:

id objc_getAssociatedObject(id object, const void *key)
 {
 return objc_getAssociatedObject_non_gc(object, key);
 }void objc_setAssociatedObject(id object, const void *key, id value,
 objc_AssociationPolicy policy)
 {
 objc_setAssociatedObject_non_gc(object, key, value, policy);
 }#endif
void objc_removeAssociatedObjects(id object)
 {
 #if SUPPORT_GC
 if (UseGC) {
 auto_zone_erase_associative_refs(gc_zone, object);
 } else
 #endif
 {
 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;
 {
 AssociationsManager manager;// 关联对象管理类
 AssociationsHashMap &associations(manager.associations());// 获取其维护的一个hashMap
 disguised_ptr_t disguised_object = DISGUISE(object);
 if (new_value) {
 // break any existing association.
 AssociationsHashMap::iterator i = associations.find(disguised_object);
 if (i != associations.end()) {
 // secondary table exists
 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 {
 // setting the association to nil breaks the association.
 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);
 }

其本质用图表示如下:

ios和安卓的底层架构 安卓和ios底层区别_ios


二、扩展

1、扩展常用作什么

a、声明私有属性、私有变量。

b、声明私有方法

2、扩展的特点

a、编译时决议

b、只以声明的形式存在

c、不能为系统类添加扩展