IOS ObjectiveC中的赋值与对象拷贝

IOS ObjectiveC中的赋值与对象拷贝

在开发过程中我们经常会遇到对象拷贝的问题,下面我们分别讨论赋值操作、对象拷贝、以及浅拷贝(Shallow copy)与深拷贝(Deep copy)的区别与各自的实现方式。

一、不同对象的赋值操作

Objective-C中有两类对象,一类是结构体(或者基本数据类型也算),另一类是NSObject对象。

对于结构体,代码直接会操作其实体,因此赋值操作会创建一个源对象的副本(一个新的对象);而对于NSObject对象,必须使用指针来操作对象,所以其赋值操作相当于复制了指针,而非对象,也就是说赋值操作使得源指针和新指针都指向同一个NSObject对象。这样讲有些难以理解,请看下面的代码:

// main.m 

#import <Foundation/Foundation.h> 

@interface TestObject : NSObject
{
  @public
  int x;
  int y;
}
@end 

@implementation TestObject
@end 

typedef struct TestStruct
{
  int x;
  int y;
}
TestStruct; 

int main(int argc, const char * argv[])
{ 

  @autoreleasepool { 

    TestStruct ts1 = {100, 50};
    NSLog(@"ts1: %p, %d, %d", &ts1, ts1.x, ts1.y); 

    TestStruct ts2 = ts1;
    NSLog(@"ts2: %p, %d, %d", &ts2, ts2.x, ts2.y); 

    TestObject* to1 = [[[TestObject alloc] init] autorelease];
    NSLog(@"to1: %p, %d, %d", to1, to1->x, to1->y); 

    TestObject* to2 = to1;
    NSLog(@"to2: %p, %d, %d", to2, to2->x, to2->y); 

  }
  return 0;
}

程序的运行结果如下:

ts1: 0x7fff63463898, 100, 50
ts2: 0x7fff63463890, 100, 50
to1: 0x7fc342d00370, 0, 0
to2: 0x7fc342d00370, 0, 0

程序代码首先定义了一个类TestObject(继承自NSObject),然后又定义了一个结构体TestStruct。这两者都包含两个整型的成员变量x和y。然后在main函数中,程序首先为TestStruct结构体ts1分配内存空间,并为其成员变量赋初值,x为100,y为50。然后通过NSLog函数打印出该结构体的地址和成员变量的值,即输出的第一行内容。接着,程序执行了赋值语句,将ts1赋值给另一个TestStruct结构体对象ts2,这条语句会为ts2分配另一块内存,然后把ts1的每个成员变量的值复制过来。第二行输出也可以看出来,地址不一样了,所以如果修改ts1的成员变量的值,是不会影响ts2的。

接着再来看TestObject。程序接着使用alloc静态方法分配了一块新的内存空间,然后通过init实例方法进行初始化(所有成员变量的值为0),最后将该内存空间的首地址返回。to1的实质就是一个指针,指向创建的TestObject对象。接着,程序将to1赋值给to2。to2也是一个指向TestObject对象的指针,其值与to1一样,即两者都指向同一个对象。所以在这种情况下,对to1的修改会同时影响to2。

二、对象拷贝

Foundation框架的NSObject类提供了两个方法,分别是copy和mutableCopy方法,用于对NSObject对象进行拷贝操作。copy方法会调用NSCopying协议的copyWithZone:方法,而mutableCopy会调用 NSMutableCopying协议的mutableCopyWithZone:方法。将上面的代码修改如下:

#import <Foundation/Foundation.h> 

@interface TestObject : NSObject
{
  @public
  int x;
  int y;
}
@end 

@implementation TestObject
- (NSString*)description
{
  return [NSString stringWithFormat:@"%@: %p, x: %d, y: %d", [self class], self, x, y];
}
@end 

