浅拷贝与深拷贝

1、保护封装性:

// Person对象中的name属性使用copy修饰
@interface Person : NSObject
    @property (copy, nonatomic) NSString *name;
@end
// Man对象中的name属性使用strong修饰
@interface Man : NSObject
    @property (strong, nonatomic) NSString *name;
@end


int main(int argc, const char * argv[]) {
    
    Person *p = [[Person allow] init];
    Man *m = [[Man allow] init];
    NSMutableString *str = [NSMutableString stringWithFormat:@"%@", @"hello"];
    p.name = str;
    m.name = str;
    [str appendFormat:@"%@", @" world"];

    NSLog(@"%p === %@ === %@", p.name, p.name, str);
    NSLog(@"%p === %@ === %@", m.name, m.name, str);
}

打印结果:

 0x6f6c6c656855 === hello === hello world

 0x100442af0 === hello world === hello world

总结:上面的实例中,Man实例对象中的name属性,没有通过setter方法,就完成了name属性的修改,破坏了封装性。

2、备份内容:

// 拷贝一个内存空间的内容,只需要一行代码就搞定,不需要再进行繁琐操作了(申请内存空间,将原内存空间的内容赋值到新内存空间中)
int main(int argc, const char * argv[]) {
   NSString *str1 = nil;
    NSMutableString *str = [NSMutableString stringWithFormat:@"%@", @"hello"];
    // 复制str中的内容到str1中。
    str1 = [str copy];
}

3、copy和mutableCopy使用区别:

总结:

  • 使用copy创建的对象,永远是不可变对象
  • 使用mutableCopy创建的对象,永远是可变对象,并且一定是深拷贝。
  • 不可变对象使用copy,永远是浅拷贝,可变对象使用copy,永远是深拷贝。

3、容器对象的单层深拷贝

int main(int argc, const char * argv[]) {
    NSMutableArray *arrM = [NSMutableArray arrayWithObject:@"aaa"];        
    // 对arrM进行深拷贝
    NSArray *arr = [arrM copy];
    // 打印两个数组的首地址,是否是深拷贝
    NSLog(@"arrM = %p", arrM);
    NSLog(@"arr = %p", arr);
    // 打印两个数组第0个元素的地址,是否也是深拷贝
    NSLog(@"arrM[0] = %p", arrM[0]);
    NSLog(@"arr[0] = %p", arr[0]);
}

打印结果:
arrM = 0x10200ad00
arr = 0x102004d30
arrM[0] = 0x1000021e0
arr[0] = 0x1000021e0

总结:

  对于容器对象的深拷贝,是单层拷贝,虽然两个数组不共享同一块内存空间,但是数组中的元素,却是共享一块内存空间。

4、实现数组的元素的深拷贝:

/*
  * 1、创建数组类的分类
  * 2、重新copy和mutableCopy方法
  * 3、在方法中实现数组元素的深拷贝
  * 4、不要实现copyWithZone:和mutableCopyWithZone:方法,不会调用的。
*/

@interface NSMutableArray (Copy)

@end

@implementation NSMutableArray (Copy)

// 重写copy方法
- (id)copy
{
    NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:self.count];
    // 对数组中的元素进行深拷贝,并保存到新数组中。
    for (NSObject *obj in self) {
        // 判断数组中的元素是否实现NSCopying协议
        if([obj conformsToProtocol:@protocol(NSCopying)]){
            NSObject *temp = [obj copy];
            [arrM addObject:temp];
        }
        [arrM addObject:obj];
    }
    // 将数组转成不可变对象
    return [NSArray arrayWithArray:arrM];
}

// 重写mutableCopy方法
- (id)mutableCopy
{
    NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:self.count];
    // 对数组中的元素进行深拷贝,并保存到新数组中。
    for (NSObject *obj in self) {
        // 判断数组中的元素是否实现NSCopying协议
        if([obj conformsToProtocol:@protocol(NSCopying)]){
            NSObject *temp = [obj copy];
            [arrM addObject:temp];
        }
        [arrM addObject:obj];
    }
    return arrM;
} 

@end

----------main.m----------------------------------
#import "NSMutableArray+Copy.h"
int main(int argc, const char * argv[]) {

    NSMutableString *s5 = [[NSMutableString alloc] initWithString:@"aaa"];
    NSMutableArray *arrM = [NSMutableArray arrayWithObject:s5];
    // 调用数组分类中的copy方法。
    NSArray *arr = [arrM copy];

    NSLog(@"arrM = %p", arrM);
    NSLog(@"arr = %p", arr);
    NSLog(@"arrM[0] = %p", arrM[0]);
    NSLog(@" arr[0] = %p", arr[0]);
    return 0;
}


