[学习笔记—Objective-C]《Objective-C 程序设计 第6版》第十八章 复制对象

origin = pt; 
  • 将对象pt的地址复制到origin中。两个变量都指向内存中同一个地址
  • Foundation对象:将一个变量赋值给另一个对象仅仅创建了另一个对这个对象的引用(地址)。

Part 1.copy 和 mutablecopy 方法:创建对象的副本

  • 复制对象(的引用)
 dataArray2 = dataArray;//创建了内存中同一数组对象的另一个引用:总共一个数组
  • 创建对象的副本
 dataArray2 = [dataArray mutalbeCopy];//创建信的dataArray副本并复制了所有元素:总共两个数组
  • 注意:可以创建可变对象的不可变副本。也可以创建不可变对象的可变副本。

Part 2. 浅复制与深复制

Part 2.1 浅复制

        NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:

        [NSMutableString stringWithString:@"one"],
        [NSMutableString stringWithString:@"two"],
        [NSMutableString stringWithString:@"three"],nil];

         NSMutableArray *dataArray2;
         NSMutableString *mStr;    

         dataArray2 = [dataArray mutableCopy];
        //两个数组(dataArray,dataArray2)的元素都指向内存中同一个字符串,等同于一个对象赋值给另一个对象,属于浅复制

        mStr = dataArray[0];
        //检索dataArray第一个元素 dataArray[0]返回的对象和dataArray中的一个元素指向内存中的同一个对象。

        [mstr appendString:@"ONE"];
        //改变mstr的同时改变了dataArray的第一个元素。由于浅复制,dataArray2也发生了改变,因为浅复制只是复制了对象的引用,内容被更改即被更改。

Part 2.2 深复制

  • 创建数组中每个对象内容的副本,而不仅仅是这些对象引用的副本。
  • 更改dataArray2中的第一个元素,而不改变dataArray的第一个元素:
mStr = [NSMutableString stringWithString: dataArray2[0]];
//创建一个新字符串,并存储到dataArray2的第一个位置。

[mStr appendString @"ONE"];
[dataArray2 replaceObjectAtIndex: 0 withObject: mStr];
//更改mStr并添加得到数组中

Part 3. 实现 < NSCopying > 协议

  • 若要对自己的类进行复制,必须根据协议实现其中的方法。
  • 协议:实现copyWithZone:方法来响应copy消息,返回不可变副本
  • 协议:实现mutableCopyWithZone:方法,返回可变副本

在Fraction类创建新的分数

@interface Fraction: NSObject <NSCopying>

-(id) copyWithZone: (NSZone *)zone
{
  Fraction *newFact = [[Fraction allocWithZone: zone] init];

  [newFact setTo: numerator over: denominator];

  return newFact;
}
Fraction *f1 = [[Fraction alloc]init];
Fraction *f2;

[f1 setTo: 2 over 5];

f2 = [f1 copy]; //产生f1的副本,产生新的Fraction,将f1复制其中

[f2 setTo: 1 over: 3];

[f1 print]; // 2/5 复制的操作对f1没有影响
[f2 print]; // 1/3

注意

  • 如果将copy语句改成 f2 = f1,则改变f2的同时会改变f1。
  • 如果产生此类的子类,copyWithZone:方法将被继承:
Fraction *newFact = [[Fraction allocWithZone: zone] init];
应该改为:
id newFract = [[self class allocWithZone: zone] init];

Part 4. 用设值方法和取值方法复制对象

赋值:

newCard.name = newName; //设置对象(AddressCard)的名称(姓名)

设值方法:

-(void) setName: (NSString *) theName
{
   name = theName; //简单将参数赋值给了相应的实例变量
}

如果修改了newName的字符

为了避免改变其他对象的值,比较安全的做法是在设值方法中将对象复制一份。

使用copy版本的setName方法:

-(void) setName: (NSString *) theName
{
   name = [theName copy];
}
  • 属性的copy特性:
@property (nonatomic,copy) NSString *name;

synthesize 一同使用等效于:

