[TOC]
本质是什么
实际上@property = 实例变量 + get方法 + set方法。也就是说属性
@property name: setter方法+getter方法+_name
自动合成
定义一个property,在编译期间,编译器会生成实例变量,getter方法、setter方法,这些方法是通过自动合成(autosynthesize)的方式生成并添加到类中。
实际上,一个类经过编译后,会生成变量列表ivar_list,方法列表,method_list。每添加一个属性,在变量列表ivar_list会添加对应的变量,如_name,在方法列表method_list中会添加对应的setter和getter方法。
@synthesize name = _name;
手动合成
那么有自动合成,就有手动合成。
手动合成代码
@dynamic sex;
这样就告诉编译器,sex属性的实例变量,getter方法、setter方法都由开发者自己添加
//下面代码不添加,在使用该属性时候,程序会崩溃
@interface Student : NSObject
{
NSString *_sex;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@end
整体代码
@property (nonatomic,copy) NSString *name;///< name
@end
@implementation ViewController
/*
两个一起写会报错,因为重写了get和set方法,编译器认为开发者想手动管理@property,此时会将@property作为@dynamic来处理
因此也就不会自动生成变量。解决方法,显示的将属性和一个变量绑定:@synthesize name = _name;
@synthesize name = _name;
*/
@synthesize name = _name;
- (void)setName:(NSString *)name{
_name = name;
}
//get 不能使用self. 会造成死循环
-(NSString *)name{
return _name;
// return self.name; // 错误的写法,会造成死循环
}
@property修饰符
- 修饰符的种类
- 原子性 -nonatomic, atomic
- 读写权限 -readwrite和readonly
- 内存管理 - assign,strong,weak,copy,unsafe_unretained
- set,get方法
- weak和assign的区别
- Weak修饰对象,对象释放后,引用计数为0,对象会被置为nil。
- assign修饰基本数据类型,如果用assign修饰对象,对象释放后,引用计数为0后,对象会变成野指针,容易崩溃。为什么基本数据类型不会,因为基本数据类型是在栈上面,不是在堆上面的。
- copy和strong
- 不可变使用copy,可变使用strong
不可变使用strong的例子
@property (nonatomic, strong) NSString *strongStr;
- (void)testStrCopy {
NSString *tempStr = @"123";
NSMutableString *mutString = [NSMutableString stringWithString:tempStr];
self.strongStr = mutString; // 子类初始化父类
NSLog(@"self str = %p mutStr = %p",self.strongStr,mutString); // 两者指向的地址是一样的
[mutString insertString:@"456" atIndex:0];
NSLog(@"self str = %@ mutStr = %@",self.strongStr,mutString); // 两者的值都会改变,不可变对象的值被改变
/*
self str = 0x281806520 mutStr = 0x281806520
self str = 456123 mutStr = 456123
*/
}
可变使用copy的例子
@property (nonatomic, copy) NSMutableString *mutString;
- (void)testStrCopy {
NSString *str = @"123";
self.mutString = [NSMutableString stringWithString:str];
/*
// 首先声明一个临时变量 NSMutableString *tempString = [NSMutableString stringWithString:str];
// 将该临时变量copy,赋值给self.mutString self.mutString = [tempString copy];
通过[tempString copy]得到的self.mutString是一个不可变对象
*/
NSLog(@"str = %p self.mutString = %p",str,self.mutString); // 两者的地址不一样
[self.mutString appendString:@"456"]; // 会崩溃,因为此时self.mutArray是NSString类型,是不可变对象
}
可变对象的copy、mutableCopy,可以看到,只要是可变对象,无论是集合对象,还是非集合对象,copy和mutableCopy都是深拷贝。
- (void)testMutableCopy
{
NSMutableString *str1 = [NSMutableString stringWithString:@"abc"];
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"str1 = %p str2 = %p str3 = %p",str1,str2,str3);
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"a",@"b", nil];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"array1 = %p array2 = %p array3 = %p",array1,array2,array3);
}
2018-12-13 19:12:44.287404+0800 testcopy[2654:1672606] str1 = 0x28020fba0 str2 = 0x92e1881c542bdf17 str3 = 0x28020f780
2018-12-13 19:12:44.287475+0800 testcopy[2654:1672606] array1 = 0x28020f8d0 array2 = 0x280c5e780 array3 = 0x28020fa20
不可变对象的copy、mutableCopy,不可变对象的copy是浅拷贝,mutableCopy是深拷贝。以NSString和NSArray为例,测试代码如下:
- (void)testCopy
{
NSString *str1 = @"123";
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"str1 = %p str2 = %p str3 = %p",str1,str2,str3);
NSArray *array1 = @[@"1",@"2"];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"array1 = %p array2 = %p array3 = %p",array1,array2,array3);
}
2018-12-13 19:14:27.982467+0800 testcopy[2656:1673120] str1 = 0x102fc8078 str2 = 0x102fc8078 str3 = 0x280558f00
2018-12-13 19:14:27.982529+0800 testcopy[2656:1673120] array1 = 0x280b09ea0 array2 = 0x280b09ea0 array3 = 0x280558fc0
自定义对象如何支持copy:
自定义对象遵守NSCopying协议,且实现copyWithZone方法,NSCopying是系统方法,直接使用即可。
@interface Student : NSObject
{
NSString *_sex;
}
@property (atomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) int age; @end
- (instancetype)initWithName:(NSString *)name age:(int)age sex:(NSString *)sex
{
if(self = [super init]){
self.name = name;
_sex = sex;
self.age = age;
}
return self;
}
- (instancetype)copyWithZone:(NSZone *)zone
{
// 注意,copy的是自己,因此使用自己的属性
Student *stu = [[Student allocWithZone:zone] initWithName:self.name age:self.age sex:_sex];
return stu;
}
输出
- (void)testStudent
{
Student *stu1 = [[Student alloc] initWithName:@"Wang" age:18 sex:@"male"];
Student *stu2 = [stu1 copy];
NSLog(@"stu1 = %p stu2 = %p",stu1,stu2);
}
stu1 = 0x600003a41e60 stu2 = 0x600003a41fc0
//深复制,因为是内存复制,copyWithZone,而不是直接内存地址指向。
来源:iOS面试之@property