iOS开发那些事儿(五)Objective-C浅拷贝与深拷贝

  1. 浅拷贝:copy操作出来的对象指针直接指向模板的地址。即两个对象公用一块内存地址

     1 #import <Foundation/Foundation.h>
     2 int main(int argc, const char * argv[]) {
     3     @autoreleasepool {
     4         NSString * testStr          = @"connor";
     5         NSLog(@"testStr‘s Address   = %p",testStr);
     6
     7         NSString * testStrCopy      = [testStr copy];
     8         NSLog(@"testStrCopy Address = %p",testStrCopy);
     9
    10         NSArray * array             = @[@1,@2,@3];
    11         NSLog(@"array Address       = %p",array);
    12
    13         NSArray * copyArray         = [array copy];
    14         NSLog(@"copyArray Address   = %p",copyArray);
    15     }
    16     return 0;
    17 }

    输出结果如下:

    DataStruct[11210:2189074] testStr‘s Address      = 0x100004280

    DataStruct[11210:2189074] testStrCopy Address = 0x100004280

    DataStruct[11210:2189074] array Address            = 0x100300650

    DataStruct[11210:2189074] copyArray Address     = 0x100300650

  2. 深拷贝:copy操作出来的对象指针直接指向新开辟的内存。即持有原对象的拷贝副本

     1 #import <Foundation/Foundation.h>
     2 #import "Father.h"
     3 int main(int argc, const char * argv[]) {
     4     @autoreleasepool {
     5         NSString * test     = @"a";
     6         NSLog(@"%p",test);
     7
     8         NSString * testCopy = [test mutableCopy];
     9         NSLog(@"%p",testCopy);
    10     }
    11     return 0;
    12 }

    输出结果如下:

    DataStruct[11355:2249168] 0x100004280

    DataStruct[11355:2249168] 0x100203cf0

  3. Foundation总结大致如下:
    •   NS*类型的类调用Copy属于浅拷贝(例如NSString、NSArray等等)
    •   NS*类型的类调用MutableCopy属于深拷贝(例如NSString、NSArray等等)
    •   NSMutable*类型无论调用Copy或者MutableCopy都属于深拷贝
  4. 拷贝构造:以上我们谈的都是Foundation中用到的深浅拷贝,如果我们自己定义了一个类。怎么去实现它的深浅拷贝呢?这里就要用到拷贝构造方法。iOS中默认NSObject是不遵循NSCopying(不变副本)、NSMutableCopying(可变副本)的,所以如果想一个对象可Copy,就必须实现其中两个协议并且重写copyWithZone、mutableCopyWithZone方法。
    •   下面我们定义一个Father类,来实现深拷贝:

       1 /** .h  */
       2 #import <Foundation/Foundation.h>
       3 @interface Father : NSObject<NSCopying>
       4 @property(nonatomic,copy)NSString * name;
       5 @end
       6
       7 /** .m  */
       8 #import "Father.h"
       9
      10 @implementation Father
      11 -(id)copyWithZone:(NSZone *)zone{
      12     Father * copyFather = [[self class]allocWithZone:zone];
      13     return copyFather;
      14 }
      15 @end

      写好了Father类我们在外界就可以利用copy的方法去copy出一个Father对象

       1 #import <Foundation/Foundation.h>
       2 #import "Father.h"
       3 int main(int argc, const char * argv[]) {
       4     @autoreleasepool {
       5         Father * fatherA = [[Father alloc]init];
       6         fatherA.name     = @"connor";
       7         NSLog(@"%p",fatherA);
       8
       9         Father * fatherB = [fatherA copy];
      10         NSLog(@"%@",fatherB.name);
      11         NSLog(@"%p",fatherB);
      12     }
      13     return 0;
      14 }

      输出结果:

      DataStruct[11383:2256524] 0x100206c80

      DataStruct[11383:2256524] (null)

      DataStruct[11383:2256524] 0x100300600

      从输出结果来看,两个Father实例的内存地址是不一样的我们实现了深拷贝。但是为什么第二个Father没有名字?在拷贝过程过虽然我是从你FatherA拷贝过来的,但是你并没有指定FatherA中的属性到底是什么方式拷贝过来。所以这里面如果想要实现名字也拷贝过来,需要我们自己去定义到底是深拷贝过来还是浅拷贝过来。这也符合苹果做事的风格嘛。

      1 #import "Father.h"
      2 //添加name的浅拷贝
      3 @implementation Father
      4 -(id)copyWithZone:(NSZone *)zone{
      5     Father * copyFather = [[self class]allocWithZone:zone];
      6     copyFather.name     = [self.name copy]; //如果想要深拷贝过来直接mutableCopy即可
      7     return copyFather;
      8 }
      9 @end

      结果输出两个name的内存地址:

      DataStruct[11409:2261412] 0x100004290

      DataStruct[11409:2261412] 0x100004290

    • 现在情况改变了,Father平时出门需要开车。Father需要持有Car类。那这个时候我们要如果复制这个爸爸!(因为复制出来的Father不能和之前那个Father开一辆车,所以我们应该用深拷贝。过程很简单直接贴代码了!)

       1 /** .h */
       2 #import <Foundation/Foundation.h>
       3
       4 @interface Car : NSObject<NSCopying>
       5 @property(nonatomic,copy)NSString * brand;
       6 @end
       7
       8 /** .m */
       9 @implementation Car
      10 -(id)copyWithZone:(NSZone *)zone{
      11     Car * copyCar = [[self class]allocWithZone:zone];
      12     copyCar.brand = [self.brand mutableCopy];
      13     return copyCar;
      14 }
      15 @end

      Car

      /** .h */
      #import <Foundation/Foundation.h>
      #import "Car.h"
      @interface Father : NSObject<NSCopying>
      @property(nonatomic,copy)NSString * name;
      @property(nonatomic,strong)Car    * car;
      @end
      
      /** .m */
      #import "Father.h"
      
      @implementation Father
      -(id)copyWithZone:(NSZone *)zone{
          Father * copyFather = [[self class]allocWithZone:zone];
          copyFather.name     = [self.name copy];
          copyFather.car      = [self.car copy];
          return copyFather;
      }
      @end

      Father

      #import <Foundation/Foundation.h>
      #import "Father.h"
      #import "Car.h"
      int main(int argc, const char * argv[]) {
          @autoreleasepool {
              Father * fatherA = [[Father alloc]init];
              fatherA.car      = [[Car alloc]init];
              NSLog(@"%p",fatherA.car);
      
              Father * fatherB = [fatherA copy];
              NSLog(@"%p",fatherB.car);
          }
          return 0;
      }

      main

      输出结果:

      DataStruct[11441:2268004] 0x100111f60

      DataStruct[11441:2268004] 0x1001154a0

    • copy这种操作如果发生在继承关系之后就有什么样的效果?或者说1.父类实现了NSCopying,子类该如果实现NSCopying。2.父类没有实现NSCopying,子类如何实现NSCopying?要接一个问题我们先看下表象是什么,然后通过表象来分析问题并解决问题。接下来我们先试着用第一种情况来看看表象:Son继承于Father

       1 /** .h */
       2 #import "Father.h"
       3 @interface Son : Father
       4 @property(nonatomic,copy)NSString * toy;
       5 @end
       6
       7 /** .m */
       8 #import "Son.h"
       9 @implementation Son
      10 @end

      Son

       1 #import <Foundation/Foundation.h>
       2 #import "Son.h"
       3 int main(int argc, const char * argv[]) {
       4     @autoreleasepool {
       5         Son * sonA = [[Son alloc]init];
       6         sonA.name  = @"connor";
       7         sonA.toy   = @"iPhone";
       8
       9         Son * sonB = [sonA copy];
      10
      11         NSLog(@"sonA‘s Address = %p",sonA);
      12         NSLog(@"sonB‘s Address = %p",sonB);
      13
      14         NSLog(@"sonA‘s name Address = %p",sonA.name);
      15         NSLog(@"sonB‘s name Address = %p",sonB.name);
      16
      17         NSLog(@"sonA‘s toy Address = %p",sonA.toy);
      18         NSLog(@"sonB‘s toy Address = %p",sonB.toy);
      19     }
      20     return 0;
      21 }

      main

      输出结果:

      DataStruct[11563:2279201] sonA‘s Address      = 0x100114bc0

      DataStruct[11563:2279201] sonB‘s Address          = 0x100111f70

      DataStruct[11563:2279201] sonA‘s name Address = 0x100004260

      DataStruct[11563:2279201] sonB‘s name Address = 0x726f6e6e6f6365

      DataStruct[11563:2279201] sonA‘s toy Address    = 0x100004280

      DataStruct[11563:2279201] sonB‘s toy Address = 0x0                                                                                  这个结果也在我们意料之内的嘛,Son是继承与Father类的,Father实现了Copy协议,所以这个copy协议实现会继承到子类。我的理解其实:当Son调用Copy方法的时候就将消息转发给Son抽象类,发现Son中没有实现Copt则向Father发送消息SuperSendMsg就相当于事件响应链传递一样。一直向上传递直到能被处理。**这里需要注意一点Father中  Father * copyFather = [[self class]allocWithZone:zone];这里用的是self而不是Father因为Son会走到这里来,我们不能限制死到底是谁来调用***。接下来我们继续看为什么toy是个空置,这个原因和上面说的嵌套copy有点类似。因为Father不清楚你Son的属性到底是怎么copy过来的。那么怎么修改呢?我们只要在toy中稍作修改即可

      1 @implementation Son
      2 -(id)copyWithZone:(NSZone *)zone{
      3      //这句话保证父类的copy也被call到
      4     Son * son = [super copyWithZone:zone];
      5     son.toy   = [self.toy mutableCopy];
      6     return son;
      7 }
      8 @end

      Son

      输出结果:toy成功被深拷贝

      DataStruct[11606:2301994] sonA‘s toy Address = 0x100004288

      DataStruct[11606:2301994] sonB‘s toy Address = 0x656e6f68506965

    • 接下来我们看一下第二种情况,父类没有实现NSCopying,子类如何实现NSCopying。直接让子类去实现就好了呗。父类如果需要深拷贝的我延迟到子类去执行呗!(这里有个问题,我的Son没有继承NSCopying,但是重写CopyWithZone居然没有报错??!!)

       1 #import "Son.h"
       2 @implementation Son
       3 -(id)copyWithZone:(NSZone *)zone{
       4 //    Son * son = [super copyWithZone:zone];
       5     Son * son = [[self class] allocWithZone:zone];
       6     son.name  = [self.name mutableCopy];
       7     son.toy   = [self.toy mutableCopy];
       8     return son;
       9 }
      10 @end

      Son

      输出结果:

      DataStruct[11660:2317623] sonA‘s Address          = 0x100206e00

      DataStruct[11660:2317623] sonB‘s Address          = 0x1002040e0

      DataStruct[11660:2317623] sonA‘s name Address = 0x100004260

      DataStruct[11660:2317623] sonB‘s name Address = 0x726f6e6e6f6365

      DataStruct[11660:2317623] sonA‘s toy Address    = 0x100004280

      DataStruct[11660:2317623] sonB‘s toy Address    = 0x656e6f68506965

  5. Copy
