Category为什么不能添加属性

QUIC 协议域名封堵:核心原理、关键技术与实现路径(C/C++代码实现) 随着 QUIC 协议在互联网领域的广泛应用,其基于 UDP 的传输特性与加密设计,打破了传统 TCP 协议下的域名封堵逻辑,给网络合规管理与安全防护带来新的技术难题。本文将脱离具体代码实现,聚焦 QUIC 协议域名封堵的核心原理、关键技术环节与实际落地路径,从协议特性本质出发,解析如何在遵循 RFC 9000、RFC 9001 等规范的前提下,实现对 QUIC 流量的精准域名封堵。 阅读详情

分类中能不能定义实例变量,为什么?
答案:不能。类的内存布局在编译时期就已经确定了,category是运行时才加载的早已经确定了内存布局所以无法添加实例变量,如果添加实例变量就会破坏category的内部布局。
继续追问:
1:为什么说category是在运行时加载的?
2:不能添加实例变量,那为什么能添加属性? 

先来看看在runtime中的结构体的样子

在分类转化为c++文件中可以看出_category_t结构体中,存放着类名,对象方法列表,类方法列表,协议列表,以及属性列表。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc CategoryName.m

 

1:category小括号里写的名字
2:要扩展的类对象,编译期间这个值是不会有的,在app被runtime加载时才会根据name对应到类对象
3:这个category所有的-方法
4:这个category所有的+方法
5:这个category实现的protocol,比较不常用在category里面实现协议,但是确实支持的
6:这个category所有的property,这也是category里面可以定义属性的原因,不过这个property不会@synthesize实例变量,一般有需求添加实例变量属性时会采用objc_setAssociatedObject和objc_getAssociatedObject方法绑定方法绑定,不过这种方法生成的与一个普通的实例变量完全是两码事。
这里已经可以回答第二个问题了。

再看catagory如何添加进runtime

其实在main函数之前,将runtime通过dyld动态加载进来的时候生效的。怎么验证,再来看runtime源码:
先从objc_init开始,其中大量出现的image并不是图片,而是一个二进制文件(可执行文件或 so 文件),里面是被编译过的符号、代码等,所以 ImageLoader 作用是将这些文件加载进内存,且每一个文件对应一个ImageLoader实例来负责加载。

 

void _objc_init(void)
{
    // 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);
} 

在load_images之后调用_read_images方法初始化map后的image,这里面干了很多的事情,像load所有的类、协议和category。
再仔细看category的初始化: 

/ 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);
            // 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);
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)",
                                 getName(cls), cat->name);
                }
            }
        }
    } 

__objc_catlist,就是上面category存放的数据段。
以上代码做的事:
1把category的实例方法、协议以及属性添加到类上。
2把category的类方法和协议添加到类的metaclass上。
具体怎么做,主要是两个方法addUnattachedCategoryForClass和remethodizeClass。
addUnattachedCategoryForClass实现映射,remethodizeClass去做具体操作。
再往下看category的各种列表是怎么最终添加到类上的。
点开attachCategoryMethods方法可以看到它将所有category的实例方法列表拼成了一个大的实例方法列表,再通过attachMethodLists去加到方法列表里

static void 
attachCategoryMethods(class_t *cls, category_list *cats,
                      BOOL *inoutVtablesAffected)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    BOOL isMeta = isMetaClass(cls);
    method_list_t **mlists = (method_list_t **)
        _malloc_internal(cats->count * sizeof(*mlists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int i = cats->count;
    BOOL fromBundle = NO;
    while (i--) {
        method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= cats->list[i].fromBundle;
        }
    }

    attachMethodLists(cls, mlists, mcount, NO, fromBundle, inoutVtablesAffected);

    _free_internal(mlists);

} 

结论:
1)category的方法并没有“完全替换掉”原来类已经有的方法,而是把扩展的方法放入到方法列表的前头,举个栗子(原来的方法列表<a,b,c,>,扩展的方法是<1,2,3>,会变成<1,2,3,a,b,c>。)
2)为什么平常所说的category的方法会“覆盖”掉原来类的同名方法,就是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,而且只要一找到对应名字的方法,就会结束查找。

另外的一些疑问

问:Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
答:Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法

问:load、initialize的区别,以及它们在category重写的时候的调用的次序。
答:区别在于调用方式和调用时刻
调用方式:load是根据函数地址直接调用,initialize是通过objc_msgSend调用
调用时刻:load是runtime加载类、分类的时候调用(只会调用1次),initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)

