美文网首页
关于 OC 中的 copy

关于 OC 中的 copy

作者: __zimu | 来源:发表于2016-01-04 10:51 被阅读821次

首先

iOS开发者都知道, 在写属性的时候, 一般 NSString 的属性都用 copy 去修饰, 那么你们有没有想过, 到底为什么要用 copy, 而这个 copy 又确实起了什么作用呢?

copy 的效果

在 oc 中, copy属性的直接效果是将原对象进行一个 '拷贝', 创建出一个新的副本, 从而达到二者互不干扰的效果.
那么, 请问你们有没有试过, 将 NSString 用 strong 去修饰呢..想必试过的人一定知道后果有多严重吧.(挖坑^.^)

copy 的种类

细心的人会发现, oc 中是支持两种拷贝的 即 copymutableCopy
二者区别如下:

 - copy
建立对象的副本 {
    如果对象有可变/不可变版本的区别,copy方法,只能拷贝出不可变的版本
    如果对象没有可变/不可变的区别,copy方法就是建立一个副本
}
 - mutableCopy 
建立对象的可变副本(如果对象有"可变/不可变"版本的区别,才需要使用此方法)
--------------------------------------------------------
副本的特点:彼此的内容一样,具有相同的方法

深拷贝 & 浅拷贝

由于我们的 "拷贝" 操作需要区分 可变/不可变 .于是引出了关于 深拷贝&浅拷贝 的问题.
那么如何区分呢?
如下:

 - 都会建立新的副本,深拷贝(只要有一个可以修改,就是深拷贝)
 可变 -> 可变
 可变 -> 不可变
 不可变 -> 可变
 
 - 不会建立新的副本,只是引用计数+1,浅拷贝,指针拷贝(两个对象前后都不需要修改)
 不可变 -> 不可变

说的简单一点, 对于我们最长使用的 NSString来说, 只有当不可变字符串拷贝到不可变字符串的时候, 我们的 拷贝 操作才是浅拷贝, 而这恰好是我们开发中最长用到的.

实际应用

模拟环境

首先我们模拟这样一个环境

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSMutableString *title;
@end

我们有这样一个Person模型, 为了区分 可变/不可变, 分别用 NSString/NSMutableString 来修饰.

由于我们要展示的内容都是基于网络的, 换句话说, 我们要展示的内容都是从网络上获取到以后才展示的.
在控制器里写下如下代码:

- (void)viewDidLoad {
    [super viewDidLoad];
  
    // 模拟从网络获取到一个字符串
    NSMutableString *strM = [NSMutableString stringWithString:@"BOSS"];
    // 将得到的字符串赋值给 Person对象的 title 属性
    Person *p = [[Person alloc] init];
    p.title = strM;
    //分别修改 strM 和 p.title 的字符串内容
    [strM setString:@"经理一"];
    [p.title setString:@"经理二"];
    
    NSLog(@"----> %@ %@", p.title, strM);
}

各位小伙伴可以自己尝试运行一下这段代码, 猜一下打印结果会是什么?
dang~~~
结果如下

2016-01-04 10:12:28.303 06-Copy属性[10095:669120] -[NSTaggedPointerString setString:]: unrecognized selector sent to instance 0xa00000053534f424
2016-01-04 10:12:28.306 06-Copy属性[10095:669120] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString setString:]: unrecognized selector sent to instance 0xa00000053534f424'

没错. 模拟器果断的崩给我看.. 那么具体是为什么呢 ?

我们逐一排查代码问题, 可以轻松发现 报错的原因就在于

    [p.title setString:@"经理二"];

这一句话, 那么我们明明将 title的属性设置为了 NSMutableString, 为什么还在运行的时候报了一个 "未发现 setString:" 方法的错误呢 ?
我们尝试解决一下这个问题:

方法一:

将 title 的描述设置为 strong, 这样再运行就不会报错了.但是这样会造成一个更大的隐患, 这里后面会解释

@property (nonatomic, strong) NSMutableString *title;
方法二:

好吧, 其实没有方法二. 这里就涉及到我上面提过的

如果对象有可变/不可变版本的区别,copy方法,只能拷贝出不可变的版本

所以, 表面上我们修改的是一个我们定义为"NSMutableString"的 title属性, 但是由于我们用copy修饰了这个属性, 于是当我们使用p.title = strM赋值的时候, 其实系统会默认的将原有的title属性做一次copy操作.

所以, 我们实际操作的实际上是被copy出来的一份不可变的字符串, 这也是为什么当我们再调用[p.title setString:@"经理二"];的时候, 模拟器会崩给我们看的原因.

---------------------------

