OC学习篇之---对象的拷贝

在前一篇文章中我们说到了如何解决对象的循环引用问题:http://blog.csdn.net/jiangwei0910410003/article/details/41926369,这一篇文章我们就来介绍一下OC中的对象拷贝概念,这个对于面向对象语言中都会有这种的问题,只是不同的语言有不同的解决方式:C++中有拷贝构造函数,Java中需要实现Cloneable接口,在clone方法中进行操作。但是不过OC更偏向于Java这种方式,OC中如果一个对象需要被拷贝,他需要实现协议:

<NSCopying>、<NSMutableCopying>

从名字上我们可以看到,一个协议是用于不可变对象的,一个协议适用于可变对象的

首先来介绍一下对象的拷贝的概念吧:

为什么要由对象的拷贝这么一个概念呢?看一个场景:假如现在一个对象中又一个数组对象,现在我们生成一个对象,同时将这个对象赋值给另外一个对象,那么现在问题是这两个对象中的数组对象是同一个,那么如果一个对象中去修改这个数值中的内容,另外一个对象中的数组内容也会被修改,相当于这个数组对象是共享的,当然我们有时候是不希望这种形式的出现的,这时候我们就出现了对象的拷贝。

具体来看一个例子吧

一、系统类对象的拷贝

//
//  main.m
//  30_CopyObject
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

/**

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

        //对象具备拷贝功能,必须实现如下协议
        //<NSCopying>、<NSMutableCopying>

        //copy方法返回的是一个不可变对象,mutableCopy方法返回的是一个可变对象

        /*
        NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
        NSMutableArray *array2 = [array1 retain];
        //retain只是引用计数+1,没有创建新的对象
        //array1与array2指针相同,指向同一个对象
        if(array1 == array2){
            NSLog(@"array1 == array2");
            NSLog(@"array1的引用计数:%ld",array1.retainCount);
        }
         */

        NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
        //复制对象,创建一个新的副本对象
        //这里使用copy方法复制,返回的是一个不可变数组,但是用一个可变数组来声明,但是我们关心的是指针的的内容,而不是类型
        //所以array2的真实类型还是不可变类型的
        NSMutableArray *array2 = [array1 copy];//array2计数为:1,因为是新创建出来的对象

        //使用mutableCopy方法,返回的就是可变数组
        //当然这种方法只针对于那些有可变对象之分有用,对于其他的对象这个方法和copy方法的效果是一样的
        NSMutableArray *array3 = [array1 mutableCopy];

        if(array1 != array2){
            NSLog(@"array1 != array2");
            NSLog(@"array1的引用计数:%ld",array1.retainCount);
            NSLog(@"array2的引用计数:%ld",array2.retainCount);
        }
        [array2 release];
        [array1 release];

    }
    return 0;
}

我们看到,NSMutableArray有一个mutableCopy方法,这样返回的一个数组对象就是一个拷贝对象了。

但是这里需要注意的是:

copy方法和mutableCopy方法的区别

这两个方法的区别只在于那些有可变对象和不可变对象之分的对象上,对于没有这种区分的对象来说,这两个方法的效果是一样的。

[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]

