This article will focus on the copy operation and the copy keyword, most of the actual code application part, sample code reference -copy.

A copy,

There are two things to know about copying:

  • Why copy?
  • How to copy?

Two, scalar type of copy

In general, the standard copy refers to a simple assignment call, which uses the = operator to assign one variable to another, such as:Copy the code
int a = 5;
int b = a;
Copy the code
Then 'B' gets a copy of 'A'. 'B' and 'A' have different memory addresses. They each occupy different memory regions. But if you try to copy a Core Foundation object this way, you're just copying the reference to the object, and the object itself isn't actually being copied. The copy of scalar types is straightforward, while the copy of object types is much more complex. There are two reasons:Copy the code
  • There are different types of objects, including container-type objects and non-container-type objects.
  • Object types deal with more complex structures, dealing with member variables in objects;

Non-container objects

We'll start with the most common string objects, but for both container and non-container objects, 'copy' returns immutable objects, and 'mutablecopy' returns mutable objects.Copy the code

3.1 nsstrings

Copy of NSString is a shallow copy, and mutableCopy is a deep copy. We can understand it simply:

  • copyIn order to copy an immutable object, andNSStringThe source object itself is immutable. For performance and memory optimization, the system simply adds references and makes shallow copies.
  • mutableCopyIn order to copy a mutable object that can change later, so in order not to affect the sourceNSStringObject, naturally to copy a new object, for deep copy.

For the above understanding, the system and our understanding are actually completely consistent. Take a look at the following code example:

- (void)stringCopy { NSString *str1 = [NSString stringWithFormat:@"123"]; NSString *str2 = [str1 copy]; NSMutableString *str3 = [str1 mutableCopy]; 0xa000000003332313, 0xA000000003332313, 0x1C00524e0 NSLog(@"%p, %p, %p", str1, str2, str3); //-1, -1, 1 NSLog(@"retainCount: %ld, %ld, %ld ", [str1 retainCount], [str2 retainCount], [str3 retainCount]); [str1 release]; [str2 release]; [str3 release]; }Copy the code

The print above was not what was expected:

  • threestrThe pointer of the object is large, and the three objects are allocated in the heap, so there should be a relationship between them.
  • threestrObject reference counts are even more confusing, and according to the above analysis,str1,str2The reference count of2**-1**, and **-1** is a reference count.

Both of these problems are essentially caused by Tagged Pointer counts.

The 'Tagged Pointer' object stores data, i.e., '123', directly to the Pointer. Str1 does not use 'Tagged Pointer'. There is no possibility of change. 'str3' is mutable, which makes it impossible for it to exist in a pointer. Otherwise, it would be more wasteful to check whether the pointer can be stored for each change.Copy the code

Summary of NSSting copy:

  • Non-container class immutable objectcopyIs a shallow copy;
  • Non-container class immutable objectmutableCopyIt’s a deep copy.

3.2 NSMutableString

For the mutable string object NSMutableString, if the source object is str1, two copy operations are performed:

  • copy:str1Is a mutable object, to produce an immutable object, so can only create a new object, that is, a deep copy.
  • mutableCopy:str1It’s a mutable object, and it’s going to create another mutable object, and it’s not going to affect each other, and it’s going to create a new object, which is a deep copy.

Here is a code example:

- (void)mutableStringCopy
{
    NSMutableString *str1 = [NSMutableString stringWithString:@"123"];
    NSString *str2 = [str1 copy];                     //深拷贝
    NSMutableString *str3 = [str1 mutableCopy];       //深拷贝
}
Copy the code

NSMutableString copy summary:

  • Non-container class mutable objectcopy,mutableCopyIt’s all deep copy.

Container class object

In fact, after understanding the nature of non-container object copying, or object copying, above, you can see that just following:

  • Copy purpose: The target object and source object are not affected by each other.
  • Copy method:copyCopy immutable objects,mutableCopyCopy the mutable object.

Then from the storage optimization and performance of the object system, you can understand the matter of copy.

  • Two immutable objects, is it necessary to keep two memory objects?
  • When should Tagged Pointer technology be used to maximize efficiency?

4.1 NSArray

Copy is a shallow copy, and mutableCopy is a deep copy. It depends on the elements inside the container:

- (void)arrayCopy {// Copy returns an immutable object, mutablecopy returns a mutable object // Shallow copy: arr2 is the same object as arr1 with shallow copies of its internal elements // deep copies: Arr3 is a mutable copy of ARR1 that points to different objects, NSArray *arr1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@" A "],@"b",@"c",nil]; NSArray *arr2 = [arr1 copy]; NSMutableArray *arr3 = [arr1 mutableCopy]; // arR [0]: 0x1C00525A0, 0x1C00525A0, 0x1C00525A0, 0x1C00525A0 // shallow copy NSLog(@" arR [0]: %p, %p, %p", arR1 [0], ARR2 [0], arR3 [0]); //arr[0] retainCount: 3, 3, 3 NSLog(@"arr[0] retainCount: %ld, %ld, %ld ", [arr1[0] retainCount], [arr2[0] retainCount], [arr3[0] retainCount]); }Copy the code

It turns out that whatever copy of the elements in the container is a shallow copy.

NSArray copy summary:

  • Copy of an immutable object is a shallow copy, and mutableCopy of an immutable object is a deep copy.
  • The elements in the container are shallow copies.

4.2 NSMutableArray

Following the pattern above, we directly have the following test code:

- (void)mutableArrayCopy {// Copy returns an immutable object, and mutablecopy returns a mutable object. // Deep copy: arr2 is a different object from arr1, and its internal elements are shallow copies. Arr3 is also a different object than ARR1, NSMutableArray * arR1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@" A "],@"b",@" C ", nil]; NSArray *arr2 = [arr1 copy]; NSMutableArray *arr3 = [arr1 mutableCopy]; //arr[0]: 0x1c0052540, 0x1c0052540, 0x1c0052540 NSLog(@"arr[0]: %p, %p, %p", arr1[0], arr2[0], arr3[0]); //arr[0] retainCount: 3, 3, 3 NSLog(@"arr[0] retainCount: %ld, %ld, %ld ", [arr1[0] retainCount], [arr2[0] retainCount], [arr3[0] retainCount]); }Copy the code

NSMutableArray copy summary:

  • MutableCopy, mutableCopy is a deep copy;
  • The elements in the container are shallow copies.

4.3 Deep copies of elements

This seems to be the end of our discussion of container objects, but we have a requirement: how to make a deep copy of the elements in the container. The system provides us with two options:

4.3.1 copyItems

Call the copyItems method of the container class object:

//NSArray
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
//NSDictionray
- (instancetype)initWithDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary copyItems:(BOOL)flag;
Copy the code

As follows:

    BFPerson *person = [[BFPerson alloc] init];
    NSMutableArray *strArr = [NSMutableArray arrayWithObjects:@"a", nil];
    NSArray *arr1 = [NSArray arrayWithObjects:strArr,
                     [NSMutableString stringWithString:@"b"],person,@"c", nil];
    NSArray *arr2 = [[NSArray alloc] initWithArray:arr1 copyItems:YES];
Copy the code

The copyItems value is YES, which means that a copyWithZone: message will be sent to the top level object of the element in the container. For example, a copyWithZone: message will be sent only to strArr above, and the @” A “element inside it will not be sent.

There is a problem with copying container objects whose elements depend heavily on the element’s copyWithZone: implementation. Therefore, it may cause the object type to change after copying, such as NSMutableArr object elements copied into NSArray.

4.3.2 NSCoding protocol

All the elements in the container implement NSCoding protocol, and archive objects through NSKeyedArchiver, and then unarchive objects through NSKeyedUnarchiver, the objects can be successfully copied comprehensively, not only deep copy, but also ensure that the object type will not change.

    BFPerson *person = [[BFPerson alloc] init];
    NSArray *arr1 = [NSArray arrayWithObjects:[NSMutableArray arrayWithObjects:@"a", nil],
                     [NSMutableString stringWithString:@"b"],person, @"c",nil];
    NSArray *arr2 = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:arr1]];
Copy the code

However, this implementation has a disadvantage of high cost and poor performance.

Here is an example of an NSCoding protocol implementation:

@interface BFPerson : NSObject<NSCopying, NSCoding> @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @property (nonatomic, strong) NSMutableArray *data; @implementation BFPerson - (instancetype)initWithCoder:(NSCoder *)aDecoder {if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntegerForKey:@"age"]; self.data = [aDecoder decodeObjectForKey:@"data"]; } return self; } // archive - (void)encodeWithCoder:(NSCoder *)aCoder {[aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.age forKey:@"age"]; [aCoder encodeObject:self.data forKey:@"data"]; } @endCopy the code