时间: 2024-10-08 12:31:48

iOS开发那些事儿(五)Objective-C浅拷贝与深拷贝的相关文章

从零开始学ios开发(十五):Navigation Controllers and Table Views(中)

这篇内容我们继续上一篇的例子接着做下去,为其再添加3个table view的例子,有了之前的基础,学习下面的例子会变得很简单,很多东西都是举一反三,稍稍有些不同的内容,好了,闲话少说,开始这次的学习. 如果没有上一篇的代码,可以从这里下载Nav_1 1)第三个subtableview:Controls on Table Rows这个例子,我们将为每个table view的每一行添加一个按钮,这个按钮将放在accessory icon的位置(之前我们使用过accessoryType,其实这也是一个

iOS开发——语法OC篇&amp;Objective-C新特性的总结

Objective-C新特性的总结 1.nonnull nonnull : 标示当前属性不为空,让外界放心用,只做标示用,即使为空,也木有办法    相当于swift里面的 ! 号 @property (nonnull, nonatomic, strong) NSString *name; 2.nullablenullable : 标示当前属性可能为空,让外界使用时注意    相当于swift里面的 ? 号 @property (nullable, nonatomic, strong) NSSt

iOS开发那些事儿(一)热补丁

一.热补丁作用:修复导致崩溃的错误.替换/增加方法.替换原来的界面等等 二.实现手段:JSPatch (使用Objective-C Objective-C和JavaScript jspatch桥.你可以调用任何Objective-C类和方法在JavaScript中的只是其中一小发动机.这使应用程序获取脚本语言的力量:添加模块或更换Objective-C代码来修复漏洞动态).传送门:https://github.com/agelessman/JSPatch 三.Demo: JSPatch导入:拷贝

【ios开发学习 - 第五课】UITableView使用

在开发iphone的应用时基本上都要用到UITableView,这里讲解一下UITableView的使用方法及代理的调用情况 - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 NSArray *array1_=@[@"张铁林",@"张国立",@"张国荣",@"张艺谋",@"张惠妹"]; NSArray *array2_=@[@"李小龙"

iOS开发那些事儿(四)the dark arts of the Objective-C runtime

一."Black Magic":Method Swizzling 利用 Runtime 特性把一个方法的实现与另一个方法的实现进行替换,也可以用runtime的四维理解——修改Dispatch Table让一个方法的IMP对应到我们指定的IMP上去 二.实例说明:比如我们想要在APP中记录每一个ViewController的出现次数 三.实例分析: 第一种思路就是在ViewController出现的一瞬间(viewDidAppear)我就用记录工具记录一条日志. 1 @implemen

IOS开发知识(五)

流程控制 顺序结构:默认的流程结构.按照书写顺序执行每一条语句. 选择结构:对给定的条件进行判断,再根据判断结果来决定执行哪一段代码. 循环结构:在给定条件成立的情况下,反复执行某一段代码. 选择结构-if 简单使用 if (表达式)  语句1; if(count>50)  开班;  上课; if(表达式)  语句1;  else 语句2; f(count>50)  开班;  else 不开班; if(表达式)  {  } if(count>50)  {开班; 布置课室;}  else

ios开发核心动画五:转场动画

#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *imageV; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } static int _i = 1; -(void)touchesBegan:(NSSet<U

iOS开发那些事儿(一)轮播器

前言 市面上绝大部分的APP被打开之后映入眼帘的都是一个美轮美奂的轮播器,所以能做出一个符合需求.高效的轮播器成为了一个程序员的必备技能.所以今天的这篇博客就来谈谈轮播器这个看似简单的控件其中蕴含的道理. 正文  首先我们来分析一下该如何去实现一个类似下图的轮播器(图片数量.URL由服务器返回):   策略一:UIScrollView->UIImageView->NSTimer轮询 这算是常规的策略,但是如果仔细想想,如果服务器返回给你50图片是不是就需要创建50个UIImageView来做容

ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型

一:KVC和KVO的学习 #import "StatusItem.h" /* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字典,得到所有的key,value值,再利用kvc, setVaue forkey来为value赋值 2: [item setValue:@"来自即刻笔记" forKey:@"source"],内部的底层实现, 1.首先去模型中查找有没有setSource,找到,直接调用