iOS 基础篇 -《@property,深拷贝vs浅拷贝》

以下笔记内容仅供个人参考,如有理解错误,请高抬贵手,仙人指路,互相学习进步...

大纲:

1、@property是什么?
2、@property常见写法
3、手动创建存储器(繁琐)
4、使用@Property自动创建存取器(快捷)
5、@property指示符
6、深拷贝与浅拷贝、copy、mutableCopy
7、知识点总结:
8、参考链接

1、@property是什么?

理解1
@property = ivar + getter + setter;就是通过设置property属性,编译器给我们创建了一个ivar对象,并且实现了一个get方法,和一个set方法。并且我们可以通过点语法进行获取或设置属性的值.
理解2
@Property是声明属性的语法,它可以快速方便的为实例变量创建存取器,并允许我们通过点语法使用存取器//注:存取器(accessor):指用于获取和设置实例变量的方法。用于获取实例变量值的存取器是getter,用于设置实例变量值的存取器是setter

2、@property常见写法

@property (nonatomic, strong) NSMutableString * mutableString;
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
@property (nonatomic,strong, nonnull) NSMutableArray *mutableArray;
@property (nonatomic, readonly) UITableViewStyle style;
@property (nonatomic, readwrite) UITableViewStyle style;
@property (nonatomic, readwrite,setter=setMyStyle,getter=getMyStyle) NSString* style;

3、手动创建存储器(繁琐)

// Car.h
@interface Car : NSObject{
    // 实例变量
    NSString *carName;
    NSString *carType;
}
// setter                                   
- (void)setCarName:(NSString *)newCarName; 
// getter
- (NSString *)carName;
// setter
- (void)setCarType:(NSString *)newCarType;
// getter
- (NSString *)carType;
@end
#import "Car.h"
@implementation Car
// setter
- (void)setCarName:(NSString *)newCarName{
    carName = newCarName;
}
// getter
- (NSString *)carName{
    return carName;
}
// setter
- (void)setCarType:(NSString *)newCarType{
    carType = newCarType;
}
// getter
- (NSString *)carType{
    return carType;
}
@end
使用方式
#import "Car.h"
int main(int argc, char * argv[])
{
    @autoreleasepool {
        Car *car = [[Car alloc] init];
        [car setCarName:@"Jeep Cherokee"];
        [car setCarType:@"SUV"];
        NSLog(@"The car name is %@ and the type is %@",[car carName],[car carType]);      
    }
    return 0;
}
小结:上面代码中创建了两个实例变量carName,carType,通过自定义set,get方法进行存取操作。过程太过繁琐了

4、使用@Property自动创建存取器(快捷)

@interface Car : NSObject
@property(nonatomic,copy) NSString *carName;
@property(nonatomic,copy) NSString *carType;
@end
// Car.m
#import "Car.h"
@implementation Car

//编译器会帮我们自动生成_carName和_carType这两个实例变量,.m文件中的其他代码就可以正常使用这两个变量了
//注意:系统是默认隐藏的,我们看不见,假如需要替换如@synthesize carName = myCarName;
@synthesize carName = _carName;
@synthesize carType = _carType;
@end
使用方式
点语法(推荐)
Car *car = [[Car alloc] init];
car.carName = @"Jeep Compass";
car.carType = @"SUV";
NSLog(@"The car name is %@ and the type is %@",car.carName,car.carType); 
或者(自行调用)
[car setCarName:@"Jeep Cherokee"];
[car setCarType:@"SUV"];
NSLog(@"The car name is %@ and the type is %@",[car carName],[car carType]);      
小结:上面代码中使用@property创建了两个实例变量carName,carType,编译器自动生成set,get方法。过程快速便捷,同时,我们可以使用点语法进行存取变量。

5、@property指示符

在声明属性的时候一般会带上几个指示符,常用指示符有

atomic nonatomic
readwrite readonly
strong
copy
assign
weak
retain
还可以设置getter和setter对其重命名

atomic、nonatomic原子、非原子

指定合成存取方法是否为原子操作,可以理解为是否线程安全,但在iOS上即时使用atomic也不一定是线程安全的,要保证线程安全需要使用锁机制,超过本文的讲解范围,可以自行查阅。
可以发现几乎所有代码的属性设置都会使用nonatomic,这样能够提高访问性能,在iOS中使用锁机制的开销较大,会损耗性能。

readwrite、readonly 可读、只读

readwrite是编译器的默认选项,表示自动生成getter和setter,如果需要getter和setter不写即可。
readonly表示只合成getter而不合成setter。

strong、copy、assign、weak、retain

strong修饰表示属性对所赋的值持有强引用表示一种“拥有关系”(owning relationship),会先保留新值即增加新值的引用计数,然后再释放旧值即减少旧值的引用计数。只能修饰对象。如果对一些对象需要保持强引用则使用strong。

copy修饰 修饰的属性会在内存里拷贝一份对象,两个指针指向不同的内存地址。
修饰不可变类型子类的对象,用copy
如:NSString,NSArray,NSDictionary等。
当修饰可变类型的属性时,用strong,如NSMutableArray、NSMutableDictionary、NSMutableString。

