ios學習路線—Objective-C(深淺拷貝)

来源:https://www.cnblogs.com/jiuyi/archive/2018/12/14/10120745.html
-Advertisement-
Play Games

在ObjC中,什麼是深淺拷貝? 深淺拷貝分別指深拷貝和淺拷貝,即 mutableCopy 和 copy 方法。 copy複製一個不可變對象,而 mutableCopy 複製一個 mutable 可變對象。 非容器類對象 如NSString,NSNumber等一類對象 示例1: 查看記憶體可以發現,st ...


在ObjC中,什麼是深淺拷貝?  深淺拷貝分別指深拷貝和淺拷貝,即 mutableCopy 和 copy 方法。  copy複製一個不可變對象,而 mutableCopy 複製一個 mutable 可變對象。

 

非容器類對象  如NSString,NSNumber等一類對象 
示例1:

// 非容器類對象
    NSString *str = @"origin string";
    NSString *strCopy = [str copy];
    NSMutableString *mstrCopy = [str mutableCopy];
    [mstrCopy appendString:@"??"];

//    NSLog(@"array1     = %p", array1);
//    NSLog(@"arrayCopy1 = %p", arrayCopy1);

查看記憶體可以發現,str和strCopy指向的是同一塊記憶體區域,我們稱之為弱引用(weak reference)。而mstrCopy是真正的複製,系統為其分配了新記憶體空間,保存從str複製過來的字元串值。從最後一行代碼中修改這些值而不影 響str和strCopy中可證明。

示例2:

    NSMutableString *mstr = [NSMutableString stringWithString:@"origin"];
    NSString *strCopy = [mstr copy];
    NSMutableString *mstrCopy = [mstr copy];
    NSMutableString *mstrMCopy = [mstr mutableCopy];
    //[mstrCopy appendString:@"1111"];  //error
    [mstr appendString:@"222"];
    [mstrMCopy appendString:@"333"];

以上四個對象所分配的記憶體都是不一樣的。而且對於mstrCopy,它所指向的其實是一個imutable對象,是不可改變的,所以會出錯。這點要註意,好好理解。

小結: 
1.如果對一個不可變對象複製,copy是指針複製,即淺拷貝;而mutableCopy則是對象複製,即深拷貝。 
2.如果是對可變對象複製,都是深拷貝,但是copy複製返回的對象是不可變的。

容器類對象深淺複製 
比如NSArray,NSDictionary等。對於容器類本身,上面討論的結論也適用的,下麵探討的是複製後容器內對象的變化。

示例3:

    /* copy返回不可變對象,mutablecopy返回可變對象 */
    NSArray *array1     = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
    NSArray *arrayCopy1 = [array1 copy];
    //arrayCopy1是和array同一個NSArray對象(指向相同的對象),包括array裡面的元素也是指向相同的指針
    NSLog(@"array1 retain count: %d",[array1 retainCount]);      // 2
    NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);    //  2

    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
    //mArrayCopy1是array1的可變副本,指向的對象和array1不同,但是其中的元素和array1中的元素指向的還是同一個對象。mArrayCopy1還可以修改自己的對象
    [mArrayCopy1 addObject:@"de"];
    [mArrayCopy1 removeObjectAtIndex:0];

array1和arrayCopy1是指針複製,而mArrayCopy1是對象複製,符合前面示例1討論的結論。mArrayCopy1可以改變其內的元素:刪除或添加。但容器內的元素內容都是淺拷貝。

 

示例4

    NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    NSArray *mArrayCopy2 = [mArray1 copy];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    // mArray1和mArrayCopy2指向同一對象,retain值+1。

    NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    //mArrayCopy2和mArray1指向的是不一樣的對象,但是其中的元素都是一樣的對象——同一個指針

    NSMutableString *testString = [mArray1 objectAtIndex:0];
    //testString = @"1a1";//這樣會改變testString的指針,其實是將@“1a1”臨時對象賦給了testString
    [testString appendString:@" tail"];//這樣以上三個數組的首元素都被改變了

由此可見,對於容器而言,其元素對象始終是指針複製。如果需要元素對象也是對象複製,就需要實現深拷貝。

 

示例5

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"] ,[NSString stringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSLog(@"array[2] = %@, deepCopyArray[2]=%@ ",[array objectAtIndex:2], [deepCopyArray objectAtIndex:2]);  //輸出值是一樣的
NSLog(@"array[2]         %p", [array objectAtIndex:2]);
NSLog(@"deepCopyArray[2] %p", [deepCopyArray objectAtIndex:2]);   
  //最後兩個列印的log記憶體地址值是一樣的


NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray 是完全意義上的深拷貝,而deepCopyArray則不是,對於 deepCopyArray 內的不可變元素其還是指針複製。

 

自己實現深拷貝的方法 NSDictionaryMutableDeepCopy.h

#import <foundation /Foundation.h>
@interface NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy;
@end

NSDictionaryMutableDeepCopy.m

#import "NSDictionaryMutableDeepCopy.h"
@implementation NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy {
    NSMutableDictionary *ret = [[NSMutableDictionary alloc]
                                initWithCapacity:[self count]];
    NSArray *keys = [self allKeys];
    for (id key in keys) {
        id oneValue = [self valueForKey:key];
        id oneCopy = nil;
        if ([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
            oneCopy = [oneValue mutableDeepCopy];
        }
        else if ([oneValue respondsToSelector:@selector(mutableCopy)]) {
            oneCopy = [oneValue mutableCopy];
        }
        if (oneCopy == nil) {
            oneCopy = [oneValue copy];
        }
        [ret setValue:oneCopy forKey:key];
    }
    return ret;
}

使用類別方法來實現 
如果是我們定義的對象,那麼我們自己要實現NSCopying,NSMutableCopying這樣就能調用copy和mutablecopy了。舉個例子

@interface MyObj : NSObject<nscopying ,NSMutableCopying>
{
         NSMutableString *name;
         NSString *imutableStr;
         int age;
}
@property (nonatomic, retain) NSMutableString *name;
@property (nonatomic, retain) NSString *imutableStr;
@property (nonatomic) int age;

@end

@implementation MyObj
@synthesize name;
@synthesize age;
@synthesize imutableStr;
- (id)init
{
         if (self = [super init])
         {
                   self.name = [[NSMutableString alloc]init];
                   self.imutableStr = [[NSString alloc]init];
                   age = -1;
         }
         return self;
}

- (void)dealloc
{
         [name release];
         [imutableStr release];
         [super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
         MyObj *copy = [[[self class] allocWithZone:zone] init];
         copy->name = [name copy];
         copy->imutableStr = [imutableStr copy];
//       copy->name = [name copyWithZone:zone];;
//       copy->imutableStr = [name copyWithZone:zone];//
         copy->age = age;
         return copy;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
    MyObj *copy = NSCopyObject(self, 0, zone);
    copy->name = [self.name mutableCopy];
    copy->age = age;
    return copy;
}
@end

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.概述 Sentinel(哨崗或哨兵)是Redis的高可用解決方案:由一個或多個Sentinel實例(instance)組成的Sentinel系統(system)可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,併在被監視的主伺服器進入下線狀態時,自動將下線主伺服器屬下的某個從伺服器升 ...
  • ...
  • 下載需要的版本的xtrabackup軟體包,鏈接如下: https://www.percona.com/downloads/XtraBackup/LATEST/ percona-xtrabackup-8.0.4只支持mysql8.0及以上版本備份,如果想備份8.0以下版本MySQL,需要下載perc ...
  • 恢復內容開始 ODI流程 Topology 1、建立 源 物理結構體系 2、建立 目的 物理結構體系 步驟同上 3、建立 源 邏輯架構 4、建立 目的 邏輯架構 步驟同上 Designer 5、建立 源 模型 點擊 6、建立 目的 模型 步驟同上 7、建立項目 8、 導入模塊 路徑為 :F:\Ora ...
  • 1 /*修改欄位類型*/ 2 alter table 表名 ALTER COLUMN 列名 nvarchar(500) 3 go 4 /*增加欄位和說明*/ 5 alter table 表名 add 列名 nvarchar(50) 6 EXECUTE sp_addextendedproperty N... ...
  • map reduce的解釋 這是一張來自mongodb mapreduce圖示,比較能說明問題 其實我們可以從word count這個實例來理解MapReduce。MapReduce大體上分為六個步驟:input, split, map, shuffle, reduce, output。細節描述如下 ...
  • 本文簡述在Android開發中佈局的簡單應用,屬於基礎知識,僅供學習分享使用。 ...
  • android底部增加背景 底部陰影寬度為1,若是左邊右邊上邊需要陰影按照這個方法加上left,right,top就好了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...