copy及其用法

来源:互联网 发布:mac 顿号 编辑:程序博客网 时间:2024/06/04 18:05

copy的概念特点

  • copy产生一个新副本的过程,利用一个原对象产生一个新对象
    • copy:创建一个不可变的副本(NSString;NSArray;NSDictionary;)
    • mutableCopy :创建一个可变的副本 (NSMutableDictionary; NSMutableArray;NSMutableString)
  • 修改新文件,不会影响原文件
  • 修改原文件,不会影响新文件
    //会生成新对象    /*     拷贝出来的对象中的内容和以前内容一致     一般情况下拷贝生成一个新的对象     ·因为拷贝要求修改原来的对象不能影响到拷贝出来的对象,修改拷贝出来的对象不能影响到原来的对象,所以需要生成一个新对象     ·由于以前的对象是一个不可变的对象,而通过mutableCopy拷贝出来的对象必须是一个可变的对象,所以需要生成一个新对象     */    NSString * str = @"demo";    NSMutableString *copyStr = [str mutableCopy];    NSLog(@"str = %@ copyStr = %@",str,copyStr);    NSLog(@"str = %p copyStr = %p",str,copyStr);    NSLog(@"");    //会生成新对象    NSMutableString *str1 = [NSMutableString stringWithFormat:@"demo"];    NSMutableString *copyStr1 =  [str1 mutableCopy];    [str1 appendString:@"cool"];    NSLog(@"str = %@ copyStr = %@",str1,copyStr1);    NSLog(@"str = %p copyStr = %p",str1,copyStr1);    NSLog(@"");    //会生成新对象    NSMutableString *str2 = [NSMutableString stringWithFormat:@"demo"];    NSString *copyStr2 =  [str2 copy];    [str2 appendString:@"cool"];    NSLog(@"str = %@ copyStr = %@",str2,copyStr2);    NSLog(@"str = %p copyStr = %p",str2,copyStr2);    NSLog(@"");    //不会生成新对象    /*      如果是通过不可变对象调用了copy方法,那么不会生成新对象        因为原来的对象时不可修改的,拷贝出来的对象也是不可修改的,既然俩个都不能改,所有永远都不会影响到另一个对象,OC为了堆内存优化,所以就不会生成一个新对象     */    NSString *str3 = @"demo";    NSString *copyStr3 =  [str3 copy];    NSLog(@"str = %@ copyStr = %@",str3,copyStr3);    NSLog(@"str = %p copyStr = %p",str3,copyStr3);//结果:test2[7366:74428] str = demo copyStr = demotest2[7366:74428] str = 0x100005288 copyStr = 0x100400090test2[7366:74428] test2[7366:74428] str = democool copyStr = demotest2[7366:74428] str = 0x1002000f0 copyStr = 0x10030a2e0test2[7366:74428] test2[7366:74428] str = democool copyStr = demotest2[7366:74428] str = 0x100400780 copyStr = 0x6f6d656445test2[7366:74428] test2[7366:74428] str = demo copyStr = demotest2[7366:74428] str = 0x100005288 copyStr = 0x100005288test2[7366:74428] name = name cooltest2[7366:74428] name = play
  • 浅拷贝:如果没有生成新对象我们称为浅拷贝,本质是指针拷贝
  • 深拷贝:如果生成了新的对象,我们称为深拷贝,本质就是创建了一个新的对象
  • 一般修饰字符串用copy

copy应用

防止外界修改内部数据