调用顺序:先调用类的load方法,先编译那个类,就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法,先编译的分类优先调用load方法。initialize先初始化父类,之后再初始化子类。如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),如果分类实现了+initialize,就覆盖类本身的+initialize调用。

反观扩展(extension),作用是为一个已知的类添加一些私有的信息,必须有这个类的源码,才能扩展,它是在编译器生效的,所以能直接为类添加属性或者实例变量。

protocol添加属性会怎么?

protocol是一系列的协议,要求代理去实现,自己并没有实现,方法或属性都是这样,只是做了声明要求代理去实现。所以添加的属性也只是声明其代理有实现这个属性,自身并没有实现其getter、setter以及ivar。

我们来试一下:

未实现警告
当我们在协议中定义一个必须实现(@ required修饰)的属性以后,如果实现类没有对这个属性做任何实现那么XCode中实现类中就会发出警告

方式一:在.m实现类中添加@synthesize speed;

@protocol FlyDelegate <NSObject>

@required
@property(nonatomic, assign) NSUInteger speed;

@end

@interface TestProtocolProperty : NSObject<FlyDelegate>

@end

@implementation TestProtocolProperty

@synthesize speed;

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}

@end

方式二:在.m实现文件中添加合成speed属性的成员变量_speed和对应的getter和setter方法

@protocol FlyDelegate <NSObject>

@required
@property(nonatomic, assign) NSUInteger speed;

@end

@interface TestProtocolProperty : NSObject<FlyDelegate> {
    NSUInteger _speed;
}
@end

@implementation TestProtocolProperty

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}

- (void)setSpeed:(NSUInteger)speed {
    _speed = speed;
}

- (NSUInteger)speed {
    return _speed;
}

@end

结论:OC语言的协议里面是支持定义属性的,而在协议中定义属性其实和在其中定义方法一样只是定义了getter和setter方法,并没有具体实现,所以当这个协议属性修饰符为@ required时,如果不实现编译器就会报出警告,最简单的方式就是加上属性同步语句@synthesize propertyName;

思考:属性和方法其实都是一个事物的特性,协议正是描述某类行为和特性的一种规范,基于这个事实,所以在协议中定义属性是很符合道理的。之所以在iOS开发中很少看到有人这么使用过是因为,iOS开发中协议通常是被用作代理模式而存在的,并且如果在协议中定义了是属性,就必须在实现类中添加对属性自动同步或者手动添加属性实现代码


 

 

别再为数据发愁:5分钟理解事件相机原理,并自己动手生成事件图像 本文深入浅出地解析了事件相机的工作原理,并提供了从静态图像生成事件数据的实用方法。通过Python代码示例,读者可以快速掌握如何模拟事件相机的输出,应用于机器人导航、自动驾驶等领域,解决数据获取难题。 阅读详情

相关推荐

团子杂记:SAP收入确认工具RAR(Revenue Recognition&Reporting)在新收入准则下的应用

SAP收入确认工具RAR知识分享

Lucas1211的博客 4283

category为什么不能添加属性?

Category不能添加成员变量,可以添加属性,但是属性要手动实现setter和getter方法。 Category的原理 简单地说就是通过runtime动态的吧Category中的方法等添加到类中, 从category的定义也可以看出category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性)和不可为(无法添加实例变量)。 经过编译的类在程序启动后就被runtime加载,没...

bowei1105的博客 741

vietjetair 航空的webpack多参数生成学习

工作内不是特别多,居安思危,有一个抓机票信息的公司面试前竟然需要做面试题,这就是一个题。看了下,有点麻烦,webpack 最初不得要领,看了下资料,找了个框架,练了两个简单的,搞这个吧。入库需要自己找,有几个参数不是明文的,算法不是很复杂,位置不太好找。整体难度一般。

liu_yueyang的博客 1510

iOS面试:9、为什么Category 不能添加属性,为什么还要设计ro,直接添加不是很方便么,如果将flag改变ro能不能写入,改变了会怎样?

在一个分类中添加了一个属性,Xcode 不会自动的为其生成一个下划线开头的成员变量及 set和get方法,如果你没有手动的实现这两个方法,直接在外面通过点语法调用这个属性,肯定就直接挂了,Unrecognised selector send to instance,因为他压根就没有这两个方法,所以当你真的在分类中声明了一个属性的时候,就要手动的去实现这个属性的set和get方法,这个时候就要用到运行时机制了,关联上去这个属性的存取过程。ro是为了存储编译后的类的内存结构,直接加会改变内存结构。

