- Deep copy and shallow copy
- Introduction and usage of copy and mutableCopy.
- Why is copy used to modify blocks?
- When declaring NSArray or NSMutableArray variables, which one is better decorated with copy?
- conclusion
One, depth copy
- What is shallow copy and what is deep copy
- Deep copy: the address of the copied object is not the same as that of the original object. Modification of the original object has no effect
- Shallow copy: the pointer address of the copied object, the object is the same object, and both are modified
- For container objects such as NSArray, NSDictionary, NSSet, etc., deep copies can be divided into “incomplete deep copy” and “full deep copy”. You know…).
- Incomplete deep copy: The copied container is a new object, but the objects inside the container are still the original objects.
- Full deep copy: The copied container is the new object, and the objects inside the container are also the new objects.
Introduction to copy and mutableCopy
copy
Returns the object returned by copyWithZone:. // Copy actually calls copyWithZone:Copy the code
CopyWithZone: Returns a new instance that's a copy of the receiver. // Returns a new instance that is a copy of the receiver. Discussion The returned object is implicitly retained by the sender, Who is responsible for releasing it. The copy returned is immutable if The consideration "immutable vs. mutable" applies to the receiving object; Otherwise the exact nature of the copy is determined by the class. // The returned object is implicitly reserved by the sender and the sender is responsible for releasing it. If the consideration "immutable" and "mutable" apply to the received object, the copy returned is immutable; Otherwise, the exact nature of the copy is determined by the class.Copy the code
mutableCopy
Returns the object returned by mutableCopyWithZone: Where the zone is nil. MutableCopyWithZone: Returns a new instance that's a mutable copy of the receiver. Required. MutableCopyWithZone: Returns a new instance that's a mutable copy of the receiver. Returns a new instance that is a mutable copy of the sink.Copy the code
For example the use of
1 copy and mutableCopy of NSString and NSMutableString
// immutable string NSString * STR = @" string "; id str_copy = [str copy]; id str_mutableCopy = [str mutableCopy]; NSMutableString *mStr = [[NSMutableString alloc] initWithString:@" mutable String "]; id mStr_copy = [mStr copy]; id mStr_mutableCopy = [mStr mutableCopy]; NSLog(@"str = %@ str-p = %p", str, str); NSLog(@"str_copy = %@ str_copy-p = %p", str_copy, str_copy); NSLog(@"str_mutableCopy = %@ str_mutableCopy-p = %p", str_mutableCopy, str_mutableCopy); NSLog(@"--------"); NSLog(@"str = %@ str-p = %p", str, str); NSLog(@"Str_copy = %@ mStr_copy-p = %p", mStr_copy, mStr_copy); NSLog(@"Str_mutableCopy = %@ mStr_mutableCopy-p = %p", mStr_mutableCopy, mStr_mutableCopy);Copy the code
So if you think about it over here, what is the result?
// immutable string NSString * STR = @" string "; id str_copy = [str copy]; id str_mutableCopy = [str mutableCopy]; NSMutableString *mStr = [[NSMutableString alloc] initWithString:@" mutable String "]; id mStr_copy = [mStr copy]; id mStr_mutableCopy = [mStr mutableCopy]; NSLog(@"str = %@ str-p = %p", str, str); NSLog(@"str_copy = %@ str_copy-p = %p", str_copy, str_copy); NSLog(@"str_mutableCopy = %@ str_mutableCopy-p = %p", str_mutableCopy, str_mutableCopy); NSLog(@"--------"); NSLog(@"str = %@ str-p = %p", str, str); NSLog(@"Str_copy = %@ mStr_copy-p = %p", mStr_copy, mStr_copy); NSLog(@"Str_mutableCopy = %@ mStr_mutableCopy-p = %p", mStr_mutableCopy, mStr_mutableCopy); /** Result STR = string STR -p = 0x104CD0040 str_copy = string str_copy-p = 0x104CD0040 str_mutableCopy = string str_mutableCopy-p = 0x600001149ec0 STR = Change STR -p = 0x104CD0040 Str_copy = variable string mStr_copy-p = 0x600001149dd0 Str_mutableCopy = Variable string mStr_mutableCopy-p = 0x600001149f20 */Copy the code
Copy and mutableCopy rules for immutable and mutable strings: NSString: string
NSString | NSMutableString | |
---|---|---|
copy | Shallow copy | Deep copy |
mutableCopy | Deep copy | Deep copy |
If STR copy changes STR’s value, does str_copy change at the same time?
At first glance, it should be changing together, right? I think so too, but still with the facts, right? Go on…
// immutable string NSString * STR = @" string "; id str_copy = [str copy]; id str_mutableCopy = [str mutableCopy]; STR = @" change "; NSLog(@"str = %@ str-p = %p", str, str); NSLog(@"str_copy = %@ str_copy-p = %p", str_copy, str_copy); NSLog(@"str_mutableCopy = %@ str_mutableCopy-p = %p", str_mutableCopy, str_mutableCopy); /** Result STR = change STR -p = 0x104CD0040 str_copy = string str_copy-p = 0x104CD0020 str_mutableCopy = string str_mutableCopy-p = 0x600001149ec0 */Copy the code
Id str_copy = [STR copy]; Different Pointers to the same memory space, str_copy is reassigned to two different memory Spaces.
The copy object and the source object do not affect each other
- Modified source objects (properties and behavior) without affecting replica objects.
- The properties and behavior of the replica object are modified without affecting the source object.
So when str_copy = STR, both strings are immutable, pointing to @” string “in the same memory space, it is impossible to change to @” morphing”. Therefore, in order to optimize performance, the system does not need to provide additional memory, just generate another pointer to the same memory space. But when you assign a new value to STR or str_copy, the system will create a new area of memory, because the previous content is immutable and does not affect each other.
so ? Got it….
- Container type:
NSArray, NSMutableArray, NSDictionary, NSMutableDictionary NSSet, NSMutableSet
的copy
和mutableCopy
Take arrays: go straight to the code
NSString * str1 = @"str1"; NSString * str2 = @"str2"; NSArray *arr = [NSArray arrayWithObjects:str1, str2, nil]; id arr_copy = [arr copy]; id arr_mutableCopy = [arr mutableCopy]; NSMutableArray *mArr = [NSMutableArray arrayWithObjects:str1, str2, nil]; id mArr_copy = [mArr copy]; id mArr_mutableCopy = [mArr mutableCopy]; NSLog(@"\narr = %@ arr-p = %p", arr, arr); NSLog(@"\narr_copy = %@ arr_copy-p = %p", arr_copy, arr_copy); NSLog(@"\narr_mutableCopy = %@ arr_mutableCopy-p = %p", arr_mutableCopy, arr_mutableCopy); NSLog(@"--------"); NSLog(@"\nmArr = %@ mArr-p = %p", mArr, mArr); NSLog(@"\nmArr_copy = %@ mArr_copy-p = %p", mArr_copy, mArr_copy); NSLog(@"\nmArr_mutableCopy = %@ mArr_mutableCopy-p = %p", mArr_mutableCopy, mArr_mutableCopy); Arr = (str1, str2) arr-p = 0x600000AC1320 Arr_copy = (str1, str2) arr-p = 0x600000AC1320 str2 ) arr_copy-p = 0x600000ac1320 arr_mutableCopy = ( str1, str2 ) arr_mutableCopy-p = 0x600000414780 mArr = ( str1, str2 ) mArr-p = 0x600000414750 mArr_copy = ( str1, str2 ) mArr_copy-p = 0x600000ac1300 mArr_mutableCopy = ( str1, str2 ) mArr_mutableCopy-p = 0x600000414690 */Copy the code
Copy and mutableCopy rules for immutable and mutable arrays:
NSArray | NSMutableArray | |
---|---|---|
copy | Shallow copy | Deep copy |
mutableCopy | Deep copy | Deep copy |
In short, copy is usually a shallow copy, but in some cases it is a deep copy.
2.1 What is the result of copying a mutable array?
NSMutableArray *mArr = [[NSArray arrayWithObjects:@"1", @"2", nil] mutableCopy]; NSMutableArray *mArr_copy = [mArr copy]; NSLog(@"\n mArr = %@ mArr-p = %p mArr class = %@", mArr, mArr, [mArr class]); NSLog(@"\n mArr_copy = %@ mArr_copy-p = %p mArr_copy class = %@", mArr_copy, mArr_copy, [mArr_copy class]); /* Print result mArr = (1, 2) mArr-p = 0x600002AD4db0 mArr class = __NSArrayM mArr_copy = (1, 2 ) mArr_copy-p = 0x600002432d40 mArr_copy class = __NSArrayI */Copy the code
Copy does not copy Pointers. The address should be the same, but what happened? Memory addresses are different, and mArr_copy is immutable.
mArr_copy
Is through thecopy
I got it. The point iscopy
And the result of themArr
No, so he’s immutable.mArr
The memory space to point to is variable if themArr
Make changes and the contents of the same memory space will change. Follow the principle that meeting does not affectmArr_copy
Is immutable,mArr
It’s mutable,mArr
The memory space is not suitable, so at this timecopy
Memory space needs to be reopened.
2.2 Directly Copy Objects
NSArray *arr = @[@"123", @"456", @"asd"]; self.mArr = arr; // self.mArr = [arr copy]; NSLog(@"\n arrP = %p \n self.mArrP = %p, self.mArr class = %@", arr, self.mArr, [self.mArr class]); /* Result arrP = 0x6000005ee550 self.mArrP = 0x6000005ee550, self.mArr class = __NSArrayI */Copy the code
When assigning or copying a pointer directly, since it is an immutable array, the pointer can be copied directly without changing the memory address.
NSMutableArray @property (nonatomic, copy) NSMutableArray *mArr; , what happens when you assign to mArr?
NSArray *arr = @[@"1", @"2", @"3"]; self.mArr = [arr mutableCopy]; NSLog(@"\n arrP = %p \n self.mArrP = %p, self.mArr class = %@", arr, self.mArr, [self.mArr class]); ArrP = 0x600002588bd0 self.mArrP = 0x600002588ba0 self.mArr class = __NSArrayI */Copy the code
You can see that the memory address is different, but _mArr is an immutable array. Because _mArr is declared with copy, it limits it to an immutable array. It’s going to use mutableCopy, which is a mutable array copy method, so it’s going to reallocate memory.
And then analyze it
NSMutableArray * a = [NSMutableArray array].copy;
NSMutableArray * b = [NSMutableArray array].mutableCopy;
NSLog(@"\n a = %p \n a class = %@", a, [a class]);
NSLog(@"\n b = %p \n b class = %@", b, [b class]);
/*
a = 0x7fff8004b160
a class = __NSArray0
b = 0x6000030dae50
b class = __NSArrayM
*/
Copy the code
copy
: just put theNSMutableArray
To perform thecopy
Operation, buta
Now it’s an immutable array,mutableCopy
:b
Is a mutable array
OC is a dynamic language, and you only know what type of object it is at the time of execution. If you are lazy, you can avoid such a Crash.
- (NSMutableArray *)mArr { if (! _mArr) { _mArr = [NSMutableArray array]; } return _mArr; }Copy the code
We can see that the instance variable used in mArr is generated by the system for you. There is no copy attribute like the attribute, so it will not execute the copy operation directly and change the variable array into an immutable variable group. The nature of the instance variable is also variable.
Retain and strong self.mArr = [NSMutableArray array]; The object generated by this method is also a mutable array. Retain reference count +1; Strong refers to a strong reference
Three, why usecopy
modifiedblock
Simply put, a block is like a function pointer to the function we want to use.
Just like function calls, it doesn’t matter where you write the block, as long as you put it in memory (by calling the method or function that contains the block), whether it’s on the stack, in the heap, or in a static section. As long as it’s not destroyed, you can call it from your declared block.
To explain why we use copy to declare a block in a class, we need to start with the three types of blocks.
NSConcreteGlobalBlock Specifies a static block that does not access external variables. That is, if your block doesn’t call any other external variables, then your block type is this. For example, you just write an NSLog(” Hello world”) in your block;
NSConcreteStackBlock Holds blocks in the stack that are destroyed when the function returns. This block is the one where you declare it without copy, and your block accesses external variables.
NSConcreteMallocBlock A block stored in the heap that is destroyed when the reference count reaches zero. Ok, so that’s the hero of the day, block with copy.
As we know, the declaration cycle of a function ends with the end of a function call. Our block is written inside the function. If it is a global static block, it will not be released until the end of the program. But we basically don’t use blocks that don’t access external variables.
If it is a block on the stack, it is destroyed at the end of the function call. As a result, we can no longer access a block after executing a function that contains it. Null-pointer exceptions occur when we use a block again, because the stack is destroyed and the block inside the function is gone.
If it’s a block in the heap, it’s a copy modified block. His life cycle ends with the destruction of the object. As long as the object is not destroyed, we can call a block in the heap.
That’s why we use copy to decorate blocks. Blocks that access external variables without copy are available only at the moment their function is called. And then it disappeared.
conclusion
- with
copy
A modified or assigned variable must be immutable. - with
copy
Assigns, depending on whether the source object is mutable, to copy only Pointers or also objects to another memory space - Between objects
mutableCopy
Assignment is bound to copy the entire object memory into another block of memory - After the assignment of values between objects, and then change, follow the principle of mutual influence