那么, 如果我们一定要修改原始的title所在的内存空间,要怎么办呢 ? 这就是方法一的解决办法, 用strong去修饰, 这样当我们再修改的时候, 系统不会创建一个备份, 而是直接在原始的内存空间去修改. 这么做的隐患在哪呢? 看下面的代码:

@property (nonatomic, strong) NSMutableString *title;

- (void)viewDidLoad {
    [super viewDidLoad];
  
    // 模拟从网络获取到一个字符串
    NSMutableString *strM = [NSMutableString stringWithString:@"BOSS"];
    // 将得到的字符串赋值给 Person对象的 title 属性
    Person *p = [[Person alloc] init];
    p.title = strM;
    
    [strM setString:@"经理一"];
    [p.title setString:@"经理二"];
    NSLog(@"----> %@ %@", p.title, strM);

    [strM setString:@"经理三"];
    NSLog(@"----> %@ %@", p.title, strM);
}

属性为 strong, 当我们在控制器里加上

   [strM setString:@"经理三"];
   NSLog(@"----> %@ %@", p.title, strM);

再看看打印出来的结果会是什么?

2016-01-04 10:33:25.878 06-Copy属性[10495:685186] ----> 经理三 经理三

没错, 你会惊奇的发现, 我们只是修改了strM的值, 为什么p.title也跟着变了呢?
这就是我上面说的, 当用strong去修饰的一个大坑.

原因(.填坑)
  1. 由于我们用strong去修饰了title属性, 系统中就只有一份属于title的内存空间
  2. 当我们把 strM赋值给title的时候, 其实就是让两者的指针指向了同一块内存空间
  3. 所以当我们修改了strM的时候, 实际修改的就是strM指针指向的内块内存空间里面的值
  4. 所以当我们打印p.title的时候, 由于titlestrM指向同一块内存空间, 而且里面的值已经被strM修改过了, 所以打印出来的结果就 都变成了 经理三

综上

  1. 由于我们使用的是copy, 而且实际操作的是被新拷贝出来的存储空间, 所以对于原始属性是NSString/NSMutableString并不那么在意
  2. 而在 oc 中, 所有的带mutable的东西都是线程不安全的, 所以在我们实际开发中, 对于NSString属性来说, 我们的规范样式一般是这样样子的
@property (nonatomic, copy) NSString *name;
  1. 现在小伙伴们, 明白为什么我们定义字符串的时候要用 copy 了么?

最后再说两句

  1. 文笔不好, 可能表达逻辑有点混乱, 有任何疑问请留言.
  2. copy 的应用当然不止这些, 下篇文章应该会讲一讲 对于对象的copy, 也即是设计模式中的 原型设计模式
  3. 打个广告...求职中,打包求带走

相关文章

  • 关于 OC 中的 copy

    首先 iOS开发者都知道, 在写属性的时候, 一般 NSString 的属性都用 copy 去修饰, 那么你们有没...

  • 关于OC中的copy

    首先NSObject中声明了两个方法copy和mutableCopy,所以继承自NSObject的对象都可以调用。...

  • OC中关于copy和mutableCopy

     这里主要有两个概念深拷贝和浅拷贝,主要涉及两个方法 - (id)copy;、- (id)mutableCopy;...

  • OC中的copy

    什么是copy? copy从字面意思来看就是“复制”、“拷贝”,是一个产生副本的过程。而在OC中,copy是用来复...

  • OC中的copy

    前言 不敢说覆盖OC中所有copy的知识点,但最起码是目前最全的最新的一篇关于 copy的技术文档了。后续发现有新...

  • OC中的copy

    1.所有被copy修饰的对象都会进行深copy吗? 答案:NO,例如block 全局block被copy修饰不会有...

  • 【OC梳理】Copy、KVC、KVO

    Copy OC中copy的作用是:利用一个源对象产生一个副本对象,它们之间不会相互影响。 关于深拷贝与浅拷贝 深拷...

  • 浅析OC中的copy、mutableCopy

    关于OC中的copy、mutableCopy看过别人写的很多文章,平时工作中也经常用,但并没有验证过,今天来...

  • 关于copy、mutableCopy的讨论(2)

    上一篇是关于OC中字符串的copy、mutablecopy的讨论,本篇将对集合类型进行讨论,以NSArray为例 ...

  • OC中的copy/mutableCopy

    这周由于公司招新人,面试官的一道关于copy和mutalbeCopy的问题引发了组员之间的激烈探讨,这时有位号称s...

网友评论

      本文标题:关于 OC 中的 copy

      本文链接:https://www.haomeiwen.com/subject/yrjohttx.html