androiios的博客 343

滚动时域优化方法

滚动时域优化(Receding Horizon Optimization, RHO),也被广泛称为模型预测控制(Model Predictive Control, MPC),是一种用于动态系统优化和控制的。其核心思想是“在每个当前时刻k,控制器只考虑未来一个(称为“预测时域”或“优化时域”,长度N)内的系统行为。(通过测量或估计得到)。(描述状态如何随输入和干扰变化)。(如果有,可能基于历史数据或假设)。在这个有限的未来时域[k, k+N-1]上,求解一个。这个优化问题的目标是找到一个。

m0_69522810的博客 2291

STM32-TIM4-定时器中断

#include "project.h" #include "timer.h" //TIM4_Init(2000 ,72);//定时2ms /* 定时器中断的定时时间设定 定时器只需要配置时钟、TIM和NVIC即可 */ void TIM4_Init(u16 period, u16 prescaler) { NVIC_InitTypeDef NVIC_InitStructure; TIM_...

snaygo 6204

C# 为自定义控件(或类)的方法属性添加注释

一、在Form的属性窗口中显示属性及属性说明 [Description("容器分辨率"), Category("自定义")] public Size PanelSize { get { return _PanelSize; } set { _PanelSize = value; } } ...

wugengjie的博客 4606

PTA 编程题(C语言)-- 然后是几点

PTA 编程题(C语言)-- 然后是几点

LCCprogrammer 5074

Category详解-本质分析

文章目录前言一、Category的底层结构1.底层结构2.Category加载处理过程3. +load方法调用 前言 Category在系统编译的时候会把分类中属性,对象方法,类方法分别放到一个大数组中,此时属于2个独立的个体。 等程序运行的时候会运用系统runtime,进行合并,把对象方法,属性,协议信息放到类对象中,把类方法放到元类对象中 提示:以下是本篇文章正文内容,下面案例可供参考 一、Category的底层结构 1.底层结构 2.Category加载处理过程 通过runtime加载某个类的所

Ares称雄 2194

使用下一代的嵌入式IDE-PlatformIO 教程

1.生成工程 1.1 打开STM32CubeMX: 选择你的MCU,这里我用的是STM32F103RC RCC中开启外部HSE时钟,外部时钟比HSI更稳定些 开启DEBUG 4线 打开串口1用于打印调试信息,波特率115200,校验位0,停止位1 打开串口2用于MODBUS协议,波特率9600,校验位0,停止位1 接入RT-thread:参考官方文档 基于 CubeMX 移植 RT-Thread Nano 根据官方文档,需要1.取消生成HardFault_Handler、PendSV_Handler、Sy

jiladahe1997的博客 3万+

多维时空数据介绍(4)多维栅格数据构建

上图中包含了经度、纬度和时间三个维度,其变量为经度、纬度、时间以及降雨量,对应的变量单位为东经多少度、北纬多少度、阳历1895-1-1日开始多少天以及每年降雨多少毫升,其中经度数值4个、纬度数值3个、时间数值2个都是一维的数据,而降雨量是一个包含经度、纬度、时间的三维数组,数值有4*3*2=24个。在GeoScene 中NetCDF数据可以用做多种多维时空数据的数据源,进行创建、可视化和分析,它可以是多维栅格数据、时空立方体,还可以是体元图层,区别在于其所要表示的内容和目的。变量又分为坐标变量和属性变量。

你好,我是小张同学! 1286

iOS 分类 category(一)