打印结果:
       arrM = 0x10300eea0
         arr = 0x10064e5d0
  arrM[0] = 0x10300ecd0
    arr[0] = 0x61616135 

5、自定义对象的深拷贝:

  • 自定义类的实例对象调用copy方法时,会调用copyWithZone:方法,但是系统的字符串、数组、字典对象调用copy方法时,不会调用copyWithZone:方法。
  • 要实现自定义对象的拷贝功能,需要遵循NSCopying协议,并实现copyWithZone:和mutableCopyWithZone:方法。
  • 自定义类中的实例对象的属性存在另外的自定义对象(Person类中有Man类属性),如果需要保证Person的封装性,可以使用copy修饰属性对象,如果想要共享内存空间,可以使用strong修饰属性对象。
---------------Man.h----------------
@interface Man : NSObject<NSCopying>
    @property (strong, nonatomic) NSString *name;
@end

@implementation Man
    // 自定义对象不分可变和不可变,一般需要深拷贝时,在copyWithZone:中写深拷贝代码,不需要就写浅拷贝代码。
    - (id)copyWithZone:(NSZone *)zone
    {
        Man *m = [[Man alloc] init];
        m.name = self.name;
        return m;
    }
    
- (id)mutableCopyWithZone:(NSZone *)zone
    {
        Man *m = [[Man alloc] init];
        m.name = self.name;
        return m;
    }
@end

--------------------Person.h---------------
#import "Man.h"
@interface Person : NSObject<NSCopying>
    @property (copy, nonatomic) NSString *name;
    @property (copy, nonatomic) Man *m1;
    @property (strong, nonatomic) Man *m2;
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone
{
     return self;
}
    
- (id)mutableCopyWithZone:(NSZone *)zone
{
     Person *p = [[Person alloc] init];
     // Person的name属性是浅拷贝
     p.name = [self.name copy];  
     p.m1 = self.m1;
     p.m2 = self.m2;
     return p;
}

@end

--------------main.h-------------------
#import "Person.h"
int main(int argc, const char * argv[]) {
    Person *p1 = [[Person alloc] init];
    Person *p2 = [p1 copy];       // p1指向的内存空间的引用计数器为2,原因copyWithZone:方法中写的是浅拷贝代码。
    Person *p3 = [p1 mutableCopy];    // p3指向了一块新的内存空间,原因mutableCopyWithZone:方法中写的是深拷贝代码
    NSLog(@"p1");
}

-----------------main.h-------------------
int main(int argc, const char * argv[]) {
    Person *p1 = [[Person alloc] init];
    Man *m = [[Man alloc] init];
    m.name = @"man";
    p1.m1 = m;
    p1.m2 = m;
    m.name = @"Man";
    Person *p2 = [p1 mutableCopy];   

    NSLog(@"Man === %p", m);
    NSLog(@"P1.m1 copy=== %p", p1.m1);
    NSLog(@"P1.m2 strong=== %p", p1.m2);
    NSLog(@"P3.m1 copy=== %p", p3.m1);
    NSLog(@"P3.m1 strong === %p", p3.m2);
}

--------------打印------------------------
Man === 0x100432150
P1.m1 copy=== 0x100432eb0
P1.m2 strong=== 0x100432150
P3.m1 copy=== 0x10041de00
P3.m1 strong === 0x100432150

6、stringWithFormat:方法:

    NSString *str1 = [NSString stringWithFormat:@"hello"];
    NSString *str2 = [NSString stringWithFormat:@"hello"];
    NSMutableString *str3 = [NSMutableString stringWithFormat:@"hello"];
    NSMutableString *str4 = [NSMutableString stringWithFormat:@"hello"];
    NSLog(@"str1 == %p", str1);
    NSLog(@"str2 == %p", str2);
    NSLog(@"str3 == %p", str3);
    NSLog(@"str4 == %p", str4);

-------------打印结果-------------------------
str1 == 0x6f6c6c656855
str2 == 0x6f6c6c656855
str3 == 0x1007112a0
str4 == 0x100711180

总结:

  • 对于NSString对象,不管调用多少次stringWithFormat:方法,只要内容一样,它们的地址都一样。
  • 对于NSMutableString对象,尽管内容一样,每次调用stringWithFormat:方法都会创建新的内存空间来保持。

猜你喜欢

转载自www.cnblogs.com/Zp3sss/p/8874139.html