[不可变对象 mutableCopy是真拷贝

二、深拷贝和浅拷贝

在拷贝对象中也是有深拷贝和浅拷贝之分的

浅拷贝:只拷贝所有属性对象的指针

深拷贝:拷贝属性对象的内容

看个例子:

Person.h

//
//  Person.h
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCopying>

@property(nonatomic,retain)NSMutableArray *apples;
@property(nonatomic)int age;

@end

Person.m

//
//  Person.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import "Person.h"

@implementation Person

- (id)copyWithZone:(NSZone *)zone{
    //创建一个新的副本对象
    //这个方法是会被继承的,所以这里还是不用
    //[Person allocWithZone:<#(struct _NSZone *)#>];
    Person * p = [[self class] allocWithZone:zone];
    //p.apples = _apples;//是指针赋值,所以还是浅拷贝
    //深拷贝
    //拷贝之后引用计数会+1,需要release以下
    p.apples = [_apples mutableCopy];
    p.age = _age;

    [p.apples release];

    //但是如果我们使用->语法就不需要了,因为我们没有使用set方法,引用计数没有操作
    //但是这种方式我们不采用
    //p->_apples = [_apples mutableCopy];

    return p;
}

@end

我们看到,Person实现了NSCopying协议,然后需要实现一个方法:copyWithZone

在这个方法中我们开始进行拷贝操作:

Person类中有一个属性类型是数组

这里我们需要生成一个Person对象,然后进行属性的拷贝,最后在返回这个对象

浅拷贝:直接复制数组指针

深拷贝:直接复制数组的内容,这里可以直接使用mutableCopy方法进行实现

测试类

main.m

//
//  main.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"

//深拷贝和浅拷贝
//默认是浅拷贝
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:2];

        for(int i=0;i<2;i++){
            Person *p = [[Person alloc] init];
            [array1 addObject:p];
            [p release];
        }

        //引用计数都是1
        for(Person *p in array1){
            NSLog(@"复制之前的引用计数:%ld",p.retainCount);
            NSLog(@"复制之前的指针:%p",p);
        }

        //引用计数都是2,因为是浅拷贝,又有指针指向对象了,array2也是使用了person
        //浅拷贝:只拷贝对象指针
        //深拷贝:复制属性
        NSArray *array2 = [array1 copy];
        for(Person *p in array2){
            NSLog(@"复制之前的引用计数:%ld",p.retainCount);
            NSLog(@"复制之前的指针:%p",p);
        }

        //这里Person中有一个属性是NSMutableArray,但是我们只是赋值,并不是拷贝
        //所以这里还不算是深拷贝
        Person *p = [[Person alloc] init];
        p.apples = [NSMutableArray arrayWithObjects:@"iphone",@"ipad", nil];
        p.age = 20;

        Person *p1 = [p copy];

        if(p != p1){
            NSLog(@"p1.age=%d",p1.age);
            NSLog(@"p1.apples=%@",p1.apples);
        }

    }
    return 0;
}

三、字符串的拷贝

//
//  main.m
//  32_NSStringCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "Person.h"

//字符串为什么使用copy
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        NSMutableString *name = [NSMutableString stringWithString:@"jack"];
        p.name = name;

        //人的名字被修改了
        //如果Person的name是retain,则此处的name和person对象的name执行的是同一个字符串对象
        //此处的name修改之后,会导致person的name也被修改,破坏了person对象的封装性
        //正常情况下,我们会使用set方法设置名字
        //所以如果使用的是copy的话,就不会修改名字了
        [name appendString:@"-tom"];

        //Foundation框架中可复制的对象,当我们拷贝的是一个不可变对象时候
        //他的作用相当于retain(系统做的内存优化)

        //所以这里的如果换成NSString类型的时候,其实没有拷贝的动作的,因为NSString是不可变的
        //但是使用mutableCopy就可以做到拷贝了,mutableCopy是真正意义上的拷贝
        //mutableCopy拷贝方法,不管什么对象都是真实拷贝

        //[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]
        //[不可变对象 mutableCopy是真拷贝
    }
    return 0;
}

这里为什么要单独说一下字符串的拷贝呢?

因为字符串是一个特殊的对象,我们应该调用他的copy方法。因为我们对于字符串其实我们是期望他只有一分值得,就看上面的例子:

我们用NSMutableString产生一个name,然后将其赋值给person对象,当我们在外面修改name的内容的时候,其实person的name属性的值也应该修改。所以我们一般在拷贝字符串对象的时候,都会调用他的copy方法

总结

这一篇文章主要介绍了OC中对象拷贝的相关概念和知识点。我们在操作对象的时候,有时候进行拷贝,还要仔细考虑一下是深拷贝还是浅拷贝。

时间: 2024-10-15 12:11:18

OC学习篇之---对象的拷贝的相关文章

(转载)OC学习篇之---对象的拷贝

在前一篇文章中我们说到了如何解决对象的循环引用问题,这一篇文章我们就来介绍一下OC中的对象拷贝概念,这个对于面向对象语言中都会有这种的问题,只是不同的语言有不同的解决方式:C++中有拷贝构造函数,Java中需要实现Cloneable接口,在clone方法中进行操作.但是不过OC更偏向于Java这种方式,OC中如果一个对象需要被拷贝,他需要实现协议: <NSCopying>.<NSMutableCopying> 从名字上我们可以看到,一个协议是用于不可变对象的,一个协议适用于可变对象

