• iOS 集合的深复制与浅复制


    概念

    对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中。

    一图以蔽之

    image_note50592_1.png

    再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝。

    ________________________________________

    集合的浅复制(shallow copy)

    集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。

    现在让我们看一些浅复制的例子:

    1
    2
    3
    4
    5
    6
    1.NSArray*shallowCopyArray=[someArray copyWithZone:nil];
    2.
    3.NSSet*shallowCopySet=[NSSet mutableCopyWithZone:nil];
    4.
    5.NSDictionary*shallowCopyDict=[[NSDictionary alloc]initWithDictionary:someDictionary copyItems:NO];
    ________________________________________

    集合的深复制(deep copy)

    集合的深复制有两种方法。可以用initWithArray:copyItems:将第二个参数设置为YES即可深复制,如

    1
    1.NSDictionary shallowCopyDict=[[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

    如果你用这种方法深复制,集合里的每个对象都会收到copyWithZone:消息。如果集合里的对象遵循NSCopying协议,那么对象就会被深复制到新的集合。如果对象没有遵循NSCopying协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone:这种拷贝方式只能够提供一层内存拷贝(one-level-deepcopy),而非真正的深复制。

    第二个方法是将集合进行归档(archive),然后解档(unarchive),如:

    1
    1.NSArray*trueDeepCopyArray=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

    ________________________________________

    集合的单层深复制(one-level-deep copy)

    看到这里,有同学会问:如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝,这种情况是属于深复制,还是浅复制?对此,苹果官网文档有这样一句话描述

    This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy...

    If you need a true deep copy, such as when you have an array of arrays...

    从文中可以看出,苹果认为这种复制不是真正的深复制,而是将其称为单层深复制(one-level-deepcopy)。因此,网上有人对浅复制、深复制、单层深复制做了概念区分。

    • 浅复制(shallowcopy):在浅复制操作时,对于被复制对象的每一层都是指针复制。

    • 深复制(one-level-deepcopy):在深复制操作时,对于被复制对象,至少有一层是深复制。

    • 完全复制(real-deepcopy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

    当然,这些都是概念性的东西,没有必要纠结于此。只要知道进行拷贝操作时,被拷贝的是指针还是内容即可。

    ________________________________________

    系统对象的copy与mutableCopy方法

    不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:

    • copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;

    • mutableCopy返回mutable对象;

    下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述

    1、非集合类对象的copy与mutableCopy

    系统非集合类对象指的是NSString,NSNumber...之类的对象。下面先看个非集合类immutable对象拷贝的例子

    1
    2
    3
    1.NSString*string=@"origin";
    2.NSString*stringCopy=[stringcopy];
    3.NSMutableString*stringMCopy=[stringmutableCopy];

    通过查看内存,可以看到stringCopy和string的地址是一样,进行了指针拷贝;而stringMCopy的地址和string不一样,进行了内容拷贝;

    再看mutable对象拷贝例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    1.NSMutableString*string=[NSMutableStringstringWithString:@"origin"];
    2.//copy
    3.NSString*stringCopy=[stringcopy];
    4.NSMutableString*mStringCopy=[stringcopy];
    5.NSMutableString*stringMCopy=[stringmutableCopy];
    6.//changevalue
    7.[mStringCopyappendString:@"mm"];//crash
    8.[stringappendString:@"origion!"];
    9.[stringMCopyappendString:@"!!"];

    运行以上代码,会在第7行crash,原因就是copy返回的对象是immutable对象。注释第7行后再运行,查看内存,发现string、stringCopy、mStringCopy、stringMCopy四个对象的内存地址都不一样,说明此时都是做内容拷贝。

    综上两个例子,我们可以得出结论:

    在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

    • [immutableObjectcopy]//浅复制

    • [immutableObjectmutableCopy]//深复制

    • [mutableObjectcopy]//深复制

    • [mutableObjectmutableCopy]//深复制

    2、集合类对象的copy与mutableCopy

    集合类对象是指NSArray、NSDictionary、NSSet...之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:

    1
    2
    3
    1.NSArray*array=@[@[@"a",@"b"],@[@"c",@"d"];
    2.NSArray*copyArray=[arraycopy];
    3.NSMutableArray*mCopyArray=[arraymutableCopy];

    查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。这和上面的非集合immutable对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看mutable对象拷贝的例子:

    1
    2
    3
    1.NSMutableArray*array=[NSMutableArrayarrayWithObjects:[NSMutableStringstringWithString:@"a"],@"b",@"c",nil];
    2.NSArray*copyArray=[arraycopy];
    3.NSMutableArray*mCopyArray=[arraymutableCopy];

    查看内存,如我们所料,copyArray、mCopyArray和array的内存地址都不一样,说明copyArray、mCopyArray都对array进行了内容拷贝。同样,我们可以得出结论:

    在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

    • [immutableObjectcopy]//浅复制

    • [immutableObjectmutableCopy]//单层深复制

    • [mutableObjectcopy]//单层深复制

    • [mutableObjectmutableCopy]//单层深复制

    这个代码结论和非集合类的非常相似。

    这时候,是不是有人要问了,如果要对集合对象复制元素怎么办?有这疑问的同学不妨回头看看集合的深复制

    好了,深复制与浅复制就讲到这里。

    ________________________________________

    最后说个题外的东西,在搜集资料的过程中,发现一个有可能犯错的点

    1
    2
    1.NSString*str=@"string";
    2.str=@"newString";

    上面这段代码,在执行第二行代码后,内存地址发生了变化。乍一看,有点意外。按照C语言的经验,初始化一个字符串之后,字符串的首地址就被确定下来,不管之后如何修改字符串内容,这个地址都不会改变。但此处第二行并不是对str指向的内存地址重新赋值,因为赋值操作符左边的str是一个指针,也就是说此处修改的是内存地址。

    所以第二行应该这样理解:将@"newStirng"当做一个新的对象,将这段对象的内存地址赋值给str。

    我有如下的两个方法查看内存地址

    • pstr会打印对象本身的内存地址和对象内容

    1
    2
    1.(lldb)pstr
    2.(NSString*)$0=0x000000010c913680@"a"
    • po&str则打印的是引用对象的指针所在的地址

    1
    2
    1.(lldb)po&str
    2.0x00007fff532fb6c0
  • 相关阅读:
    【iPhone开发】说说Xcode4中xib绑定的原理转
    转 IOS下的图片
    免证书调试Iphone程序(转)
    牛B的Python模块(转)
    6 个手机开发方面很有用的应用
    留个脚印!
    Android初学者入门PDF版
    IOS下的MVC
    android2.2源码编译-Ubuntu10.04 (X86) + android2.2 + JDK1.6
    10 款 Windows 下最佳的免费 PHP 编辑器
  • 原文地址:https://www.cnblogs.com/guiyangxueyuan/p/5567280.html
Copyright © 2020-2023  润新知