【IOS 开发学习总结-OC-27】★★objective-c之foundation 框架——对象复制

copy 与 mutableCopy 方法

copy 与 mutableCopy 方法总是返回原对象的副本,当程序对复制出来的副本 进行修改时,原来的 对象不受影响。

copy 方法

copy 方法用于复制对象的副本——通常来说,copy 方法总是返回对象的不可修改的副本。即使该对象本身是可以修改的。例如:程序调用NSMutableString的copy方法, 将会返回不可修改的字符串对象。

mutableCopy 方法

mutableCopy 方法用于复制对象的可变副本,——通常 来说,该方法总是返回该对象可修改的副本,即使被复制的对象本身是不可修改的。例如:程序调用 NSString 的 mutableCopy 方法,将会返回一个 NSMutableString 对象。
示例代码:
copyTest.m

#import <Foundation/Foundation.h>

int main(int argc , char * argv[])
{
    @autoreleasepool{
        NSMutableString* book = [NSMutableString 
            stringWithString:@"疯狂iOS讲义"];
        // 复制book字符串的可变副本
        NSMutableString* bookCopy = [book mutableCopy];
        // 对副本修改,对原字符串没有任何影响
        [bookCopy replaceCharactersInRange:
            NSMakeRange(2, 3)
            withString:@"Android"];
        // 此处看到原字符串的值并没有改变
        NSLog(@"book的值为:%@" , book);
        // 字符串副本发生了改变。
        NSLog(@"bookCopy的值为:%@" , bookCopy);
        NSString* str = @"fkit";
        // 复制str(不可变字符串)的可变副本
        NSMutableString* strCopy = [str mutableCopy]; //①
        // 向可变字符串后面追加字符串
        [strCopy appendString:@".org"];
        NSLog(@"%@" , strCopy);
        // 调用book(可变字符串)的copy方法,程序返回一个不可修改的副本
        NSMutableString* bookCopy2 = [book copy]; //②
        // 由于bookCopy2是不可修改的,因此下面代码将会出现错误
        [bookCopy2 appendString:@"aa"]; 
    }
}

运行结果:

2015-10-01 11:34:36.841 923[2572:115135] book的值为:疯狂iOS讲义
2015-10-01 11:34:36.845 923[2572:115135] bookCopy的值为:疯狂Android讲义
2015-10-01 11:34:36.846 923[2572:115135] fkit.org
2015-10-01 11:34:36.874 923[2572:115135] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

NSCopying 与 NSMutableCopy

虽然NSObject 提供了 copy 和 mutableCopy 方法,但自定义类并不能直接调用这两个方法来复制自身。为什么呢?
为了保证一个 对象可以调用 copy 方法来复制自身的不可变副本,需要做如下事情:

1.让该类实现NSCopying协议。
2.让该类实现copyWithZone:方法。

为了保证一个 对象可以调用 mutableCopy 方法来复制自身的可变副本,需要做如下事情:

1.让该类实现NSMutableCopying协议。
2.让该类实现mutableCopyWithZone:方法。

当程序调用对象大的 copy 方法来复制自身时,程序底层需要调用copyWithZone:方法来完成实际的复制工作,copy 实际上就是copyWithZone:方法的返回值。
当程序调用对象大的 mutableCopy 方法来复制自身时,与copy方法类似。

copyWithZone:(NSZone*)zone方法中的 zone 参数与不同德尔存储区有关,通常无需过多地关心该程序,只要将 zone 参数传给copyWithZone:(NSZone*)zone方法,即可创建该对象的副本。

示例代码:
FKDog.h

#import <Foundation/Foundation.h>

@interface FKDog : NSObject <NSCopying>
@property (nonatomic , strong) NSMutableString* name;
@property (nonatomic , assign) int age;
@end

FKDog.m

#import "FKDog.h"

@implementation FKDog
@synthesize name;
@synthesize age;
//- (id)copyWithZone:(NSZone*)zone
//{
//  NSLog(@"--执行copyWithZone--");
//  // 使用zone参数创建FKDog对象
//  FKDog* dog = [[[self class] allocWithZone:zone] init];
//  dog.name = self.name;
//  dog.age = self.age;
//  return dog;
//}
// 为深复制实现的copyWithZone:方法
- (id)copyWithZone:(NSZone*)zone
{
    NSLog(@"--执行copyWithZone--");
    // 使用zone参数创建FKDog对象
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    // 将原对象的name实例变量复制一份副本后赋值给新对象的name实例变量
    dog.name = [self.name mutableCopy];
    dog.age = self.age;
    return dog;
}
@end

FKDogTest.m

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

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 创建一个FKDog对象
        FKDog* dog1 = [FKDog new];
        dog1.name = [NSMutableString stringWithString:@"旺财"];
        dog1.age = 20;
        // 复制副本
        FKDog* dog2 = [dog1 copy];
        // 复制对象的可变副本