OC学习篇之---总结和学习目录

今天终于把OC的基础知识学习完了,但是这些知识只是最基础的,还有很多高级知识,这个可能需要后面慢慢的去学习才能体会到.下面就是这次学习OC的目录教程,如果大家发现有什么不正确的地方,请指正,小弟是新生,多请OC老鸟来喷~~ 1.OC学习篇之---概述 2.OC学习篇之---第一个程序HelloWorld 3.OC学习篇之---类的定义 4.OC学习篇之---类的初始化方法和点语法的使用 5.OC学习篇之---类的三大特性(封装,继承,多态) 6.OC学习篇之[email protected]关键

OC学习篇之---单例模式

在之前的一片文章中介绍了对象的拷贝相关知识:http://blog.csdn.net/jiangwei0910410003/article/details/41926531,今天我们来看一下OC中的单例模式,单例模式在设计模式中用的可能是最多的一种了,而且也是最简单的一种 实现单例模式有三个条件 1.类的构造方法是私有的 2.类提供一个类方法用于产生对象 3.类中有一个私有的自己对象 针对于这三个条件,OC中都是可以做到的 1.类的构造方法是私有的 我们只需要重写allocWithZone方法,

(转载)OC学习篇之---单例模式

在之前的一片文章中介绍了对象的拷贝相关知识,今天我们来看一下OC中的单例模式,单例模式在设计模式中用的可能是最多的一种了,而且也是最简单的一种 实现单例模式有三个条件 1.类的构造方法是私有的 2.类提供一个类方法用于产生对象 3.类中有一个私有的自己对象 针对于这三个条件,OC中都是可以做到的 1.类的构造方法是私有的 我们只需要重写allocWithZone方法,让初始化操作只执行一次 2.类提供一个类方法产生对象 这个可以直接定义一个类方法 3.类中有一个私有的自己对象 我们可以在.m文件

(转载)OC学习篇之[email&#160;protected]关键字的作用以及#include和#import的区别

前一篇文章说到了OC中类的三大特性,今天我们来看一下在学习OC的过程中遇到的一些问题,该如何去解决,首先来看一下我们之前遗留的一个问题: 一.#import和#include的区别 当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误 但是使用两次#import的话,不会报错,所以他可以解决重复导入的问题,他会做一次判断,如果已经导入一次就不导入了 二.关键字@class的作用 在来看一下OC中的关键字@class的作用,在

OC学习篇之[email&#160;protected]关键字的作用以及#include和#import的区别

前一篇文章说到了OC中类的三大特性:http://blog.csdn.net/jiangwei0910410003/article/details/41707161今天我们来看一下在学习OC的过程中遇到的一些问题,该如何去解决,首先来看一下我们之前遗留的一个问题: 一.#import和#include的区别 当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误 但是使用两次#import的话,不会报错,所以他可以解决重复导入

OC学习6——面相对象的三大特性

我们在学习Java的时候都知道,类有三大特性:继承,封装,多态,这也是面向对象的三大特征.OC学习篇之---类的三大特性(封装,继承,多态) 1.封装(Encapsulation)是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问(该隐藏的隐藏,该暴露的暴露).封装的主要目的是: 隐藏类的实现细节 让使用者只能通过预先定义好的方法来访问数据,从而可以在该方法中加入控制逻辑,限制对成员变量的不合理访问 可进行数据检查,从而有

OC学习篇之---数组对象的引用计数问题和自动释放池的概念

之前一片文章中我们介绍了OC中的两个关键字@property和@synthesize的使用的使用: http://blog.csdn.net/jiangwei0910410003/article/details/41925967 今天我们来看一下OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题 // // main.m // 26_NSArrayMemeryManager // // Created by jian

(转载)OC学习篇之---数组对象的引用计数问题和自动释放池的概念

之前一片文章中我们介绍了OC中的两个关键字 @property 和 @synthesize 的使用的使用,今天我们来看一下OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题 1 // 2 // main.m 3 // 26_NSArrayMemeryManager 4 // 5 // Created by jiangwei on 14-10-12. 6 // Copyright (c) 2014年 jiangwei.