Category是Objective-C 2.0之后添加的语言特性,分类、类别其实都是指的Category。Category的主要作用是为已经存在的类添加方法。 Objective-C 中的 Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。 分类优点 声明私有方法 分解体积庞大的类文件 把Framework私有方法公开 模拟多继承(...

teropk的博客 485

ORB-SLAM2算法部署验证、原理代码和各种改进方法汇总

本文主要汇总个人学习使用ORB-SLAM2算法中参考的相关博客和代码汇总,以及在ORB-SLAM2算法基础上各个改进方法的汇总。

重点记录机器人和自动驾驶相关领域的技术,尤其是计算机视觉,视觉SLAM等,不仅有前言文献或必备算法的学习,也会有实际应用的点滴经验 742

由category为什么不能添加属性所想到的

在category里添加属性的情况?在类中使用@property,系统会自动生成带“_”的成员变量和该变量的setter和getter方法。也就是说,属性相当于一个成员变量加getter和setter方法在分类里使用@property声明属性,只是将该属性添加到该类的属性列表,但是没有生成相应的成员变量,也没有实现setter和getter方法。这篇博文将的很详细 : 点我 不足之处还是没有讲出类

lwq718691587的博客 3880

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

为什么不能在category 中添加属性

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34380781的博客 224

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

探究iOS分类(category)为什么不能直接添加属性

既然要探究的是分类,那么我们先看一下分类的定义 //Category表示一个结构体指针的类型 typedef struct objc_category *Category; struct objc_category { char * _Nonnull category_name OBJC2_UNAVAILABLE; ch...

lixuezhi 8652

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

ios-基础之【11】-类的category

原文链接:http://www.jianshu.com/p/535d1574cb86 参考文:http://www.cnblogs.com/crazypebble/p/3439261.html 类别的简介 在开发中有时会用到Category,类别有三个作用:(1)可以将类的实现分散到多个不同文件或多个不同框架中,方便代码管理。也可以对框架提供类的扩展(因为框架类没有源码,不能修改)。

jkxiaoxue1218的博客 436

【iOS】关联对象-在Category中添加属性

Category Category是Objective-C 2.0之后添加的语言特性,分类、类别 其实都是指的Category。 Category的主要作用是为已经存在的类添加方法。也可以说是将庞大的类代码按逻辑划入几个分区。 Objective-C 中的 Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。...

kochunk1t的博客 1581

为什么category可以添加方法不能添加变量

OC是不能直接修改对象的结构体的成员变量属性,一般做法是先取出对象的结构体变量,修改该取出来的结构体变量里面的成员,再把结构体对象赋值给原来的结构体变量,在UI中为了简化代码一般对UIView做一个category,这个分类把上面修改结构体成员变量的方法封装起来, @interface UIView (Extension) @property(nonatomic,assign)CGFloat x

小猴子 2534

iOS 用宏在category里给对象添加属性

奇技淫巧 指过于奇巧而无益的技艺与制品. 转载请注明出处 http://blog.youkuaiyun.com/uxyheaven/article/details/46391771众所周知,一般的情况下我们是没办法在category里加属性的, 如果想加呢,需要用到Associated

晓月的专栏 5738

iOS中OC给Category添加属性

利用Runtime的关联对象技术给Categroy添加属性

Minecraft 1万+

OC中category(分类)中添加属性

OC中category(分类)中添加属性 因为OC中无法添加成员变量并且添加的属性不会被保存,所以需要重写get和set方法来实现属性的保存。 //setter //self表示属性拥有者是当前类, //str表示属性名 //urlString表示属性值 //OBJC_ASSOCIATION_COPY_NONATOMIC表示属性类型为(copy,nonatomic) objc_setAssociatedObject(self, "str", urlString, OBJC_ASSOCIATION_COPY_

Le_1M的博客 1837

分类(Category) 中添加属性

在苹果的规定中分类(Category)中是不可以添加自定义属性的, why? 1.分类为什么不能拓展成员变量? 使用alloc初始化的时候,一些系统自定义的类型都有固定的大小,因为他们都有系统定义的固有的成员变量,init就是为这些成员变量赋值 如果,我们在分类中拓展成员变量,那么使用alloc开辟空间的时候,就无法检测到自定义的成员变量,所以使用init开辟空间的时

Zok93 1万+

ios用category添加属性

转载自简书,原文链接:http://www.jianshu.com/p/00f84e05be49,感谢 首先说明一下,直接在Category中是不能添加属性的,就算在.m文件中实现了相应的getter和setter方法,调用的时候也是会报错的。 首先看下报错情况 Category添加字段(常规方法) 编写Category头文件,以UIImage为例 // //

kaishener的博客 532

iOS Category 添加属性实现原理 - 关联对象

iOS Category 添加属性实现原理 - 关联对象 RunTime为Category动态关联对象 使用RunTime给系统的类添加属性,首先需要了解对象与属性的关系。对象一开始初始化的时候其属性为nil,给属性赋值其实就是让属性指向一块存储内容的内存,使这个对象的属性跟这块内存产生一种关联。 那么如果想动态的添加属性,其实就是动态的产生某种关联就好了。而想要给系统的类添加属性,只能通过分类...

weixin_30457551的博客 268

Intent中的四个重要属性——Action、Data、Category、Extras

Intent中四个重要属性 action,data,category,extras

qq_44318789的博客 1675
上一篇: EXC_BAD_ACCESS的本质详解以及僵尸模式调试
下一篇: AutoreleasePool 理解
LIN1986LIN
博客等级 码龄19年 17粉丝 93原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值