厚积薄发 - 分类

1. 分类的用处

  • 可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处,a)可以减少单个文件的体积 b)可以把不同的功能组织到不同的category里 c)可以由多个开发者共同完成一个类 d)可以按需加载想要的category 等等。
  • 声明私有方法
  • 模拟多继承
  • 把framework的私有方法公开

2.分类和扩展

扩展看起来像一个匿名的分类,但是extension和有名字的category几乎完全是两个东西。

扩展:编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。

分类:运行期决议的。category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。

3.分类的实现原理

1 typedef struct category_t {
2     const char *name;
3     classref_t cls;
4     struct method_list_t *instanceMethods;
5     struct method_list_t *classMethods;
6     struct protocol_list_t *protocols;
7     struct property_list_t *instanceProperties;
8 } category_t;

从category的定义也可以看出category的使用(可以添加实例方法,类方法,甚至可以实现协议,添加属性)。

调用顺序:

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    lock_init();
    exception_init();

    // Register for unmap first, in case some +load unmaps something
    _dyld_register_func_for_remove_image(&unmap_image);
    dyld_register_image_state_change_handler(dyld_image_state_bound,
                                             1/*batch*/, &map_images);
    dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);

1.libsystem 调用_objc_init 

2.在_objc_init中,objc-runtime-new.mm 调用 map_images ,最后_read_images 。

3.load所以的类 协议 分类 调用 +load方法。

// Discover categories.
    for (EACH_HEADER) {
        category_t **catlist =
            _getObjc2CategoryList(hi, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            class_t *cls = remapClass(cat->cls);

            if (!cls) {
                // Category‘s target class is missing (probably weak-linked).
                // Disavow any knowledge of this category.
                catlist[i] = NULL;
continue;
            }

            // Process this category.
            // First, register the category with its target class.
            // Then, rebuild the class‘s method lists (etc) if
            // the class is realized.
            BOOL classExists = NO;
            if (cat->instanceMethods ||  cat->protocols
                ||  cat->instanceProperties)
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (isRealized(cls)) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
            }

            if (cat->classMethods  ||  cat->protocols
                /* ||  cat->classProperties */)
            {
                addUnattachedCategoryForClass(cat, cls->isa, hi);
                if (isRealized(cls->isa)) {
                    remethodizeClass(cls->isa);
                }
            }
        }
    }

1)、把category的实例方法、协议以及属性添加到类上
2)、把category的类方法和协议添加到类的metaclass上

注意:

1)、category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA
2)、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休^_^,殊不知后面可能还有一样名字的方法。

3)、添加category到类上的工作会先于+load方法,所以调用类的方法时(分类也实现了相同的方法),会先调用分类的方法。如果多个分类,会按编译顺序决定。

4)、+load方法 的执行顺序是先类,后category。如果类和分类同时实现+load方法,会先调用类的+load。  如果是多个分类,一样会按编译顺序决定。

4.小技巧

怎么调用到原来类中被category覆盖掉的方法?

Class currentClass = [MyClass class];
MyClass *my = [[MyClass alloc] init];

if (currentClass) {
    unsigned int methodCount;
    Method *methodList = class_copyMethodList(currentClass, &methodCount);
    IMP lastImp = NULL;
    SEL lastSel = NULL;
    for (NSInteger i = 0; i < methodCount; i++) {
        Method method = methodList[i];
        NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method))
                                        encoding:NSUTF8StringEncoding];
        if ([@"printName" isEqualToString:methodName]) {
            lastImp = method_getImplementation(method);
            lastSel = method_getName(method);
        }
    }
    typedef void (*fn)(id,SEL);

    if (lastImp != NULL) {
        fn f = (fn)lastImp;
        f(my,lastSel);
    }
    free(methodList);

我们只要顺着方法列表找到最后一个对应名字的方法,就可以调用原来类的方法。

时间: 2024-10-09 10:20:03

厚积薄发 - 分类的相关文章

知识分类——实事求是

把握住基本的方向,做好自己,反省自己,不断学习.不断反省. 但是还要具体情况,具体对待.生活中的情况,总是各种各样的.这就涉及到知识分类. 知识分为自然科学.社会科学.自然科学,简单地说,就是一门干活的手艺.社会科学是人与人的竞争.相处之道.而哲学,是对社会科学.自然科学的概括和总结,思维的方法.步骤,比较宏观.因为无论是自然科学.社会科学,进行学习和实践的时候,都涉及到相应的方法. 要想生存,要想做事行得通,就必须要学习自然科学.社会科学,还有形成自己的方法和思维--这就是哲学.而一直所学习的

数据库介绍与分类

