關於淺複製和深複製的概念,讓我感受有點繞口,以及定義NSString是使用copy仍是使用strong那?花費一天的時間,我對這模塊作了概念理解和代碼驗證(有詳細的分析過程),最後總結了這篇文章。由衆-不知名開發者,原創文章。對內容有疑問可留言交流。ios
- 對如下內容驗證
- 對isa指針簡單釋義一下;
- 淺複製和深複製的概念完全理解;
- 非集合類(NSString)對象進行copy、mutableCopy操做;
- 面試題:爲何定義NSString要使用copy而不建議使用strong,使用strong會有什麼影響❓
- 集合類對象(NSArray)進行copy、mutableCopy操做,單層深複製和真正深複製,集合類內部包含對象是指針複製;
- 面試題:定義NSArray類型的屬性,把修飾詞copy換成strong有什麼影響❓
- 面試題:定義NSMutableArray類型的屬性,修飾詞把strong換成copy有什麼影響❓
現在進入ARC的屎蛋,就無需再次輸入retain
或者release
代碼。下面很形象一示例來釋義什麼是對象的引用計數。git
照明對比引用計數 | 對照明設備所作的動做 | 對oc對象所作的動做 | 引用技數 | oc方法 |
---|---|---|---|---|
第一個進入辦公室的人 | 開燈 | 生成並持有對象 | 0 --- >1 | alloc,new,copy,mutableCopy等方法 |
以後每當有人進入辦公室 | 須要照明 | 持有對象 | 1 --- >2 | retain方法 |
每當有人下班離開辦公室 | 不須要照明 | 釋放對象 | 2 --- > 1 | release方法 |
最後一我的下班離開辦公室 | 關燈 | 廢棄對象 | 1 ----> 0 | dealloc方法 |
總結: 1.在oc的對象中存有引用計數這一整數值。 2.調用alloc
或retain
方法後,引用計數值加1。 3.調用release
後,引用計數值減1。 4.引用計數值爲0時,調用dealloc
方法廢棄對象。 5.固然無論引用計數是否爲0,你也能夠主動在dealloc
方法中廢棄對象。github
說明:
1.如下全部示例代碼打印用的Log宏,打印:內存地址,指針,真實類型,引用計數,值
#define LNLog(description,obj) NSLog(@"%@: 內存地址:%p, 指針地址:%p, 真實類型:%@, 引用計數:%lu, 值:%@", (description),(obj),&(obj),(obj).class,(unsigned long)(obj).retainCount,(obj));
2.打印retainCount
NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge CFTypeRef)obj));//ARC_橋接字方式
NSLog(@"Retain count %@", [obj valueForKey:@"retainCount"]);//ARC_kvc方式
NSLog(@"retain count = %ld",obj.retainCount);//MRC
3.打印對象地址有兩種狀況
對象指針的地址;打印方式:`NSLog(@"%p",&b) - 0x7ffeebf79a78、NSLog(@"%x",&b) - ebf79a78`;
指針指向對象的內存地址(即保存的內容);打印方式:`NSLog(@"%p",b) - 0x60400022b740`;
- (void)testSringOrMutableStringRetainCount
{
// 不可變字符串建立幾種方式
NSString * str1 = @"PublicCoderLN";
LNLog(@"直接複製", str1);
NSString * str2 = [NSString stringWithString:@"PublicCoderLN"];//會有警告
LNLog(@"WithString1", str2);
NSString * str3 = [[NSString alloc] initWithString:@"PublicCoderLN"];
LNLog(@"WithString2", str3);
NSString * str4 = [NSString stringWithFormat:@"PublicCoderLN"];
LNLog(@"WithFormat1", str4);
NSString * str5 = [[NSString alloc] initWithFormat:@"PublicCoderLN"];
LNLog(@"WithFormat2", str5);
/
打印:
直接複製: 內存地址:0x104516290, 指針地址:0x7ffeeb6eba78, 真實類型:__NSCFConstantString, 引用計數:18446744073709551615, 值:PublicCoderLN
WithString1: 內存地址:0x104516290, 指針地址:0x7ffeeb6eba70, 真實類型:__NSCFConstantString, 引用計數:18446744073709551615, 值:PublicCoderLN
WithString2: 內存地址:0x104516290, 指針地址:0x7ffeeb6eba68, 真實類型:__NSCFConstantString, 引用計數:18446744073709551615, 值:PublicCoderLN
WithFormat1: 內存地址:0x604000235dc0, 指針地址:0x7ffeeb6eba60, 真實類型:__NSCFString, 引用計數:1, 值:PublicCoderLN
WithFormat2: 內存地址:0x604000237f00, 指針地址:0x7ffeeb6eba58, 真實類型:__NSCFString, 引用計數:1, 值:PublicCoderLN
*/
NSMutableString * strM1 = [[NSMutableString alloc] initWithString:@"PublicCoderLN"];
LNLog(@"strM1", strM1);
NSMutableString * strM2 = [[NSMutableString alloc] initWithFormat:@"PublicCoderLN"];
LNLog(@"strM2", strM2);
/
打印:
strM1: 內存地址:0x6040004412c0, 指針地址:0x7ffeeb6eba50, 真實類型:__NSCFString, 引用計數:1, 值:PublicCoderLN
strM2: 內存地址:0x6040004413b0, 指針地址:0x7ffeeb6eba48, 真實類型:__NSCFString, 引用計數:1, 值:PublicCoderLN
*/
}
複製代碼
總結:面試
註解:數組
isa:是一個Class 類型的指針。可Xcode(cmd+shift+O)快捷搜索objc.h-Line38 和 runtime.h-Line55,驗證查看。app
- 每個對象本質上都是一個類的實例。其中類定義了成員變量和成員方法的列表。對象經過對象的isa指針指向所屬類。
- 每個類本質上都是一個對象,類實際上是元類(meteClass)的實例。元類定義了類方法的列表。類經過類的isa指針指向元類。
- 元類保存了類方法的列表。當類方法被調用時,先會從自己查找類方法的實現,若是沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類經過isa指針最終指向的是一個根元類(root meteClass)。
- 根元類的isa指針指向自己,這樣造成了一個封閉的內循環。
非集合類對象指的是NSString,NSNumber等對象。 集合類對象指的是NSArray,NSDictionary,NSSet等對象。ide
在Objective-C中,必須遵照 <NSCopying, NSMutableCopying>
,才能經過兩個方法 copy和mutableCopy能夠執行拷貝操做,其中copy是得到一個不可變對象,而mutableCopy是得到一個可變對象。而且兩個方法分別調用copyWithZone和mutableCopyWithZone兩個方法來進行拷貝操做。若是對copy返回對象使用mutable對象接口就會crash,或者強制調用這兩個方法會發生crash,如:對UILabel(繼承UIView,只遵照了NSCopying協議)進行mutableCopy操做,結果會報reason: '-[UILabel mutableCopyWithZone:]: unrecognized selector sent to instance 0x7fbaecf1e880'
。性能
淺複製和深複製學習
四、圖解複製概念 ui
@property (nonatomic, copy) NSString * stringCopy;
@property (nonatomic, strong) NSString * stringStrong;
- (void)testStringUseRetainOrCopyOrMutableCopy
{
NSString * string = [NSString stringWithFormat:@"publicCoderLN"];
LNLog(@"originString", string);
NSLog(@"--------");
// 場景1:定義NSString類型的屬性(copy/strong修飾詞)指向不可變對象.
self.stringCopy = string;
LNLog(@"_stringCopy", _stringCopy);
self.stringStrong = string;
LNLog(@"_stringStrong", _stringStrong);
NSLog(@"--------");
// 場景2:對不可變類型NSString,進行retain、copy、mutableCopy操做
NSString * strRetain1 = [string retain];
LNLog(@"strRetain1", strRetain1);
NSString * strCopy1 = [string copy];
LNLog(@"strCopy1", strCopy1);
NSString * strMCopy1 = [string mutableCopy];
LNLog(@"strMCopy1", strMCopy1);
/
打印:
originString: 內存地址:0x60400003e320, 指針地址:0x7ffee5e6ba78, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringCopy: 內存地址:0x60400003e320, 指針地址:0x7fa7bd609770, 真實類型:__NSCFString, 引用計數:2, 值:publicCoderLN
_stringStrong: 內存地址:0x60400003e320, 指針地址:0x7fa7bd609778, 真實類型:__NSCFString, 引用計數:3, 值:publicCoderLN
--------
strRetain1: 內存地址:0x60400003e320, 指針地址:0x7ffee5e6ba70, 真實類型:__NSCFString, 引用計數:4, 值:publicCoderLN
strCopy1: 內存地址:0x60400003e320, 指針地址:0x7ffee5e6ba68, 真實類型:__NSCFString, 引用計數:5, 值:publicCoderLN
strMCopy1: 內存地址:0x600000251e50, 指針地址:0x7ffee5e6ba60, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
*/
}
複製代碼
分析: 1.從打印結果能夠看出,copy、strong、retain均使對象的retainCount +1; 2.copy不可變對象,只是複製了對象的指針,和原對象string指針地址不一樣(文中其實作了2次copy),但指針指向對象的內存地址仍是同一個,因此是淺複製; 3.mutableCopy不可變對象,即複製了對象的指針,也複製了指針指向對象的內存地址,生成了新的指針和新的內存地址,因此是深複製; 4.提醒:這裏的顯示地址不是不變的,地址是系統分配的,可能你代碼驗證的時候就不是這裏顯示的地址了,但概念是對的;
// 場景3:定義NSString類型的屬性,修飾詞用把copy換成strong後,再修改原對象有什麼影響❓
string = [string substringToIndex:3];
LNLog(@"originString", string);
LNLog(@"_stringCopy", _stringCopy);
LNLog(@"_stringStrong", _stringStrong);
LNLog(@"strRetain1", strRetain1);
LNLog(@"strCopy1", strCopy1);
LNLog(@"strMCopy1", strMCopy1);
/
打印:
originString: 內存地址:0xa000000006275703, 指針地址:0x7ffee5e6ba78, 真實類型:NSTaggedPointerString, 引用計數:18446744073709551615, 值:pub【改原對象】
_stringCopy: 內存地址:0x60400003e320, 指針地址:0x7fa7bd609770, 真實類型:__NSCFString, 引用計數:5, 值:publicCoderLN
_stringStrong: 內存地址:0x60400003e320, 指針地址:0x7fa7bd609778, 真實類型:__NSCFString, 引用計數:5, 值:publicCoderLN
- - -
strRetain1: 內存地址:0x60400003e320, 指針地址:0x7ffee5e6ba70, 真實類型:__NSCFString, 引用計數:5, 值:publicCoderLN
strCopy1: 內存地址:0x60400003e320, 指針地址:0x7ffee5e6ba68, 真實類型:__NSCFString, 引用計數:5, 值:publicCoderLN
strMCopy1: 內存地址:0x600000251e50, 指針地址:0x7ffee5e6ba60, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
分析:
1、原對象string的類型自己就是不可變類型,再對其進行操做,系統自動分配了新的內存地址(只是對象的指針和沒有修改前原對象的指針同樣),這裏對原對象string操做後生成的內存地址和其餘類型(copy/strong/retain)都不同,因此只有原對象string操做後改變。
2、【提醒】:有廠友這時候可能會說,從上面修改前和修改後的打印結果來看,我定義NSString使用copy修飾和使用strong,內存地址和值都是同樣的,那我爲何不使用strong修飾NSString那❓
分析:一些狀況下從性能考慮定義不可變NSString類型的屬性,修飾詞也可使用strong。
copy的set方法內部會對傳入的字符串進行判斷是不可變的仍是可變的,若是是不可變字符串,就直接給屬性進行賦值,不會生成新的內存地址;
若是是可變字符串對string屬性賦值,會進行Copy操做,從新生成一份新的內存地址。
這裏會對copy修飾的string會進行判斷,幾個不會影響性能,可是若是不少就會有了吧。
開發中不少都是定死的NSString不可變字符串的,使用strong也能夠。
// copy的set方法
- (void)setStringCopy:(NSString *)stringCopy
{
_stringCopy = [stringCopy copy];
}
*/
複製代碼
註解: 上面打印出現了NSTaggedPointerString
類型,想更多瞭解的可參考採用Tagged Pointer的字符串。 簡單點說,在字符串小於長度12且爲immutable對象時,Apple會讓其共用同一地址來節省內存的開銷。但中若是出現了中文,則會另外開闢新的內存空間進行存儲。
總結:對於不可變對象
一、retain/strong/copy,都會使原對象的引用計數+1,指針指向對象的內存地址和原對象同樣; 二、copy操做爲淺複製,沒有生成新的內存地址;mutableCopy操做爲深複製,生成了新的內存地址,新生成的對象引用計數爲1; 三、修改原對象string的值,對copy和strong修飾定義的屬性目標對象_stringCopy 沒有影響,仍是原來的值(內存地址和值都是原來的);
@property (nonatomic, copy) NSMutableString * stringMCopy;
@property (nonatomic, strong) NSMutableString * stringMStrong;
- (void)testMStringUseRetainOrCopyOrMutableCopy
{
NSMutableString * stringM = [NSMutableString stringWithFormat:@"%@", @"publicCoderLN"];
LNLog(@"originString", stringM);
NSLog(@"--------");
// 場景1:定義NSString類型的屬性(copy/strong修飾詞)指向可變對象.
self.stringCopy = stringM;
LNLog(@"_stringCopy", _stringCopy);
self.stringStrong = stringM;
LNLog(@"_stringStrong", _stringStrong);
NSLog(@"--------");
// 場景2:對可變類型NSMutableString,進行retain、copy、mutableCopy操做
NSMutableString * stringMRetain = [stringM retain];
LNLog(@"stringMRetain", stringMRetain);
NSMutableString * stringMCopy = [stringM copy];
LNLog(@"stringMCopy", stringMCopy);
NSMutableString * stringMMutableCopy = [stringM mutableCopy];
LNLog(@"stringMMutableCopy", stringMMutableCopy);
/
打印:
originString: 內存地址:0x604000245cd0, 指針地址:0x7ffee1d38a78, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringCopy: 內存地址:0x604000236da0, 指針地址:0x7f94d4603980, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringStrong: 內存地址:0x604000245cd0, 指針地址:0x7f94d4603988, 真實類型:__NSCFString, 引用計數:2, 值:publicCoderLN
--------
stringMRetain: 內存地址:0x604000245cd0, 指針地址:0x7ffee1d38a70, 真實類型:__NSCFString, 引用計數:3, 值:publicCoderLN
stringMCopy: 內存地址:0x60000003b080, 指針地址:0x7ffee1d38a68, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
stringMMutableCopy: 內存地址:0x600000456860, 指針地址:0x7ffee1d38a60, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
--------
分析:
1、定義NSString類型的屬性(copy/strong修飾詞)指向可變對象時,能夠看到copy修飾的生成了新的內存地址。
2、對可變對象stringM進行操做,retain操做引用計數+1,而copy/mutableCopy操做均產生了新指針和新的指針指向的內存地址,都爲深複製,。
*/
NSLog(@"--------");
// 場景4:定義NSString類型的屬性,修飾詞用把copy換成strong後,再修改原對象有什麼影響❓
[stringM appendString:@"+CoderLN"];
LNLog(@"originString", stringM);
LNLog(@"_stringCopy", _stringCopy);
LNLog(@"_stringStrong", _stringStrong);
LNLog(@"stringMRetain", stringMRetain);
LNLog(@"stringMCopy", stringMCopy);
LNLog(@"stringMMutableCopy", stringMMutableCopy);
/
打印:
originString: 內存地址:0x604000245cd0, 指針地址:0x7ffee1d38a78, 真實類型:__NSCFString, 引用計數:3, 值:publicCoderLN+CoderLN【改原對象】
_stringCopy: 內存地址:0x604000236da0, 指針地址:0x7f94d4603980, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringStrong: 內存地址:0x604000245cd0, 指針地址:0x7f94d4603988, 真實類型:__NSCFString, 引用計數:3, 值:publicCoderLN+CoderLN
- - -
stringMRetain: 內存地址:0x604000245cd0, 指針地址:0x7ffee1d38a70, 真實類型:__NSCFString, 引用計數:3, 值:publicCoderLN+CoderLN
stringMCopy: 內存地址:0x60000003b080, 指針地址:0x7ffee1d38a68, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
stringMMutableCopy: 內存地址:0x600000456860, 指針地址:0x7ffee1d38a60, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
分析:
1.上面copy修飾的指向可變對象,生成了新的內存地址,而strong修飾的和原對象stringM的內存地址同樣,因此原對象的修改,strong修飾的也被改變了。
2.對NSMutableString屬性進行copy、mutableCopy操做,均生成了新的內存地址(指向另外一個對象),都爲深複製,因此原對象改變不會對他們有影響;
*/
}
複製代碼
面試題:爲何定義NSString要使用copy而不建議使用strong❓ 分析:將對象聲明爲NSString不可變類型時,都不但願它改變(外界修改了,不影響自身的值),從上面打印結果能夠看出,strong修飾的NSString類型屬性遇到賦值可變類型時,修改原對象的值,_stringStrong也一樣改變了,而copy修飾的string的值沒有改變。 回答:定義NSString使用copy,爲了防止遇到把一個可變字符串在未使用copy方法時賦值給這個字符串對象後,再修改原字符串時(可變字符),本字符串也會被修改的狀況發生(就用strong的狀況)。
總結:對可變對象 一、strong、retain會使原對象的引用計數+1,指針指向的內存地址和原對象仍是同樣的;copy、mutableCopy對原對象的引用計數沒有影響,使新生成的對象的引用計數爲1; 二、進行操做copy、mutableCopy均爲深複製,即複製了對象的指針,也複製了指針指向的內存地址;
@property (nonatomic, copy) NSMutableString * stringMCopy;
@property (nonatomic, strong) NSMutableString * stringMStrong;
- (void)testMutableStringCopyOrStrong
{
NSMutableString *stringM = [[NSMutableString alloc] initWithFormat:@"%@",@"publicCoderLN"];
LNLog(@"stringM", stringM);
self.stringMCopy = stringM;
LNLog(@"_stringMCopy", _stringMCopy);
self.stringMStrong = stringM;
LNLog(@"_stringMStrong", _stringMStrong);
// 面試題:定義NSMutableString類型的屬性,若是把修飾詞strong換成copy有什麼影響❓
//[self.stringMCopy appendString:@"+CoderLN"];// 會crash
//[self.stringMStrong appendString:@"+CoderLN"];// strong修飾的正常運行.
//[[self.stringMCopy mutableCopy] appendString:@"+CoderLN"];// 返回的可變類型.
/
打印:
stringM: 內存地址:0x60400025f2f0, 指針地址:0x7ffee4502a78, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringMCopy: 內存地址:0x60000022bda0, 指針地址:0x7f7f90525de0, 真實類型:__NSCFString, 引用計數:1, 值:publicCoderLN
_stringMStrong: 內存地址:0x60400025f2f0, 指針地址:0x7f7f90525de8, 真實類型:__NSCFString, 引用計數:2, 值:publicCoderLN
總結:
1.定義NSMutableString使用copy,copy修飾後的對象生成了新的內存地址,爲深複製。對新生成的對象進行appendString:操做會發生crash,說明其實copy修飾後返回的是不可變類型。
2.定義NSMutableString使用strong,strong修飾後對象的內存地址和原對象的內存地址相同,爲淺複製,能夠進行appendString:操做,說明其實strong修飾後返回的仍是是可變類型。
3.回答:定義NSMutableString使用修飾詞strong換成copy,若是對copy後的不可變類型,再進行可變的修改操做,就會形成崩潰。
*/
}
複製代碼
@property (nonatomic ,copy) NSArray *arrayCopy;
@property (nonatomic ,strong) NSArray *arrayStrong;
- (void)testArrayUseCopyOrMutableCopy
{
NSArray *array = @[@"A", @"B"];
LNLog(@"originAry", array);
NSLog(@"--------");
// 場景1:定義NSArray類型的屬性(copy|strong修飾)指向不可變對象
self.arrayCopy = array;
LNLog(@"_arrayCopy", _arrayCopy);
self.arrayStrong = array;
LNLog(@"_arrayStrong", _arrayStrong);
NSLog(@"--------");
// 場景2:對不可變類型數組,進行copy|mutableCopy操做
NSArray * arrayCopy = [array copy];// 淺
LNLog(@"arrayCopy", arrayCopy);
NSMutableArray * arrayMCopy = [array mutableCopy];// 深
LNLog(@"arrayMCopy", arrayMCopy);
NSLog(@"--------");
// 場景3:對數組不可變類型,進行淺深複製
NSArray * arrayCopy1 = [array copyWithZone:nil];// 淺
LNLog(@"arrayCopy1", arrayCopy1);
NSArray * arrayCopy2 = [[NSArray alloc] initWithArray:array copyItems:NO];// 淺
LNLog(@"arrayCopy2", arrayCopy2);
NSArray * arrayCopy3 = [[NSArray alloc] initWithArray:array copyItems:YES];// 單層深複製
LNLog(@"arrayCopy3", arrayCopy3);
// 真正的深複製:先歸檔再解檔
NSArray * arrayCopy4 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array]];
LNLog(@"arrayCopy4", arrayCopy4);
NSLog(@"--------");
// 場景4:驗證數組中的元素均爲指針複製
NSLog(@"%p",array[0]);
NSLog(@"%p",arrayCopy[0]);
NSLog(@"%p",arrayMCopy[0]);
NSLog(@"--------");
// 面試題:定義NSArray類型的屬性,把修飾詞copy換成strong有什麼影響❓
// 指向不可變對象,外界修改原對象array
array = [array arrayByAddingObject:@"Public-CoderLN"];
LNLog(@"originArray", array);
LNLog(@"_arrayCopy", _arrayCopy);
LNLog(@"_arrayStrong", _arrayStrong);
NSLog(@"--------");
// 場景6:定義NSArray類型的屬性(copy|strong修飾詞)指向可變對象
NSMutableArray * arrayM = [[NSMutableArray alloc] initWithArray:@[@"a",@"b"]];
self.arrayCopy = arrayM;
self.arrayStrong = arrayM;
// 指向可變對象,外界修改原對象arrayM
[arrayM removeAllObjects];
LNLog(@"originArrayM", arrayM);
LNLog(@"_arrayCopy", _arrayCopy);
LNLog(@"_arrayStrong", _arrayStrong);
}
複製代碼
/
打印:
originAry: 內存地址:0x604000037f00, 指針地址:0x7ffee8599a50, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B)
--------
場景1:
_arrayCopy: 內存地址:0x604000037f00, 指針地址:0x7fa41771edf0, 真實類型:__NSArrayI, 引用計數:2, 值:(A,B)
_arrayStrong: 內存地址:0x604000037f00, 指針地址:0x7fa41771edf8, 真實類型:__NSArrayI, 引用計數:3, 值:(A,B)
分析:定義NSArray類型的屬性,指向不可變類型,copy和strong修飾均沒有生成新的內存地址,只是複製了對象的指針且引用計數+1,copy修飾爲淺複製;
--------
場景2:
arrayCopy: 內存地址:0x604000037f00, 指針地址:0x7ffee8599a48, 真實類型:__NSArrayI, 引用計數:4, 值:(A,B)
rrayMCopy: 內存地址:0x60000044c090, 指針地址:0x7ffee8599a40, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
分析:對原對象array進行Copy操做,沒有生成新的內存地址且引用計數+1,爲淺複製;進行mutableCopy操做,生成了新的內存地址,因此爲深複製。
--------
場景3:
arrayCopy1: 內存地址:0x604000037f00, 指針地址:0x7ffee8599a38, 真實類型:__NSArrayI, 引用計數:5, 值:(A,B)
arrayCopy2: 內存地址:0x604000037f00, 指針地址:0x7ffee8599a30, 真實類型:__NSArrayI, 引用計數:6, 值:(A,B)
arrayCopy3: 內存地址:0x6000004200c0, 指針地址:0x7ffee8599a28, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B)
arrayCopy4: 內存地址:0x604000038060, 指針地址:0x7ffee8599a20, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B)
分析:對原對象array進行 copyWithZone:和initWithArray:copyItems:參數爲NO 沒有生成新的內存地址,都爲淺複製,
對原對象array進行 initWithArray:copyItems:參數爲YES 和 先歸檔再解檔操做,均生成了新的內存地址,都爲深複製,
--------
場景4:
0x10766a230// 原數組array[0]
0x10766a230// arrayCopy[0]
0x10766a230// arrayMCopy[0]
分析:打印數組中的元素內存地址都是同一個,不論是對原數組作copy/mutableCopy操做,數組中的元素均爲淺複製。
--------
【面試題】:定義NSArray類型的屬性,把修飾詞copy換成strong有什麼影響❓
//指向不可變對象,外界修改原對象array
originArray: 內存地址:0x60000044c060, 指針地址:0x7ffee8599a50, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B,"Public-CoderLN")【改原對象】
_arrayCopy: 內存地址:0x604000037f00, 指針地址:0x7fa41771edf0, 真實類型:__NSArrayI, 引用計數:6, 值:(A,B)
_arrayStrong: 內存地址:0x604000037f00, 指針地址:0x7f9395d0a6f8, 真實類型:__NSArrayI, 引用計數:6, 值:(A,B)
--------
//指向可變對象,外界修改原對象arrayM
originArrayM: 內存地址:0x60000044c1b0, 指針地址:0x7ffeea6c7a18, 真實類型:__NSArrayM, 引用計數:2, 值:( )
_arrayCopy: 內存地址:0x600000420160, 指針地址:0x7fa41771edf0, 真實類型:__NSArrayI, 引用計數:1, 值:(a,b)
_arrayStrong: 內存地址:0x60000044c1b0, 指針地址:0x7fa41771edf8, 真實類型:__NSArrayM, 引用計數:2, 值:( )
*/
複製代碼
面試題:定義NSArray類型的屬性,把修飾詞copy換成strong有什麼影響❓ 分析: 一、指向不可變對象,外界修改原對象array,這裏原對象自己爲不可變對象,對它進行修改,系統又從新分配了內存地址與_arrayCopy的內存地址不同,因此只有原對象改變。 二、指向可變對象,外界修改原對象arrayM,這裏原對象自己爲可變對象,對它進行修改,因爲copy修飾作了深複製生成了新的內存地址,而strong修飾內存地址和原對象arrayM相同,因此原對象改變,strong修飾的也跟着改變了。 回答:定義NSArray使用copy,爲了防止遇到把一個可變數組在未使用copy方法時賦值給這個對象後,再修改原數組時,這個對象也會被修改的狀況發生(就如strong的狀況會被修改)。
@property (nonatomic ,copy) NSMutableArray *arrayMCopy;
@property (nonatomic ,strong) NSMutableArray *arrayMStrong;
- (void)testMutableArrayUseCopyOrMutableCopy
{
NSMutableArray * arrayM = [NSMutableArray arrayWithArray:@[@"A",@"B"]];
LNLog(@"originAry", arrayM);
// 場景1:給定義NSMutableArray類型的屬性(copy/strong修飾詞)賦值可變對象
self.arrayMCopy = arrayM;
LNLog(@"_arrayCopy", _arrayCopy);
self.arrayMStrong = arrayM;
LNLog(@"_arrayMStrong", _arrayMStrong);
NSLog(@"--------");
// 場景2:對可變類型數組,進行copy、mutableCopy操做
NSArray * arrayC1 = [arrayM copy];
LNLog(@"arrayC1", arrayC1);
NSArray * arrayC2 = [arrayM mutableCopy];
LNLog(@"arrayC2", arrayC2);
NSMutableArray * arrayM1 = [arrayM copy];
LNLog(@"arrayM1", arrayM1);
NSMutableArray * arrayM2 = [arrayM mutableCopy];
LNLog(@"arrayM2", arrayM2);
NSLog(@"--------");
// 場景3:NSCopying和NSMutableCopying
NSMutableArray * shallowCopyArrayM = [arrayM mutableCopyWithZone:nil];// 淺複製
LNLog(@"shallowCopyArrayM", shallowCopyArrayM);
NSMutableArray * deepCopyArrayM1 = [[NSMutableArray alloc] initWithArray:arrayM copyItems:NO];
LNLog(@"deepCopyArrayM1", deepCopyArrayM1);
NSMutableArray * deepCopyArrayM2 = [[NSMutableArray alloc] initWithArray:arrayM copyItems:YES];
LNLog(@"deepCopyArrayM2", deepCopyArrayM2);
// 面試題:定義NSMutableArray類型的屬性,修飾詞把strong換成copy有什麼影響❓
//[self.arrayMCopy removeLastObject];// 會crash
[self.arrayMStrong removeLastObject];// 正常運行
}
/
打印:
originAry: 內存地址:0x604000253740, 指針地址:0x7ffee591da60, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
_arrayCopy: 內存地址:0x0, 指針地址:0x7fa029f2b400, 真實類型:(null), 引用計數:0, 值:(null)
_arrayMStrong: 內存地址:0x604000253740, 指針地址:0x7fa029f2b418, 真實類型:__NSArrayM, 引用計數:2, 值:(A,B)
--------
arrayC1: 內存地址:0x60000022d780, 指針地址:0x7ffee591da58, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B)
arrayC2: 內存地址:0x6000004573d0, 指針地址:0x7ffee591da50, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
arrayM1: 內存地址:0x60000022d680, 指針地址:0x7ffee591da48, 真實類型:__NSArrayI, 引用計數:1, 值:(A,B)
arrayM2: 內存地址:0x600000457520, 指針地址:0x7ffee591da40, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
--------
shallowCopyArrayM: 內存地址:0x604000253a70, 指針地址:0x7ffee591da38, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
deepCopyArrayM1: 內存地址:0x604000253d10, 指針地址:0x7ffee591da30, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
deepCopyArrayM2: 內存地址:0x600000457430, 指針地址:0x7ffee591da28, 真實類型:__NSArrayM, 引用計數:1, 值:(A,B)
分析:
1.對NSMutableArray類型的屬性,copy和mutableCopy操做都是深複製。
*/
複製代碼
面試題:定義NSMutableArray類型的屬性,修飾詞把strong換成copy有什麼影響❓ 回答:定義NSMutableArray類型的屬性,修飾詞用把strong換成copy,遇到可變對象賦值,再對_arrayMCopy作可變操做會崩潰,由於copy後返回的是不可變類型。reason: '-[__NSArrayI removeLastObject]: unrecognized selector sent to instance 0x600000426f00'
總體總結一下:不論是非集合類仍是集合類
(指向不可變對象爲淺複製;指向可變對象時生成了新的內存地址,爲深複製)
,使用mutableCopy是深複製。