iOS开发那些事儿(五)Objective-C浅拷贝与深拷贝

- 浅拷贝:copy操作出来的对象指针直接指向模板的地址。即两个对象公用一块内存地址
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString * testStr = @"connor";
NSLog(@"testStr's Address = %p",testStr); NSString * testStrCopy = [testStr copy];
NSLog(@"testStrCopy Address = %p",testStrCopy); NSArray * array = @[@,@,@];
NSLog(@"array Address = %p",array); NSArray * copyArray = [array copy];
NSLog(@"copyArray Address = %p",copyArray);
}
return ;
}输出结果如下:
DataStruct[11210:2189074] testStr's Address = 0x100004280
DataStruct[11210:2189074] testStrCopy Address = 0x100004280
DataStruct[11210:2189074] array Address = 0x100300650
DataStruct[11210:2189074] copyArray Address = 0x100300650
- 深拷贝:copy操作出来的对象指针直接指向新开辟的内存。即持有原对象的拷贝副本
#import <Foundation/Foundation.h>
#import "Father.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString * test = @"a";
NSLog(@"%p",test); NSString * testCopy = [test mutableCopy];
NSLog(@"%p",testCopy);
}
return ;
}输出结果如下:
DataStruct[11355:2249168] 0x100004280
DataStruct[11355:2249168] 0x100203cf0
- Foundation总结大致如下:
- NS*类型的类调用Copy属于浅拷贝(例如NSString、NSArray等等)
- NS*类型的类调用MutableCopy属于深拷贝(例如NSString、NSArray等等)
- NSMutable*类型无论调用Copy或者MutableCopy都属于深拷贝
- 拷贝构造:以上我们谈的都是Foundation中用到的深浅拷贝,如果我们自己定义了一个类。怎么去实现它的深浅拷贝呢?这里就要用到拷贝构造方法。iOS中默认NSObject是不遵循NSCopying(不变副本)、NSMutableCopying(可变副本)的,所以如果想一个对象可Copy,就必须实现其中两个协议并且重写copyWithZone、mutableCopyWithZone方法。
- 下面我们定义一个Father类,来实现深拷贝:
/** .h */
#import <Foundation/Foundation.h>
@interface Father : NSObject<NSCopying>
@property(nonatomic,copy)NSString * name;
@end /** .m */
#import "Father.h" @implementation Father
-(id)copyWithZone:(NSZone *)zone{
Father * copyFather = [[self class]allocWithZone:zone];
return copyFather;
}
@end写好了Father类我们在外界就可以利用copy的方法去copy出一个Father对象
#import <Foundation/Foundation.h>
#import "Father.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Father * fatherA = [[Father alloc]init];
fatherA.name = @"connor";
NSLog(@"%p",fatherA); Father * fatherB = [fatherA copy];
NSLog(@"%@",fatherB.name);
NSLog(@"%p",fatherB);
}
return ;
}输出结果:
DataStruct[11383:2256524] 0x100206c80
DataStruct[11383:2256524] (null)
DataStruct[11383:2256524] 0x100300600
从输出结果来看,两个Father实例的内存地址是不一样的我们实现了深拷贝。但是为什么第二个Father没有名字?在拷贝过程过虽然我是从你FatherA拷贝过来的,但是你并没有指定FatherA中的属性到底是什么方式拷贝过来。所以这里面如果想要实现名字也拷贝过来,需要我们自己去定义到底是深拷贝过来还是浅拷贝过来。这也符合苹果做事的风格嘛。
#import "Father.h"
//添加name的浅拷贝
@implementation Father
-(id)copyWithZone:(NSZone *)zone{
Father * copyFather = [[self class]allocWithZone:zone];
copyFather.name = [self.name copy]; //如果想要深拷贝过来直接mutableCopy即可
return copyFather;
}
@end结果输出两个name的内存地址:
DataStruct[11409:2261412] 0x100004290
DataStruct[11409:2261412] 0x100004290
- 现在情况改变了,Father平时出门需要开车。Father需要持有Car类。那这个时候我们要如果复制这个爸爸!(因为复制出来的Father不能和之前那个Father开一辆车,所以我们应该用深拷贝。过程很简单直接贴代码了!)
/** .h */
#import <Foundation/Foundation.h> @interface Car : NSObject<NSCopying>
@property(nonatomic,copy)NSString * brand;
@end /** .m */
@implementation Car
-(id)copyWithZone:(NSZone *)zone{
Car * copyCar = [[self class]allocWithZone:zone];
copyCar.brand = [self.brand mutableCopy];
return copyCar;
}
@endCar
/** .h */
#import <Foundation/Foundation.h>
#import "Car.h"
@interface Father : NSObject<NSCopying>
@property(nonatomic,copy)NSString * name;
@property(nonatomic,strong)Car * car;
@end /** .m */
#import "Father.h" @implementation Father
-(id)copyWithZone:(NSZone *)zone{
Father * copyFather = [[self class]allocWithZone:zone];
copyFather.name = [self.name copy];
copyFather.car = [self.car copy];
return copyFather;
}
@endFather
#import <Foundation/Foundation.h>
#import "Father.h"
#import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Father * fatherA = [[Father alloc]init];
fatherA.car = [[Car alloc]init];
NSLog(@"%p",fatherA.car); Father * fatherB = [fatherA copy];
NSLog(@"%p",fatherB.car);
}
return ;
}main
输出结果:
DataStruct[11441:2268004] 0x100111f60
DataStruct[11441:2268004] 0x1001154a0
- copy这种操作如果发生在继承关系之后就有什么样的效果?或者说1.父类实现了NSCopying,子类该如果实现NSCopying。2.父类没有实现NSCopying,子类如何实现NSCopying?要接一个问题我们先看下表象是什么,然后通过表象来分析问题并解决问题。接下来我们先试着用第一种情况来看看表象:Son继承于Father
/** .h */
#import "Father.h"
@interface Son : Father
@property(nonatomic,copy)NSString * toy;
@end /** .m */
#import "Son.h"
@implementation Son
@endSon
#import <Foundation/Foundation.h>
#import "Son.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Son * sonA = [[Son alloc]init];
sonA.name = @"connor";
sonA.toy = @"iPhone"; Son * sonB = [sonA copy]; NSLog(@"sonA's Address = %p",sonA);
NSLog(@"sonB's Address = %p",sonB); NSLog(@"sonA's name Address = %p",sonA.name);
NSLog(@"sonB's name Address = %p",sonB.name); NSLog(@"sonA's toy Address = %p",sonA.toy);
NSLog(@"sonB's toy Address = %p",sonB.toy);
}
return ;
}main
输出结果:
DataStruct[11563:2279201] sonA's Address = 0x100114bc0
DataStruct[11563:2279201] sonB's Address = 0x100111f70
DataStruct[11563:2279201] sonA's name Address = 0x100004260
DataStruct[11563:2279201] sonB's name Address = 0x726f6e6e6f6365
DataStruct[11563:2279201] sonA's toy Address = 0x100004280
DataStruct[11563:2279201] sonB's toy Address = 0x0 这个结果也在我们意料之内的嘛,Son是继承与Father类的,Father实现了Copy协议,所以这个copy协议实现会继承到子类。我的理解其实:当Son调用Copy方法的时候就将消息转发给Son抽象类,发现Son中没有实现Copt则向Father发送消息SuperSendMsg就相当于事件响应链传递一样。一直向上传递直到能被处理。**这里需要注意一点Father中 Father * copyFather = [[self class]allocWithZone:zone];这里用的是self而不是Father因为Son会走到这里来,我们不能限制死到底是谁来调用***。接下来我们继续看为什么toy是个空置,这个原因和上面说的嵌套copy有点类似。因为Father不清楚你Son的属性到底是怎么copy过来的。那么怎么修改呢?我们只要在toy中稍作修改即可
@implementation Son
-(id)copyWithZone:(NSZone *)zone{
//这句话保证父类的copy也被call到
Son * son = [super copyWithZone:zone];
son.toy = [self.toy mutableCopy];
return son;
}
@endSon
输出结果:toy成功被深拷贝
DataStruct[11606:2301994] sonA's toy Address = 0x100004288
DataStruct[11606:2301994] sonB's toy Address = 0x656e6f68506965
- 接下来我们看一下第二种情况,父类没有实现NSCopying,子类如何实现NSCopying。直接让子类去实现就好了呗。父类如果需要深拷贝的我延迟到子类去执行呗!(这里有个问题,我的Son没有继承NSCopying,但是重写CopyWithZone居然没有报错??!!)
#import "Son.h"
@implementation Son
-(id)copyWithZone:(NSZone *)zone{
// Son * son = [super copyWithZone:zone];
Son * son = [[self class] allocWithZone:zone];
son.name = [self.name mutableCopy];
son.toy = [self.toy mutableCopy];
return son;
}
@endSon
输出结果:
DataStruct[11660:2317623] sonA's Address = 0x100206e00
DataStruct[11660:2317623] sonB's Address = 0x1002040e0
DataStruct[11660:2317623] sonA's name Address = 0x100004260
DataStruct[11660:2317623] sonB's name Address = 0x726f6e6e6f6365
DataStruct[11660:2317623] sonA's toy Address = 0x100004280
DataStruct[11660:2317623] sonB's toy Address = 0x656e6f68506965
- 下面我们定义一个Father类,来实现深拷贝:
iOS开发那些事儿(五)Objective-C浅拷贝与深拷贝的更多相关文章
- 从零开始学ios开发(十五):Navigation Controllers and Table Views(中)
这篇内容我们继续上一篇的例子接着做下去,为其再添加3个table view的例子,有了之前的基础,学习下面的例子会变得很简单,很多东西都是举一反三,稍稍有些不同的内容,好了,闲话少说,开始这次的学习. ...
- iOS开发那些事儿(二)热补丁
一.热补丁作用:修复导致崩溃的错误.替换/增加方法.替换原来的界面等等 二.实现手段:JSPatch (使用Objective-C Objective-C和JavaScript jspatch桥.你可 ...
- iOS开发那些事儿(六)Git分之策略
git 分支策略 将要介绍的这个模型不会比任何一套流程内容多,每个团队成员都必须遵守,这样便于管理软件开发过程. 既分散又集中 我们使用的,且与这个分支模型配合的非常好的库,他有一个“真正”的中央仓库 ...
- iOS开发那些事儿(四)the dark arts of the Objective-C runtime
一."Black Magic":Method Swizzling 利用 Runtime 特性把一个方法的实现与另一个方法的实现进行替换,也可以用runtime的四维理解——修改Di ...
- iOS开发那些事儿(一)轮播器
前言 市面上绝大部分的APP被打开之后映入眼帘的都是一个美轮美奂的轮播器,所以能做出一个符合需求.高效的轮播器成为了一个程序员的必备技能.所以今天的这篇博客就来谈谈轮播器这个看似简单的控件其中蕴含的道 ...
- iOS开发那些事儿(三)JsonKit解析原理
json_parse_it :开始解析,字符串指针从头到尾循环 jk_parse_next_token:获取下个字符的type和length 大部分分隔符长度都是固定1 jk_parse_string ...
- iOS开发-OC语言 (五)字典
字典 主要知识点: 1.NSDictionary 类 2.NSMutableDictionary 类 3.了解NSMutableDictionary 与 NSDictionary 的继承关系 4.补充 ...
- ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型
一:KVC和KVO的学习 #import "StatusItem.h" /* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字 ...
- ios开发网络学习五:MiMEType ,多线程下载文件思路,文件的压缩和解压缩
一:MiMEType:一般可以再百度上搜索到相应文件的MiMEType,或是利用c语言的api去获取文件的MiMEType : //对该文件发送一个异步请求,拿到文件的MIMEType - (void ...
随机推荐
- 【数位DP】【HDU2089】不要62
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- android——生成或者下载的图片在相册中找不到
今天在写程序的时候,遇到了一个问题,就是生成的图片一直都不能在相册中显示出来,而且,就连通过发送Intent过去,都找不到.通过在网上搜索,发现了一个很好的方法. Intent intent = ne ...
- Java中使用webservice,简化开发(xfire的webservice)
首先,使用到的jar先导入项目中, xbean-spring-2.8.jar, wsdl4j-1.6.1.jar, commons-httpclient-3.0.jar, commons-codec- ...
- 【转】关于ios10中ATS的问题
原文连接:https://onevcat.com/2016/06/ios-10-ats/ WWDC 15 提出的 ATS (App Transport Security) 是 Apple 在推进网络通 ...
- T - stl 的mapⅡ
Description Ignatius is so lucky that he met a Martian yesterday. But he didn't know the language th ...
- 从汇编看c++中指向成员变量的指针(一)
在c++中,指向类成员变量的指针存储的并不是该成员变量所在内存的地址,而仅仅是该成员变量在该类对象中相对于对象首地址的偏移量.因此,它必须绑定到某一个对象或者对象指针上面,这里的对象和对象指针,就相当 ...
- javascript中可处理的浮点数的最高精度(和小数的一些小特性)
1.之前在度娘那找了一下关于javascript中可处理的浮点数的最高精度的问题,但找了好久也找不到,于是自己 小小的研究了一下,之前以为是17,后来测到18,再后来又测到19,经过一系列的改写,得到 ...
- mac10.9 设置vim tab
vimrc系统文件目录 /usr/share/vim 在vimrc中添加以下代码后,重启vim即可实现按TAB产生4个空格:set ts=4 (注:ts是tabstop的缩写,设TAB宽4个空格)s ...
- printf--动态指定输出格式长度
char a1[] = {'A', 'B', 'C'}; char a2[] = "world"; printf(, a1, , a2); printf("[%.*s][ ...
- .NET4.0下使用Net2.0类库或程序集
最近在项目上一直使用.net4.0 framework,使用ffmepeg下的一个dll时,提示只能在2.0下运行,解决方法如下: app.config中添加一个配置节:startup <?xm ...