typedef struct TestStruct
{
  int x;
  int y;
}
TestStruct; 

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    TestObject* to1 = [[[TestObject alloc] init] autorelease];
    to1->x = 100; to1->y = 50;
    TestObject* to2 = [[[TestObject alloc] init] autorelease];
    to2->x = 200; to2->y = 400;
    TestObject* to3 = [[[TestObject alloc] init] autorelease];
    to3->x = 300; to3->y = 500; 

    //创建包含to1, to2, to3的数组array1
    NSArray* array1 = [NSArray arrayWithObjects:to1, to2, to3, nil];
    NSLog(@"array1: %p, \n%@", array1, array1); 

    //array2是array1调用copy的结果
    NSArray* array2 = [array1 copy];
    NSLog(@"array2: %p, \n%@", array2, array2);
    [array2 release]; 

    //mutableArray2是array1调用mutableCopy的结果
    NSMutableArray* mutableArray2 = [array1 mutableCopy];
    NSLog(@"mutableArray2: %@, %p, \n%@", [mutableArray2 class], mutableArray2, mutableArray2);
    [mutableArray2 removeLastObject]; 

    NSLog(@"After remove last object of mutableArray2"); 

    NSLog(@"array1: %p, \n%@", array1, array1);
    NSLog(@"array2: %p, \n%@", array2, array2);
    NSLog(@"mutableArray2: %p, \n%@", mutableArray2, mutableArray2); 

    //mutableArray3是mutableArray2调用mutableCopy的结果
    NSMutableArray* mutableArray3 = [mutableArray2 mutableCopy];
    NSLog(@"mutableArray3: %p, \n%@", mutableArray3, mutableArray3);
    [mutableArray2 release]; 

    //array4是mutableArray3调用copy的结果
    NSArray* array4 = [mutableArray3 copy];
    NSLog(@"array4: %@, %p, \n%@", [array4 class], array4, array4);
    [mutableArray3 release];
    [array4 release];
  }
  return 0;
}

程序的运行结果如下:

2012-03-22 19:20:49.548 ObjectCopy[18042:403] array1: 0x7f9071414820,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400",
  "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.550 ObjectCopy[18042:403] array2: 0x7f9071414820,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400",
  "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.551 ObjectCopy[18042:403] mutableArray2: __NSArrayM, 0x7f9072800000,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400",
  "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.552 ObjectCopy[18042:403] After remove last object of mutableArray2
2012-03-22 19:20:49.552 ObjectCopy[18042:403] array1: 0x7f9071414820,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400",
  "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.553 ObjectCopy[18042:403] array2: 0x7f9071414820,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400",
  "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.553 ObjectCopy[18042:403] mutableArray2: 0x7f9072800000,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400"
)
2012-03-22 19:20:49.557 ObjectCopy[18042:403] mutableArray3: 0x7f90729000d0,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400"
)
2012-03-22 19:20:49.558 ObjectCopy[18042:403] array4: __NSArrayI, 0x7f9071416e70,
(
  "TestObject: 0x7f90714141b0, x: 100, y: 50",
  "TestObject: 0x7f90714141c0, x: 200, y: 400"
)

程序的运行结果有几点值得注意,首先是array1与array2的地址相同,因为NSArray对象在创建之后是不可以修改的。其次,NSArray的mutableCopy方法会返回一个NSMutableArray对象。第三,对于NSArray或者NSMutableArray来说,mutableCopy方法会创建新的可变数组对象,但其每个数组成员的值仅仅是原数组的一个指针赋值,这就是浅拷贝。而与之相对的则是深拷贝,即复制数组时不是复制数组每个元素的引用,而是创建一个与之相同的新对象。

第四,在NSArray对象上调用mutableCopy方法返回一个NSMutableArray对象,而在NSMutableArray对象上调用copy方法则返回一个NSArray对象,而不是NSMutableArray对象。

当然,以上讨论的是Foundation框架中的NSArray与NSMutableArray类,如果想要实现对自己创建的类的对象进行拷贝,则需要让类实现NSCopying协议。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2017-09-29

详解iOS的深浅拷贝

前言 OC对象的三种拷贝方式 OC的对象拷贝有如下三种方式,很多时候我们把深复制和完全复制混为一谈,其他他们是有区别的,具体如下 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制. 深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制. 完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制. 两图以避之 理解深复制(mutableCopy) 浅复制很简单,就不演示了,看

浅析iOS中的浅拷贝和深拷贝(copy和mutableCopy)

ios提供了copy和mutablecopy方法,顾名思义,copy就是复制了一个imutable的对象,而mutablecopy就是复制了一个mutable的对象. copy与retain的区别: copy是创建一个新对象,retain是创建一个指针,引用对象计数加1.Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象没有变化.copy减少对象对上下文的依赖. retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的

浅析javaScript中的浅拷贝和深拷贝

1.javaScript的变量类型 (1)基本类型: 5种基本数据类型Undefined.Null.Boolean.Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问. (2)引用类型: 存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置.当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据. JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一