对于可变对象类型,如NSMutableString、NSMutableArray等则不可以使用copy修饰,因为Foundation框架提供的这些类都实现了NSCopying协议,使用copy方法返回的都是不可变对象,如果使用copy修饰符在对可变对象赋值时则会获取一个不可变对象,接下来如果对这个对象进行可变对象的操作则会产生异常,因为OC没有提供mutableCopy修饰符,对于可变对象使用strong修饰符即可。具体栗子如下:

       //使用copy修饰NSMutableString
       @property (nonatomic, copy) NSMutableString* name;
      
        //copy修饰会返回不可变类型。
        p.name = [[NSMutableString alloc] initWithString:@"Jiaming Chen"];
        //修改p.name,此时抛出异常
        [p.name appendString:@" is a good guy."];

上面的栗子使用copy修饰可变对象,在进行赋值的时候会通过copy方法获取一个不可变对象,因此p.name的地址和s的地址不同,而p.name运行时类型为NSString,调用appendString:方法会抛出异常。

weak修饰表示对所赋的值对象持有弱引用表示一种“非拥有关系”(nonowning relationship),对新值不会增加引用计数,也不会减少旧值的引用计数。所赋的值在引用计数为0被销毁后,weak修饰的属性会被自动置为nil能够有效防止野指针错误。
weak常用在修饰delegate等防止循环引用的场景

assign修饰表示对属性只进行简单的赋值操作,不更改所赋的新值的引用计数,也不改变旧值的引用计数,常用于标量类型,如NSInteger,NSUInteger,CGFloat,NSTimeInterval等
注:assign也可以修饰对象如NSString等类型对象,上面说过使用assign修饰不会更改所赋的新值的引用计数,也不改变旧值的引用计数,如果当所赋的新值引用计数为0对象被销毁时属性并不知道,编译器不会将该属性置为nil,指针仍旧指向之前被销毁的内存,这时访问该属性会产生野指针错误并崩溃,
因此使用assign修饰的类型一定要为标量类型。

unsafe_unretained修饰使用unsafe_unretained修饰时效果与assign相同,不会增加新值的引用计数,也不会减少旧值的引用计数(unretained)当所赋的值被销毁时不会被置为nil可能会发生野指针错误(unsafe)。
unsafe_unretained与assign的区别在于,unsafe_unretained只能修饰对象,不能修饰标量类型,而assign两者均可修饰。

retain修饰在ARC环境下使用较少,在MRC下使用效果与strong一致。

copy,mutableCopy方法

有时候我们需要copy一个对象,或是mutableCopy一个对象,这时需要遵守NSCopying和NSMutableCopying协议,来实现copyWithZone:和mutableCopyWithZone:两个方法,而不是重写copy和mutableCopy两个方法。
Foundation框架中的很多数据类型已经帮我们实现了上述两个方法,因此我们可以使用

copy方法和mutableCopy方法来复制一个对象,两者的区别在于copy的返回值仍未不可变对象,mutableCopy的返回值为可变对象

6、深拷贝与浅拷贝方法

iOS 浅谈:深.浅拷贝与copy.strong http://www.jianshu.com/p/e6a7cdcc705d

深拷贝,浅拷贝.png

NSString不可变对象的深浅拷贝

copy浅拷贝只拷贝指针,不拷贝内容,没有创建新的对象
mutableCopy深拷贝会创建新对象,把内容也拷贝了一份,创建新的对象

NSMutableString可变对象的深浅拷贝

copy、mutableCopy都属于深拷贝

7、知识点总结:

@property等同于在.h文件中声明实例变量的get/set方法
@synthesize等同于在.m文件中实现实例变量的get/set方法。(但隐藏了)
使用@property和synthesize创建存取器要比手动声明两个存取方法(getter和setter)更简单。而且我们在使用属性时可以使用点语法赋值或取值,语法更简单,更符合面向对象编程。
什么时候使用strong,copy,assign,weak?

strong常使用在可变类型:NSMutableArray、NSMutableDictionary、NSMutableString
copy常使用在不可以变类型:NSArray、NSDictionary、NSString
assign常使用在基本类型:NSInteger,NSUInteger,CGFloat,NSTimeInterval
weak常使用在delegate等防止循环引用的场景

copy方法和mutableCopy方法来复制一个对象,两者的区别在于copy的返回值仍未不可变对象,mutableCopy的返回值为可变对象

NSString不可变对象的深浅拷贝

copy浅拷贝只拷贝指针,不拷贝内容,没有创建新的对象
mutableCopy深拷贝会创建新对象,把内容也拷贝了一份,创建新的对象

NSMutableString可变对象的深浅拷贝

copy、mutableCopy都属于深拷贝

NSString到底使用copy还是strong修饰呢?为什么呢?
应该使用copy修饰,使用copy是为了保证属性值的安全,不被其他变量的变化影响。
如下代码所示:

//假如使用strong来修饰NSString
@property (nonatomic, strong) NSString *name;

 NSMutableString*mutableStr= [[NSMutableString alloc] initWithString:@"黎"];
p.name = mutableStr;
NSLog(@"%@",p.name); //会输出“黎”
 [mutableStr appendString:@"明"];
NSLog(@"%@",p.name); //会输出“黎明”

8、参考链接

http://www.cocoachina.com/ios/20150123/11003.html
http://blog.csdn.net/u014205968/article/details/64443443
很好的一个博文:iOS 浅谈:深.浅拷贝与copy.strong http://www.jianshu.com/p/e6a7cdcc705d
@property进阶 http://blog.csdn.net/u014205968/article/details/64443487

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容