@property的本质: @property=_ivar(成员变量)+getter/setter(存取方法);
每增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。
如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
在类的实现代码里通过@synthesize语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
@interface Animal : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, copy)NSString *character;
@end
----——————————————————————————
@implementation Animal
//@synthesize name = _name; //这一行可以不写,编译器默认
@dynamic character;
@end
我们直接运行代码
Animal*animal = [[Animalalloc]init];
animal.name=@"Cat";
animal.character=@"miao";
会crash:
2020-07-21 17:01:54.808369+0800 Test[61988:1011796] -[Animal setCharacter:]: unrecognized selector sent to instance 0x600001fac420
@synthesize 如果属性没有手动实现setter和getter方法,编译器会自动合成这两个方法,编译器还会自动向类中添加对应类型的以属性名前面加下划线命名的实例变量;当有自定义的存或取方法时,自定义方法会屏蔽自动生成的方法。所以我们可以正常读取使用@synthesize修饰的name;
@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。假如一个属性被声明为 @dynamic var,而没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = instance.var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
解决这种崩溃的方法有三种:
方法一:
直接注释掉@dynamic character;,由编译器默认为@synthesize,也就是由@dynamic改为了@synthesize,自动添加@setter方法和 @getter 方法。
方法二:
手动添加。@dynamic不能自动合成,那就手动添加getter/setter和成员变量:
@interface Animal()
{
NSString*_character; //添加成员变量 _var
}
@end
@implementation Animal
@dynamic character;
//添加setter方法
- (void)setCharacter:(NSString*)character{
_character= [character copy];
}
//添加getter方法
- (NSString *)character{
return _character;
}
@end
方法三:
runtime消息转发:
#import "Animal.h"
#import <objc/runtime.h> //需要import <objc/runtime.h>
@interface Animal()
{
//在C函数中不能直接使用实例变量,需要将Objc对象self转成C中的结构体,因此在Animal类同样需要显式声明实例变量而且访问级别是@public
@public
NSString*_character;
}
@end
@implementation Animal
@dynamic character;
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//消息转发
if(sel ==@selector(setCharacter:)){
class_addMethod([selfclass], sel, (IMP)setCharacter,"v@:@");
returnYES;
}else if(sel ==@selector(character)){
class_addMethod([selfclass], sel, (IMP)getCharacter,"@@:");
return YES;
}
return[super resolveInstanceMethod:sel];
}
void setCharacter(idself,SEL_cmd,NSString*character){
if(((Animal*)self)->_character!=character){
((Animal*)self)->_character=[character copy];
}
}
NSString *getCharacter(id self,SEL _cmd){
return((Animal*)self)->_character;
}