深入理解python中的浅拷贝和深拷贝

在讲什么是深浅拷贝之前,我们先来看这样一个现象: a = ['scolia', 123, [], ] b = a[:] b[2].append(666) print a print b 为什么我只对b进行修改,却影响到了a呢?看过我在之前的文章中就说过:序列中保存的都是内存的引用. 所以,当我们通过b去修改里面的空列表的时候,其实就是修改内存中的同一个对象,所以会影响到a. a = ['scolia', 123, [], ] b = a[:] print id(a), id(a[0]), id(

详解Python核心编程中的浅拷贝与深拷贝

一.问题引出浅拷贝 首先看下面代码的执行情况: a = [1, 2, 3] print('a = %s' % a) # a = [1, 2, 3] b = a print('b = %s' % b) # b = [1, 2, 3] a.append(4) # 对a进行修改 print('a = %s' % a) # a = [1, 2, 3, 4] print('b = %s' % b) # b = [1, 2, 3, 4] b.append(5) # 对b进行修改 print('a = %s'

JS中实现浅拷贝和深拷贝的代码详解

(一)JS中基本类型和引用类型 JavaScript的变量中包含两种类型的值:基本类型值 和 引用类型值,在内存中的表现形式在于:前者是存储在栈中的一些简单的数据段,后者则是保存在堆内存中的一个对象. 基本类型值 在JavaScript中基本数据类型有 String , Number , Undefined , Null , Boolean ,在ES6中,又定义了一种新的基本数据类型 Symbol ,所以一共有6种. 基本类型是按值访问的,从一个变量复制基本类型的值到另一个变量后,这两个变量的值

浅析iOS中视频播放的几种方案

1.AVPlayer (1) 优缺点 优点:可以自定义 UI, 进行控制 缺点:单纯的播放,没有控制 UI(进度,暂停,播放等按钮),而且如果要显示播放界面, 需要借助AVPlayerLayer, 添加图层到需要展示的图层上 (2)实现远程视频播放 实现播放功能(只有声音) 1.导入框架 #import <AVFoundation/AVFoundation.h> 2.通过远程 URL 创建 AVPlayer 对象 NSURL *remoteURL = [NSURL URLWithString:

深入浅析IOS中UIControl

UIEvent是一系列UITouch的集合,在IOS中负责响应触摸事件.另外还提到了响应者链的概念,在IOS中,所有事件有一个最先响应者,事件可以沿着响应者链向下传递. 接下来是UIControl对象 UIControl是UIView的子类,当然也是UIResponder的子类.UIControl是诸如UIButton.UISwitch.UITextField等控件的父类,它本身也包含了一些属性和方法,但是不能直接使用UIControl类,它只是定义了子类都需要使用的方法. UIControl对

浅析IOS中播放gif动态图的方法

一.引言 在iOS开发中,UIImageView类专门来负责图片数据的渲染,并且UIImageView也有帧动画的方法来播放一组图片,但是对于gif类型的数据,UIImageView中并没有现成的接口提供给开发者使用,在iOS中一般可以通过两种方式来播放gif动态图,一种方式是通过ImageIO框架中的方法将gif文件中的数据进行解析,再使用coreAnimation核心动画来播放gif动画,另一种方式计较简单,可以直接通过webView来渲染gif图. 二.为原生的UIImageView添加类

Python中的复制操作及copy模块中的浅拷贝与深拷贝方法

程序中常常需要复制一个对象, 按思路应该是这样的 a = [1, 2, 3] b = a # [1, 2, 3] print b 已经复制好了,但是现在得改变一下第一个元素的值把它改成5 b[0] = 5 # [5, 2, 3] print b # [5, 2, 3] print a 我改变了b的第一个元素的值,但是a的值也改变了,这是因为python中的=是引用.a和b指向的是相同的列表,所以改变列表会出现以上的结果. 解决方法是切片操作 a = [1, 2, 3] b = a[:] b[0]

JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)

前言 说到深浅拷贝,必须先提到的是JavaScript的数据类型,之前的一篇文章JavaScript基础心法--数据类型说的很清楚了,这里就不多说了. 需要知道的就是一点:JavaScript的数据类型分为基本数据类型和引用数据类型. 对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的. 浅拷贝 浅拷贝的意思就是只复制引用,而未复制真正的值. const originArray = [1,2,3,4,5]; const originObj = {a:'a'