5. Copy custom objects

If a user-defined object wants to support copy, you need to implement the NSCopying protocol for the user-defined class. The NSCopying protocol supports copy by the following methods.

@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
Copy the code

In addition, to support mutabCopy, you need to implement the -MutablecopyWithZone: method of NSMutableCopying.

The implementation is as follows:

@implementation BFPerson
- (id)copyWithZone:(NSZone *)zone
{
    BFPerson *person = [[BFPerson alloc] init];
    person.name = self.name;
    person.age = self.age;
    return person;
}
@end
Copy the code

Six, attributes,

As a setter implementation of copy for @property memory management semantics, we mentioned in MEMORY management (3) MRC and ARC:

- (void)setName:(NSString *)name
{
    if (_name != name) {
        [_name release];
        _name = [name copy];
    }
}
Copy the code

One thing to note at this point is the following declaration:

@interface BFPerson : NSObject
@property (nonatomic, copy) NSMutableArray *data;
@end
Copy the code

We declare an NSMutableArray property data and define copy, so that data is an immutable object regardless of whether we pass in a mutable or immutable array. When using, be careful not to crash.

// ViewController - (void)copyProperty { BFPerson *tom = [[BFPerson alloc] init]; tom.name = @"tom"; tom.age = 10; tom.data = [NSMutableArray array]; // crash: -[__NSArray0 addObject:]: unrecognized selector sent to instance 0x1c4010960 //@property (nonatomic, copy) NSMutableArray *data; // Because the BFPerson class defines the copy property [Tom.data addObject: @"a"]; }Copy the code

The solution is to change the copy declaration to strong.

@property (nonatomic, strong) NSMutableArray *data;
Copy the code

reference

link

  1. Collections Programming Topics
  2. NSString retain count -1

The sample code

  1. copy