Objective-C:为什么分类中不能直接添加属性

게시일: 2019-03-16 14:21 980자 5 min read

前端项目通过bridge获取客户端资源,客户端直接返回response对象Win11 光标变白消失解决方案(浏览器开启硬件加速后,在鼠标滑到文本处出现)Terminal里用option/alt + 鼠标点击,就可以让光标到指定位置Duff's Device为 UIControl 实现线程安全的 Block 事件扩展:原理与实践前端代码规范设计模式k8s前端项目利用nginx部署项目不停机Vue3+Vite+history模式+Nginx 部署二级路径iOS开发工具常用代码模板2——数据结构常用代码模板1——基础算法iOS:特殊符号大全HomeBrew 更新报错“fatal- Could not resolve HEAD to a revision”SwiftUI基本控件代码大全——1.欢迎进入软件构建的世界C++ STL简介LeetCode 70 爬楼梯(青蛙跳台阶)深入理解计算机系统(第二版) 家庭作业 第二章Swift Module 如何被全局引用LeetCode 398 随机数索引Vue 的一些指令和缩写LeetCode 486 Predict the Winner(预测赢家)浅谈iOS中的weakCocoaPods组件化——OC/Swift动静态库混用OpenGL 术语当对象接收到不能处理的消息时调用的方法命令行通过ffmpeg剪切视频iOS:如何在UITableView调用reloadData刷新结束后再同步执行后续操作统计iOS工程代码行数CMake 入门及常用变量指令手册LeetCode 6 ZigZag Conversion(Z字转换)LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal(由中序和后序遍历建立二叉树)LeetCode 5 Longest Palindromic Substring(最长回文字串)LeetCode 8 String to Integer (atoi)Objective-C Type EncodingsObjective-C:为什么分类中不能直接添加属性MeshLab数据结构与算法解析习题2.23数据结构与算法解析习题2.19数据结构与算法解析习题2.16数据结构与算法解析习题2.14数据结构与算法解析习题2.13数据结构与算法解析习题2.12数据结构与算法解析习题2.11:二分查找数据结构与算法解析习题2.10:霍纳法则(Horner's rule)数据结构与算法解析习题2.7数据结构与算法解析习题1.3数据结构与算法解析习题1.2数据结构与算法解析习题1.1centos7 mysql数据库安装和配置Fishhook-源码分析解决Mac下 Cornerstone 无法查看SVN日志的问题UIButton扩大点击范围以及关于响应者链条的思考UILabel中文带行间距的处理,限制行数,计算高度等UITableview调用reload方法时抖动问题C语言指针小结C语言数据类型和const、static关键字iOS 截取整个 scrollview 图片WxWidgets类列表objc源码分析-runtime-classiOS自动化埋点的实现iOS平台编译Ogre游戏引擎库在 Mac OS X 上安装 TensorFlowiPhone 刘海机型UI适配(X、Xs、Xs Max、Xr)关于C语言中的union虚拟机 Linux 上编 slice3r perl Build.PL 报错iOS 沙盒与 BundleCOCOAPODS技巧-创建私有仓库iOS脚本打包 ipa(.app转.ipa)Objective-C 中禁止调用指定的方法iOS 关于 UITextField 的字数限制升级 Mac OS 10.13 High Sierra 后 ruby 报错iOS 框架学习-AsyncSocketSQL语句OC优缺点以及常见bugUIViewController 的生命周期runtime——运行时简单使用iPhone6 Plus上面神秘的缝隙CocoaPods安装和使用教程(更新至Mac High Sierra)UIApplicationiOS 网络小结NSString的各种处理HTTP请求报文和HTTP响应报文,以及工作原理Vim 快捷键整理OC中nil 、NULL、 Nil 、NSNull的区别GCD 使用指南iOS常用数据类型转换关于NSNotificationCenterGit 命令MAC OS 常用技巧Description方法和NSLog函数代码重构OC单例宏Block in Objective-CObjective-C 语法 3Objective-C 语法 2Objective-C 语法 1
이 게시물은 아직 한국어 번역이 없으므로 원문으로 표시돼요
为什么分类中不能直接添加属性,这个也是一个经典的面试题了。 搜了一些相关文章,再结合源码做了一个小小的总结。 其实通过这个问题,也可以一瞥 OC 中类的实现。 源码 先晒一下OC类和分类的部分源码: typedef struct objc_class *Class;struct objc_cla

为什么分类中不能直接添加属性,这个也是一个经典的面试题了。

搜了一些相关文章,再结合源码做了一个小小的总结。

其实通过这个问题,也可以一瞥 OC 中类的实现。

源码

先晒一下OC类和分类的部分源码:

typedef struct objc_class *Class;

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    ...
}


struct class_data_bits_t {
    ...

    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

    ...
}

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;
    
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

    ...
}

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

从上面可以看出来Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。 ivars是objc_ivar_list(成员变量列表)指针,其在objc_class中关系如下。

objc_class -> class_data_bits_t bits -> class_rw_t* data() -> const class_ro_t *ro -> const ivar_list_t * ivars;

再看一下Category的定义。

typedef struct category_t *Category;

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;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

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

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

从Category的定义也可以看出Category有实例方法,类方法,甚至可以实现协议,添加属性,但是没有实例变量。

分析

参考了位博主文章之后,这里再补充一下。

博主文章链接:http://www.jianshu.com/p/935142af6a47

通过上面的文章里面的详细介绍以及打印输出,可以看到,在一个分类中添加了一个属性,Xcode不会自动的为其生成一个下划线开头的成员变量及set和get方法。

如果你没有手动的实现这两个方法,直接在外面通过点语法调用这个属性,肯定就直接挂了,Unrecognised selector send to instance。

因为他就没有这两个方法,能不挂吗?所以当你真的在分类中声明了一个属性的时候,就要手动的去实现这个属性的set和get方法,这个时候就要用到运行时机制了,关联上去这个属性的存取过程。

结论

为什么不能为手动添加一个下划线开头的成员变量,然后在实现存取器方法的时候对这个成员变量来存取呢?非要用到所谓的运行时吗?

答案是因为:成员变量是一个类的东西,分类本身就不是一个类,它并没有自己的bits,以及后面的一些东西,分类本来就是OC里面通过运行时动态的为一个类添加的一些方法和属性等,不是一个真正的类,你怎么给他添加成员变量呢。

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

总结一下,其实分类中是可以为一个类添加属性的,但是一定做不到添加成员变量,不要混淆了成员变量和属性的概念。

只是说现在Xcode自动会给属性生成成员变量让大家对这个概念有点混淆。Property是Property,Ivar是Ivar。

有兴趣可以研究一下类是怎么被创建出来的,类最开始生成了很多基本属性,比如IvarList,MethodList,分类只会将自己的method attach到主类,并不会影响到主类的IvarList。

这就是为什么分类里面不能增加成员变量的原因。

参考

https://blog.csdn.net/JGL357/article/details/79127483 https://www.jianshu.com/p/8aa63f7e98d1 https://blog.csdn.net/mumuyinyin/article/details/72854579 https://tech.meituan.com/DiveIntoCategory.html