@interface Student : NSObject@property (nonatomic,strong) NSString *name;@property (nonatomic,copy) NSString *hobby;@end@implementation Student-(void)dealloc{    NSLog(@"%s",__func__);}@end---------------int main(int argc, const char * argv[]) { NSMutableString *name = [NSMutableString stringWithFormat:@"name"];    NSMutableString *hobby = [NSMutableString stringWithFormat:@"play"];    Student *stu = [[Student alloc]init];    stu.name = name;    stu.hobby = hobby;    //修改外面对象,影响到了对象的属性    [name appendString:@" cool"];    [hobby appendString:@" football"];    NSLog(@"name = %@",stu.name);    NSLog(@"name = %@",stu.hobby);}结果:test2[7405:77750] name = name cooltest2[7405:77750] name = play

copy修饰blcok

block默认存储在栈中,栈中的block访问了外界的对象,不会对对象进行retain
block如果存储在堆中,如果在block中访问了外界的对象,会对外界的对象进行一次retain

typedef void (^myBlock)();typedef void (^myTestBlock)();@interface Student : NSObject@property (nonatomic,strong) myBlock myBlock;//注意;如果是Block使用copy并不是拷贝,而是转移(由栈到堆),从栈到堆,这样可以保持block,避免以后调用block的时候,外界的对象已经释放@property (nonatomic,strong) myTestBlock testBlock;@end@implementation Student-(void)dealloc{    NSLog(@"%s",__func__);}@end--------------------    Student *stu = [[Student alloc]init];    Person *per = [[Person alloc]init];    stu.myBlock = ^{        NSLog(@"%@",per);    };    stu.testBlock = ^{        NSLog(@"%@",per);    };    stu.myBlock();    stu.testBlock();结果:test2[7941:112357] <Person: 0x100206f80>test2[7941:112357] <Person: 0x100206f80>test2[7941:112357] -[Student dealloc]test2[7941:112357] -[Person dealloc]

关于 copy block之后的循环引用

typedef void (^myTestBlock)();@interface Student : NSObject@property (nonatomic,strong) NSString *name;@property (nonatomic,copy) myTestBlock testBlock;@end@implementation Student-(void)dealloc{    NSLog(@"%s",__func__);    [super dealloc];}@end-----------int main(int argc, const char * argv[]) {    //如果对象中的block又用到了自己,为了避免内存泄漏,应该将该对象修饰为__block    __block Student *stu = [[Student alloc]init];    stu.name = @"ABC";    NSLog(@"1-%lu",stu.retainCount);    stu.testBlock = ^{        NSLog(@"%@",stu.name);    };    NSLog(@"2-----%lu",stu.retainCount);    stu.testBlock();    NSLog(@"3-------%lu",stu.retainCount);    [stu release];}结果:test2[8246:125564] 1-1test2[8246:125564] 2-----2test2[8246:125564] ABCtest2[8246:125564] 3-------2test2[8246:125564] -[Student dealloc]

自定义类的实现copy

  • 无父类实现
    • 让类遵守NSCopying、NSMutableCopying协议
    • 实现copyWithZone方法,在该方法中返回一个对象的副本即可
    • 在copyWithZone或mutableCopyWithZone方法中,创建一个新的对象,并设置该对象的数据与现有对象一致,并返回对象
#import <Foundation/Foundation.h>@interface Student : NSObject<NSCopying>@property (nonatomic,copy) NSString *name;@property (nonatomic,assign) int age;@end@implementation Student-(id)copyWithZone:(NSZone *)zone{    //1、创建一个新的对象(zone:表示空间,分配是需要内存空间的,如果指定了zone就可以指定创建对象对应的内存空间,为了避免堆中出现内存碎片而使用的)    Student *stu = [[[self class] allocWithZone:zone]init];    //2、设置当前对象的内容给新的对象    stu.name = _name;    stu.age = _age;    //3、返回新的对象    return stu;}@end---------int main(int argc, const char * argv[]) {   Student *stu = [[Student alloc]init];    stu.name = @"abc";    stu.age = 10;    Student *stu1 = [stu copy];    Student *stu2 = [stu mutableCopy];    NSLog(@"stu = %@ ,stu1 = %@, stu2 = %@",stu.name,stu1.name,stu2.name);    NSLog(@"stu = %d ,stu1 = %d, stu2 = %d",stu.age,stu1.age,stu2.age);    NSLog(@"stu = %@ ,stu1 = %@, stu2 = %@",stu,stu1,stu2);    return 0;}结果:test2[8511:149031] stu = abc ,stu1 = abc, stu2 = abctest2[8511:149031] stu = 10 ,stu1 = 10, stu2 = 10test2[8511:149031] stu = <Student: 0x100302890> ,stu1 = <Student: 0x1003060b0>, stu2 = <Student: 0x1003061c0>
  • 有父类实现
    • 不调用父类方法,无法拷贝父类中继承的属性
    • 不重写父类copyWithZone方法,无法拷贝本来中特有的属性
@interface Student : NSObject<NSCopying,NSMutableCopying>@property (nonatomic,copy) NSString *name;@property (nonatomic,assign) int age;@end@implementation Student-(id)copyWithZone:(NSZone *)zone{    //1、创建一个新的对象(zone:表示空间,分配是需要内存空间的,如果指定了zone就可以指定创建对象对应的内存空间,为了避免堆中出现内存碎片而使用的)    Student *stu = [[[self class] allocWithZone:zone]init];    //2、设置当前对象的内容给新的对象    stu.name = _name;    stu.age = _age;    //3、返回新的对象    return stu;}-(id)mutableCopyWithZone:(NSZone *)zone{    //1、创建一个新的对象(zone:表示空间,分配是需要内存空间的,如果指定了zone就可以指定创建对象对应的内存空间,为了避免堆中出现内存碎片而使用的)    Student *stu = [[[self class] allocWithZone:zone]init];     //2、设置当前对象的内容给新的对象    stu.name = _name;    stu.age = _age;     //3、返回新的对象    return stu;}@end--------------@interface Boy : Student@property (nonatomic,assign) double height;@end@implementation Boy-(id)copyWithZone:(NSZone *)zone{    //1、创建一个新的对象    id obj = [super copyWithZone:zone];    //2、设置当前对象的内容给新的对象    [obj setHeight:_height];    //3、返回新的对象    return obj;}-(id)mutableCopyWithZone:(NSZone *)zone{    //1、创建一个新的对象    id obj = [super copyWithZone:zone];    //2、设置当前对象的内容给新的对象    [obj setHeight:_height];    //3、返回新的对象    return obj;}-(NSString *)description{    return [NSString stringWithFormat:@"name = %@,age = %d,height =%f",[self name],[self age],_height];}@end-------------int main(int argc, const char * argv[]) {    Boy *boy = [[Boy alloc]init];    boy.name = @"abc";    boy.age = 10;    boy.height = 1.75;    Boy *boy1 = [boy copy];    Boy *boy2 = [boy mutableCopy];    NSLog(@"\nboy = %@ \nboy1 = %@ \nboy2 = %@\n",boy,boy1,boy2);    return 0;}

如果想让子类在copy的时候保留子类的属性,那么必须重写copyWithZone方法,在该方法中先调用父类创建副本设置值,然后再设置子类特有的值