运行出错?调试通过了,说明DEBUG版本没问题,那么你运悔旁行的是DEBUG 还是RELEASE。仔细检碧拦橡查下,再把出错的地方描述清楚,才好解决。
1. 分类是可以通过关联对象的方式,间接的添加了成员变量。但实际上类但成员列表里面无该成猜帆员变量信息。
2. 表现上分析,分类底层的结构掘兆渗体无成员变量的字段。
3. 根本原因: 如果分类能添加成员变量,类对象objc_class的class_rw_t李的ro属性含有instanceSize,这样instanceSize的大小会变化。而分类的添加在运行时执行,这样判脊会改变对象的内存结构。
Property是属性y,Ivar是成员变量
其实分类中是可以为一个类添加属性的,但是一定做不到添加成员变量,不要混淆了成员变量和属性的概念
在一个分类中添加了一个属性,Xcode不会自动的为其生成一个下划线开头的成员变量及set和get方法,如果你没有手动的实现这两个方法,直接在外面通过点语法调用这个属性,肯定就直接挂了,Unrecognised selector send to instance,因为他压根就没有这两个方法,所以当你真的在分类中声明了一个属性的时候,就要手动的去实现这个属性的set和get方法,这个时候就要用到运行时机制了,关联上去这个属性的存取过程。
那么为什么不能给分类添加成员变量呢
成员变量是一个类的东西,分类本身就不是一个类,分类本来就是OC里面通过运行时动态的为一个类添加的一些方法和属性等,不是一个真正的类,你怎么给他添加成员答罩变量呢?
分类里面不能添加Ivar是因为分类本身并不是一个真正的类,它并没有自己的ISA。类最开始生成了很多基本属性,比如IvarList,MethodList,分类只会将自己的method attach到主类,并不会影响到主类的IvarList。这就是为什么分类里面不能增加成员变量的原因。
为什么不能添加成员变量呢?
Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:
typedef struct objc_class *Class
objc_class结构体的定义如下:
structobjc_class{Class isa OBJC_ISA_AVAILABILITY#if!__OBJC2__Class super_class OBJC2_UNAVAILABLE// 父类constchar*name OBJC2_UNAVAILABLE// 类名longversion OBJC2_UNAVAILABLE// 类的版本信息,默认为0longinfo OBJC2_UNAVAILABLE// 类信息,供运行期使用的一些位标识longinstance_size OBJC2_UNAVAILABLE// 该类的实例变量大小structobjc_ivar_list*ivarsOBJC2_UNAVAILABLE// 该类的成员变量链表structobjc_method_list**methodListsOBJC2_UNAVAILABLE// 方法定义的链表structobjc_cache*cacheOBJC2_UNAVAILABLE// 方法缓存structobjc_protocol_list*protocolsOBJC2_UNAVAILABLE// 协议链表#endif} OBJC2_UNAVAILABLE
在上面的objc_class结构体中,ivars是objc_ivar_list(成员变量列表)指针;methodLists是指向objc_method_list指针的指针。在Runtime中,objc_class结构体大小是固定的段拆,不可能往这个结构体中添加数据,只能修改。所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变握举枣量个数。methodList是一个二维数组,所以可以修改*methodLists的值来增加成员方法,虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)。因此,可以动态添加方法,不能添加成员变量。
在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是文档中特别说明:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
意思是说,这个函数只能在“ 构建一个类的过程中 ”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用objc_allocateClassPair之后,objc_registerClassPair之前才可以被使用,同样没有机会再添加成员变量。
Category不能添加成员变量(instance variables),那到底能不能添加属性(property)呢?
这个我们要从Category的结构体开始分析:
typedefstructcategory_t{constchar*name//类的名字classref_tcls//类structmethod_list_t*instanceMethods//category中所有给类添加的实例方法的列表structmethod_list_t*classMethods//category中所有添加的类方法的列表structprotocol_list_t*protocols//category实现的所有协议的列表structproperty_list_t*instanceProperties//category中添加的所有属性}category_t
从Category的定义也可以看出Category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性)和不可为(无法添加实例变量)。
那我们为什么经常听说说类别不能添加属性呢?实际上,Category实际上允许添加属性的,同样可以使用@property,但是不会生成_变量(带下划线的成员变量),也不会生成添加属性的getter和setter方法,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法。那我们想要实现我们平时的属性所具有的功能应该怎么样做呢?
其实我们可以使用runtime去做,使用runtime去实现Category为已有的类添加新的属性并生成getter和setter方法。不要忘记了Objective-C是动态语言。方法是通过runtime.h中objc_getAssociatedObject / objc_setAssociatedObject来访问和生成关联对象。这两个方法可以让一个对象和另一个对象关联,就是说一个对象可以保持对另一个对象的引用,并获取那个对象。
//NSObject+IndieBandName.h@interfaceNSObject(IndieBandName)@property(nonatomic,strong)NSString*indieBandName@end
上面是头文件声明,下面的实现的.m文件:
// NSObject+IndieBandName.m #import"NSObject+Extension.h"#importstaticconstvoid*IndieBandNameKey = &IndieBandNameKey@implementationNSObject(IndieBandName)@dynamicindieBandName- (NSString*)indieBandName {returnobjc_getAssociatedObject(self, IndieBandNameKey)}- (void)setIndieBandName:(NSString*)indieBandName { objc_setAssociatedObject(self, IndieBandNameKey, indieBandName, OBJC_ASSOCIATION_RETAIN_NONATOMIC)}@end
通过runtime的两种方法就可以为类别添加一个实例变量了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)