-
@property
相当于声明了setter和getter方法
ivar + getter + setter
对应runtime
底层的objc_property_t
struct property_t {
const char *name;
const char *attributes;
};
其中 attributes 本质是 objc_property_attribute_t
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
attributes具体内容包括类型 原子性 内存语义 实例变量
@property (nonatomic, strong) NSArray *array;
通过 property_getAttributes(property)
获取到 attributes
,完成属性的定义,编译器会自动编写访问这些属性的所需方法,这个过程叫做自动合成(autosynthesis)
这个过程是在编译期中完成的,编译器中看不到对应的合成方法(synthesized method)源代码,除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。
@property (nonatomic, strong) id selectControl;
会生成两个实例变量,其名称分别为 _selectControl或者在类的实现代码里通过 @synthesize 语法来指定实例变量的名字.
@synthesize _ selectControl = selectControl;
添加属性的过程:
1、在var_list(成员变量列表)中添加一个成员变量的描述
2、在method_list(方法列表)中添加setter getter方法的描述
3、在prop_list属性列表中添加属性的描述
-
@synthesize 和 @dynamic
1、@synthesize 语义是系统会自动帮你生成setter和getter方法,然后生成一个对应的实例变量名
@property (nonatomic, strong) id selectControl;
//生成一个_selectControl的实例变量名,_selectControl可以根据自己需求更改,
//如果不声明 @synthesize,则默认下方的操作
@synthesize _ selectControl = selectControl;
2、@dynamic 语义是用户要求自动生成setter getter方法,系统不会自动生成,所以此时调用setter或getter方法会crash,编译不报错(oc的动态绑定机制),而且调用对应的实例变量会报错
-
assign
用来修改基本数据类型和对象,不涉及内存管理,内存计数不变化,所以如果修饰对象类型的话(一般编译的时候会产生警告:Assigning retained object to unsafe property; object will be released after assignment)会出现野指针或者EXC_BAD_ACCESS错误,因为如果对象被释放后,不会对指针地址进行置nil操作 -
retain
引用计数+1,对应的setter方法
- (void) setStr:(NSString *)str{
if (_oldStr !=str){
[_oldStr release]
_oldStr = [str retain];
}
}
NSString *str0 = [str retain];
//str0 和 str 的变量地址相同,指向相同的内容地址,str 的retainCount增加1此对象只能用于Object-c对象类型,而不能用于Core Foundation对象。(基本数据类型 和 CoreFoundation 对象都没有引用计数)当把对象添加到 数组中时,被添加对象的引用计数+1
-
copy
1、指针复制,释放旧对象,然后生成一个计数为1的新对象
- (void) setStr:(NSString *)str{
if (_oldStr !=str){
[_oldStr release]
_oldStr = [str copy];
}
}
2、一般用来修饰符合NSCopying协议的类型
3、涉及到浅拷贝和深拷贝
1、对不可变对象执行copy操作,是浅拷贝(指针拷贝,内容相同),都指向同一边存储空间,源数据被修改,副本数据也会被修改
2、对不可变字符串执行mutCopy操作,是深拷贝(对象拷贝),两者指向不同的存储空间
3、可变字符串执行copy或mutCopy都是深拷贝
4、可变容器类执行copy或mutcopy或者不可变容器执行mutCopy都是不完全深拷贝,即只是容器对象指向不同的内存空间,内部的元素则指向同一个内存
5、可变数组执行copy(NSMutableArray执行copy后返回的NSArray),在使用过程回出现crash的问题
6、数组完全深拷贝需要执行initWithArray:copyItems: 方法
NSArray *deepCopy = [[NSArray alloc] initWithArray:array copyItems:YES];
4、NSString类型用copy修饰的原因
@interface obj : NSObject
@property (nonatomic,copy) NSString *str;
@end
//实例化后赋值
obj.str = copyStr;
如果str用strong修饰(strong赋值操作参考下方),而copyStr是可变类型,则上面的赋值操作后,str同copyStr一样指向同一块内存空间,这样如果copyStr发生改变,str也会跟着改变,数据就会出现被篡改的风险;
如果用copy修饰,copy对可变对象执行深拷贝,拷贝出一个新的内存空间,这样就互不影响了
-
weak
1、弱引用,不会持有对象,也就不会有引用计数的变化,一般用来避免循环引用的问题,如果对象被释放,则对应的指针变量会置nil(runtime消息发送机制,对nil发送消息返回空,不会出现异常)
2、weak底层是通过sideTab中的hash表进行管理的,通过对象指针作为key,对应的weak指针数组作为value进行存储,一旦对象被释放,就会通过该对象地址在表中进行查找对应的weak指针数组,然后遍历进行置nil操作
3、与assign的区别
weak修饰的对象销毁的时候,指针会自动设置为nil,而assign不会
4、xib/Storyboard中,系统会自动为控件赋strong,所以拖到代码一般用weak
-
strong
1、强引用,持有对象,引用计数+1,用于延长对象的生命周期
- (void) setStr:(NSString *)str{
if (_oldStr !=str){
[str retain ];
[_oldStr release];
_oldStr = str;
}
}
2、一般的指针变量默认是strong类型
-
atomic和nonatomic
1、原子属性,线程安全,读写有加锁(互斥锁)操作,防止多条线程同时访问同一块内存,但是需要消耗大量资源
2、atomic只是对属性的getter/setter方法进行了加锁操作,这种安全仅仅是set/get 的读写安全,并非真正意义上的线程安全,因为线程安全还有读写之外的其他操作(比如:如果当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash)
3、非原子属性,不加锁,当多个线程同时访问同一个属性,会出现无法预料的结果,但是性能更好