iOS:@property和@synthesize
定义一个很简单的类:
// .h
#import <Foundation/Foundation.h>
@interface SimpleClass : NSObject {
NSString *simpleName;
}
@end
// .m
#import "SimpleClass.h"
@implementation SimpleClass
@end
运行clang -rewrite-objc SimpleClass.m
查看生成的SimpleClass.cpp文件:
struct SimpleClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *simpleName;
};
/* @end */
// @implementation SimpleClass
// @end
SimpleClass_IMPL
是SimpleClass类的实现,里面保存了simpleName
变量,但这个时候没有getter和setter方法。
我们把{NSString *simpleName;}
改成@property (nonatomic, copy) NSString *simpleName;
再重新编译:
struct SimpleClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_simpleName;
};
// @property (nonatomic, copy) NSString *simpleName;
/* @end */
// @implementation SimpleClass
static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName));
}
...
static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, _simpleName), (id)simpleName, 0, 1);
}
// @end
可以看到编译器帮我们生成了名为_simpleName
的成员变量,同时生成了对应的getter/setter
方法。
getter
方法里面利用OBJC_IVAR_$_SimpleClass$_simpleName
偏移量来计算_simpleName的位置,来看看OBJC_IVAR_$_SimpleClass$_simpleName
的定义:
extern "C" unsigned long int OBJC_IVAR_$_SimpleClass$_simpleName __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct SimpleClass, _simpleName);
是通过__OFFSETOFIVAR__
来计算偏移量的。
setter
方法里面,通过调用objc_setProperty
方法来设置值。如果我们把simpleName
的属性类型改为strong
再重新编译:
static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
(*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName)) = simpleName;
}
可以看到setter
函数的实现也改变了。
我们在@Implementation里面添加一行代码:
@implementation SimpleClass
@synthesize simpleName = newSimpleName;
@end
再重新编译,则看到原本的_simpleName
改为了newSimpleName
:
extern "C" unsigned long OBJC_IVAR_$_SimpleClass$newSimpleName;
struct SimpleClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *newSimpleName;
};
// @property (nonatomic, copy) NSString *simpleName;
/* @end */
// @implementation SimpleClass
// @synthesize simpleName = newSimpleName;
static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) {
return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$newSimpleName));
}
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, newSimpleName), (id)simpleName, 0, 1);
}
所以可以用@synthesize
来修改生成的成员变量的名字。
如果我们定义的属性可读或可写而同时又自己复写了getter或setter方法,这时编译器不会自动帮我们生成成员变量,这时我们需要自己通过@synthesize来添加成员变量。所以当我们手动添加了getter/setter方法时,我们需要利用@synthesize
来自动生成成员变量。
在协议和category里面实现了property属性时也不会自动生成成员变量。如果一个类实现了带有property的协议,那它必须自己通过@synthesize
来自动生成成员变量。
所以什么时候会用到
@synthesize
:
1、需要修改成员变量的命名;
2、无法自动生成成员变量:
- 2.1、复写了可读或可写属性的getter或setter方法;
- 2.2、属性是协议里面定义的
当我们在子类和父类里面定义相同的属性时,看看会发生什么:
@interface SimpleClass : NSObject
@property (nonatomic, copy) NSString *simpleName;
@end
@interface SubSimpleClass : SimpleClass
@property (nonatomic, copy) NSString *simpleName;
@end
编译后的代码:
struct SimpleClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_simpleName;
};
// @property (nonatomic, copy) NSString *simpleName;
/* @end */
...
struct SubSimpleClass_IMPL {
struct SimpleClass_IMPL SimpleClass_IVARS;
};
// @property (nonatomic, copy) NSString *simpleName;
/* @end */
// @implementation SimpleClass
static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, _simpleName), (id)simpleName, 0, 1); }
// @end
// @implementation SubSimpleClass
// @end
可以看到子类中并没有自动生成成员变量,也没有生成getter/setter方法。