目录 数据库介绍与分类... 1 1.1 数据库介绍... 2 1.1.1什么是数据库... 2 1.2数据库的种类... 2 1.2.1关系型数据库介绍... 2 1.2.2非关系型数据库介绍... 3 1.3 常用关系型数据库产品介绍... 4 1.3.1 Oracle数据库... 4 1.3.2 MySQL数据库... 5 1.3.3 MariaDB数据库... 5 1.3.4 SqlServer数据库... 6 1.3.5 Access数据库... 6 1.3.6 其他不常用数据库...

PHP_递归实现无限级分类

<?php /** * 递归方法实现无限级别分类 * @param array $list 要生成树形列表的数组[该数组中必须要有主键id 和 父级pid] * @param int $pid=0 父级id * @param int $level=0 缩进次数[用于指定分类名称要缩进的数量] */ function getTree($list,$pid=0,$level=0 ) { // static 表示声明一个静态变量, 静态变量在函数中会一直保存它的值 static $tree = arr

使用多级分组报表展现分类数据

当你走进便利店,你会发现所有的商品都是按类排放的,分类排放可以帮助您快速找到同类商品,也可以快速发现你想要的商品. 当去饭店吃饭的时候,菜单上的菜也是按类别排列的,比如凉菜,热菜,汤类,主食等.点菜时你会直接翻到你想要的那一类菜单,点菜. -- 通过上面场景,大家就理解分组呈现数据的好处了,分组可以帮助快速定位,方便数据查找,汇总,分析数据趋势等.有时候分析某一单一的商品并无价值,所以采用分组是最常见也是最简单的数据分析手段. 分组报表则是在报表中使用分组功能,是工作中最常用的报表类型,分组功能

2.1 二分分类

本周学习神经网络编程的基础知识 构建神经网络,有些技巧是非常重要 神经网络的计算过程中,通常有一个正向的过程(正向传播步骤),接着会有一个反向步骤(反向传播步骤), 为什么神经网络的计算可以分为前向传播和反向传播两个分开的过程?本周课程通过使用logistic回归来阐述,以便于能够更好的理解, logistic回归是一个用于二分分类的算法 比如有一个二分分类问题的例子, 假如有一张图像作为输入是这样的,你想输出识别此图的标签,如果是猫,输出1,如果不是,则输出0 使用y来表示输出的结果标签, 来

栅格重分类和条件函数均可以实现对流量统计数据进行定义划分

ArcGIS水分分析工具的流向分析是基于D8单流向算法,如果分析使用的DEM存在凹陷点,就会产生汇,导致径流断流从而影响了分析结果.在前面章节<ArcGIS水文分析实战教程(2)ArcGIS水文分析工具的基本原理>中又介绍过D8算法,而<ArcGIS水文分析实战教程(4)地形预处理>章节中笔者也较少过如何创建无凹陷点得DEM数据,在使用流向分析工具之前可以先行阅读. 首先流向分析要使用填洼过的数据,确保DEM数据没有凹陷点.如果数据准备妥当,直接使用水文分析工具箱中的[流向]工具进

JS学习笔记(一): 使用原生JS 实现导航栏下多级分类弹出效果

在给静态页面静添加交互效果时遇到的问题 : 鼠标划入二级菜单时 一级菜单样 ":hover" 式无法保持 情景如下: 解决思路: 利用二级菜单的onmouseover/out事件 重新构建一级菜单 ".hover" 样式类 代码如下: CSS部分: 在原来的目标:hover样式中 增加 .hover状态 li.app_jd a:hover,li.app_jd a.hover{ background-position: -126px -397px; } li.serv

前端学习 -- Css -- 字体分类

在网页中将字体分成5大类: serif(衬线字体) sans-serif(非衬线字体) monospace (等宽字体) cursive (草书字体) fantasy (虚幻字体) 可以将字体设置为这些大的分类,当设置为大的分类以后,浏览器会自动选择指定的字体并应用样式. 一般会将字体的大分类,指定为font-family中的最后一个字体. <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&

Softmax回归(Softmax Regression, K分类问题)

Softmax回归:K分类问题, 2分类的logistic回归的推广.其概率表示为: 对于一般训练集: 系统参数为: Softmax回归与Logistic回归的关系 当Softmax回归用于2分类问题,那么可以得到: 令θ=θ0-θ1,就得到了logistic回归.所以实际上logistic回归虽然有2个参数向量,但这2个参数向量可以退化到1个参数向量.推广到K个类别,那么就需要K-1个参数向量 参数求解 类似于logistic regression,求最大似然概率,有: 其中1{k=y}为真值