//      FKDog* dog2 = [dog1 mutableCopy];
        dog2.name = [NSMutableString stringWithString:@"snoopy"];
        dog2.age = 12;
        NSLog(@"dog1的名字为:%@" , dog1.name);
        NSLog(@"dog1的年龄为:%d" , dog1.age);
        NSLog(@"dog2的名字为:%@" , dog2.name);
        NSLog(@"dog2的年龄为:%d" , dog2.age);
    }
}

运行 结果:

2015-10-01 12:12:30.270 923[2689:128044] --执行copyWithZone--
2015-10-01 12:12:30.287 923[2689:128044] dog1的名字为:旺财
2015-10-01 12:12:30.288 923[2689:128044] dog1的年龄为:20
2015-10-01 12:12:30.288 923[2689:128044] dog2的名字为:snoopy
2015-10-01 12:12:30.289 923[2689:128044] dog2的年龄为:12

这里写图片描述

深复制(deep copy)与浅复制(shallow copy)

我们用以下程序来理解浅复制,深复制。
FKDog.h

#import <Foundation/Foundation.h>

@interface FKDog : NSObject <NSCopying>
@property (nonatomic , strong) NSMutableString* name;
@property (nonatomic , assign) int age;
@end

FKDog.m

#import "FKDog.h"

@implementation FKDog
@synthesize name;
@synthesize age;
//浅复制
- (id)copyWithZone:(NSZone*)zone
{
    NSLog(@"--执行copyWithZone--");
    // 使用zone参数创建FKDog对象
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    dog.name = self.name;
    dog.age = self.age;
    return dog;
}
// 为深复制实现的copyWithZone:方法
//- (id)copyWithZone:(NSZone*)zone
//{
//  NSLog(@"--执行copyWithZone--");
//  // 使用zone参数创建FKDog对象
//  FKDog* dog = [[[self class] allocWithZone:zone] init];
//  // 将原对象的name实例变量复制一份副本后赋值给新对象的name实例变量
//  dog.name = [self.name mutableCopy];
//  dog.age = self.age;
//  return dog;
//}
@end

运行结果:

2015-10-01 21:08:10.589 923[3306:181752] --执行copyWithZone--
2015-10-01 21:08:10.599 923[3306:181752] dog2name:snoopy
2015-10-01 21:08:10.600 923[3306:181752] dog1name:snoopy

说明:前面的代码中,我们只改了dog2 的 name 属性的值,为何 dog1 的 name 也发生改变了呢?
示意图:
这里写图片描述
这里写图片描述
dog1,dog2, 两个指针分别指向两个不同的 FKDog 对象,但这两个 FKDog 对象 的 name 实例变量都是指针,且它们都 指向同一个 NSMutabbleString 对象。所以会出现前面的情况。

这里就有了一个浅复制的概念:

浅复制浅复制——当对象的实例变量是指针变量时,如果哦程序只是复制该指针的地址,而不是真正复制指针所指向的对象。这种方式就被称为浅复制。

深复制:不仅会复制对象本身,还会”递归”复制每个指针类型的实例变量,直到两个对象没有任何共用的部分。上面将实现深复制的代码,注释解开以后即可以实现深复制——对象与复制对象彼此没有关联,相互不影响。

一般来说,foundation 框架中的类大部分都只实现浅复制。深复制的实现难度较大,尤其是当该对象包含大量的制造者类型的实例变量时——若某些实例变量里再次包含指针类型的实例变量,那么实现深复制会更加复杂。

setter 方法的复制选项

可以使用 copy 指示符合成 setter,getter 方法。 copy 指示符——指定当程序调用 setter 方法复制时,实际上是将传入参数的副本赋给程序的实例变量。
实例程序:
FKItem.h

#import <Foundation/Foundation.h>

@interface FKItem : NSObject
@property (nonatomic , copy) NSMutableString* name;
@end

FKItem.m

#import "FKItem.h"

@implementation FKItem
@synthesize name;
@end

FKItemTest.m

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

int main(int argc , char * argv[])
{
    @autoreleasepool{
        // 创建一个FKItem对象
        FKItem* item = [FKItem new];
        // 对item的name属性赋值
        item.name = [NSMutableString stringWithString:
            @"疯狂iOS讲义"];
        // 为item的name属性后追加一个字符串
        [item.name appendString:@"fkit"];
    }
}

运行结果:

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

运行出现了错误,无法进行添加字符操作,这是为啥呢?
是因为定义name 属性的时候,使用了 copy指示符,该指示符调用setName:方法(使用的点语法)时,程序实际上会使用参数的副本对 name 实例变量复制。
setName:`方法的代码如下:

-(void) setName:(NSMutableString *) aname
{
  name=[aname copy];
}

重点在这里:copy 方法默认是复制该对象的不可变副本,虽然程序传入的NSMutableString,但程序调用该参数的 copy 方法得到的是不可变副本。——因此,name 的值依然是不可变字符串。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值