-(void) setName: (NSString *) theName
{
   if (theName != name)
   name = [theName copy];
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 12:54:49

[学习笔记—Objective-C]《Objective-C 程序设计 第6版》第十八章 复制对象的相关文章

JavaScript高级程序设计学习笔记第六章--面向对象程序设计

1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”,有点类似于散列表 2.ECMAScript 中有两种属性:数据属性和访问器属性. 数据属性: [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性. [[Enumerable]]:表示能否通过 for-in 循环返回属性. [[Writable]]:表示能否修改属性的值. [[Valu

JavaScript学习笔记八:面向对象的程序设计

1. ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值,对象和函数.每个对象都是基于一个引用类型创建的. 2. 属性有两种:数据属性和访问器属性,其中访问器属性不能直接定义,必须使用Object.defineProperty()来定义. 3. 任何函数,只要通过new操作符来调用,那它就可以作为构造函数. 4. 原型:无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象. 这个连接存在于实例与构造函数的原型

[学习笔记—Objective-C]《Objective-C-基础教程 第2版》第九章 内存管理

内存管理: 确保在需要的时候分配内存,在程序运行结束时释放占用的内存 如果只分配内存而不释放内存,则会发生内存泄漏(leak memory),程序的内存占用量不断增加,最终会被耗尽并导致程序崩溃. 不要使用任何刚释放的内存,否则可能误用陈旧的数据,如果内存已经加载了其他数据,将会破坏这些新数据. 9.1 对象生命周期 对象的生命周期: 诞生:通过alloc或new方法实现 生存:接受消息并执行操作 交友:通过复合以及向方法传递函数 死去:被释放掉 9.11 引用计数 关于引用计数的操作: 增加对

python学习笔记(22)--漫画生成html最终版

说明(2017.3.14): 1. 在主文件夹生成一个main.html作为目录 2. 在每个子文件夹生成一个index.html作为看图网页 3. 通过python批量生成html网页,js配合进行遍历输出a链接 4. 生成一个booklist.js文件,存放目录数组,不然字符串不能与数组连接.这个数组是通过字符串伪装出来的,遍历数组,每个元素加上引号和逗号,去掉最后一个逗号,最后两边加上括号,var赋值就可以了. 5. 效果还不错,可以作为本地小黄网了! 1 #!/usr/bin/pytho

【学习笔记】 深入理解Linux内核第三版 —— 第三章 进程

何为进程 进程(分享资源 单位)/线程(执行的单位)/轻量级进程(共享部分资源) Linux中线程通过pthead 标准库实现,其中存在实现轻量级进程的方法,方法也可针对线程组执行. 静态:进程如何描述 进程描述符:task_struct数据类型 进程与进程描述符一一对应.进程描述符指针指向进程描述符地址,内核由此来引用进程. PID(process id)可用来标识进程(linux维护pidbitmap-array位图来管理分配PID),同一个线程组的线程使用相同的PID(与第一个线程的值相同

MiZ702学习笔记9——XADC采集片上数据PS版

这次借助zynq的内嵌的XADC来采集zynq内部的一些参数: •VCCINT:内部PL核心电压 •VCCAUX:辅助PL电压 •VREFP:XADC正参考电压 •VREFN:XADC负参考电压 •VCCBram:PL BRAM电压 •VCCPInt:PS内部核心电压 •VCCPAux:PS辅助电压 •VCCDdr:DDR RAM的工作电压 这次这个程序的开发流程和之前讲的别无二致,希望大家能够完全的熟悉这个流程~~ 还是和往常一样,新建一个Block Design,并且向其中添加zynq核以及

[学习笔记—Objective-C]《Objective-C-基础教程 第2版》第十二章 类别

12.1 创建类别 类别(category)是一种为现有的类添加新方法的方式 例:获取字符串的长度并存入NSDictionary字典中 不使用类别的方式: NSNumber *number; number = [NSNumber numberWithUnsignedInt: [string length]]; // ... do something with number 使用类别的方式: 类别的声明: @interface NSString (NumberConvenience) - (NSN

[学习笔记—Objective-C]《Objective-C-基础教程 第2版》第十一章 属性

11.1 使用属性值 @property float rainHandling; //表明此类具有float类型的属性,其名称为rainHandling 注意:属性的名称不必与实例变量名称同样. @synthesize rainHandling; //表示创建了该属性的訪问代码 注意:使用属性后,能够不声明实例变量.编译器会声明. 有两个地方能够加入实例变量声明: 头文件:让子类直接通过属性訪问变量 实现文件:变量仅仅属于当前类 点表达式: 点表达式出如今(=)的左边:setter方法 点表达式

[学习笔记—Objective-C]《Objective-C-基础教程 第2版》第十三章 协议

13.1 协议 正式协议:包含了方法和属性的有名称列表. 注意: 采用协议后,类就要实现该协议的所有方法. 通常情况下,一个协议只有少数几个需要实现的方法. 在协议中,不会引用新的实例变量. 13.11 声明协议 NSCopying 协议 @protocol NSCopying - (id) copyWithZone: (NSZone *) zone; @end //如果采用了NSCopying协议,你的对象将会知道如何创建自身的副本 继承父协议 @protocol MySuperDuberPr