Copy means copy, just like we usually copy a file to generate another file, and the two files do not interfere with each other. In oc, copy means to copy an object

The characteristics of the copy

  • Modifying the properties and behavior of the source object does not affect the replica object
  • Modifying the properties and behavior of the replica object does not affect the source object

Copy and mutableCopy of NSString, NSArray, and NSDictionary in oc

  • Create immutable copies from copy (NSString,NSArray, etc.)
  • Create a copy of the variable through mutableCopy (NSMutableString, NSMutableArray, etc.)
  • An object can use copy only if it complies with the NSCopying protocol and implements the copyWithZone: method

Copy and mutableCopy in NSString

  • An immutable string calls the copy method
NSString *str = @"123";
NSString *newStr = [str copy];
NSLog(@"str = %p, newStr = %p", str, newStr);
Copy the code
[15171:1881395] STR = 0x100004018, newStr = 0x100004018**Copy the code
  • An immutable string calls the mutableCopy method
NSString *str = @"123";
NSMutableString *newStr = [str mutableCopy];
NSLog(@"str = %p, newStr = %p", str, newStr);
Copy the code
[1535:1883896] STR = 0x100004018, newStr = 0x104804b30**Copy the code
  • Copy is called for mutable strings
NSMutableString *mutableStr = [[NSMutableString alloc]initWithString:@"123"];
NSString *newMutableStr = [mutableStr copy];
NSLog(@"mutableStr = %p, newMutableStr = %p", mutableStr, newMutableStr);
Copy the code
[15480:1887745] mutableStr = 0x1005480A0, newMutableStr = 0xd509abef3c7c50c5**Copy the code
  • Mutable string calls the mutableCopy method
NSMutableString *mutableStr = [[NSMutableString alloc]initWithString:@"123"];
NSMutableString *newMutableStr = [mutableStr mutableCopy];
NSLog(@"mutableStr = %p, newMutableStr = %p", mutableStr, newMutableStr);
Copy the code
[15534:1888793] mutableStr = 0x100598980, newMutableStr = 0x1005989D0 **Copy the code

You can see that copy only generates no new objects when an immutable string is copied, and all three cases generate new objects. Which is consistent with the characteristics of the copy, immutable copy of the string for the copy operation when the product is immutable, both are immutable, so don’t generate a new object will not cause the source object and a copy of the object interact with each other, the other three scenarios are mutable object, so you have to generate a new object, so as to each other

Copy and mutableCopy in NSArray

Copy for NSArray is actually the same as copy for NSString, only copy for immutable arrays will not generate new objects, but all three cases will generate new operations

NSArray *arr = @[@1,@2,@3];

NSArray *newArr = [arr copy];
NSLog(@"arr = %p, newArr = %p", arr, newArr);

NSMutableArray *newMutableArr = [arr mutableCopy];
NSLog(@"arr = %p, newMutableArr = %p", arr, newMutableArr);

NSMutableArray *mutableArr = [NSMutableArray arrayWithObject:@1];

NSArray *newArr1 = [mutableArr copy];
NSLog(@"mutableArr = %p, newArr1 = %p", mutableArr, newArr1);

NSArray *newMutableArr1 = [mutableArr mutableCopy];
NSLog(@"mutableArr = %p, newMutableArr1 = %p", mutableArr, newMutableArr1);

Copy the code
**2021-11-10 19:34:06.700294+0800 Copy [15884:1893935] arr = 0x1000080a8, NewArr = 0x1000080a8** **2021-11-10 19:34:06.700918+0800 Basic use of copy [15884:1893935] arr = 0x1000080a8, NewMutableArr = 0x100744d80** **2021-11-10 19:34:06.701005+0800 Copy basic usage [15884:1893935] mutableArr = 0x100744c90, NewArr1 = 0x100746390** **2021-11-10 19:34:06.701051+0800 Copy basic usage [15884:1893935] mutableArr = 0x100744c90, newMutableArr1 = 0x100746d00**Copy the code

Copy and mutableCopy in NSDictionary

Copy in NSDictionary is the same as NSString, so I won’t give you any more examples here

From the above example we can draw two concepts:

Deep copy and shallow copy

Deep copy: copies the object itself and keeps another object in memory. Shallow copy: does not copy the object itself, but only copies Pointers to the object. A retain operation is performed on the object in MRC

From this figure, you can see that only [NSArray copy], [NSDictionary Copy], and [NSString copy] are shallow copies. The other operations are deep copies.

Copy in property

So let’s think about a question here, is it better for property NSString to be strong or copy?

Let’s start with an example:

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end

NSMutableString *str = [[NSMutableString alloc]initWithString:@"MK"];
Person *person = [[Person alloc]init];
person.name = str;
[str appendString:@" cool"];
NSLog(@"person.name = %@", person.name);
Copy the code
636787+0800 copy basic use [16675:1905943] person.name = MK coolCopy the code

Above we define a Person class with a strong-modified name attribute. In main we assign a mutable string to the name variable in the Person instance. When the mutable STR string appends the string, we notice that the name property in person is also changed. Because the strong modifier is distinct from the RETAIN modifier in MRC, which simply increments the reference count by one, the strong modifier actually adds a pointer to the MK string. This problem does not occur if you change the strong modifier to copy, because copying mutable strings also generates a new object, which is identical to a deep copy.

As follows:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end

NSMutableString *str = [[NSMutableString alloc]initWithString:@"MK"];
Person *person = [[Person alloc]init];
person.name = str;
[str appendString:@" cool"];
NSLog(@"person.name = %@", person.name);
Copy the code
636787+0800 copy basic use [16675:1905943] person.name = MK coolCopy the code

Is a mutable string (NSMutableString) strong or copy? Let’s use copy

@interface Person : NSObject

@property (nonatomic, copy) NSMutableString *mutableStr;

@end

NSMutableString *str = [[NSMutableString alloc]initWithString:@"MK"];
Person *person = [[Person alloc]init];
person.mutableStr = str;

[person.mutableStr appendString:@" 123"];

Copy the code
[17899:1930293] -[NSTaggedPointerString appendString:]: [NSTaggedPointerString appendString:] unrecognized selector sent to instance 0x21a639271ae3d24f**Copy the code

When we append a character from a mutable string in an instance, it crashes, and that’s because copying a mutable string produces an immutable string, and in fact mutableStr in person is immutable, and it crashes when appending a character, so it’s better to use strong, It will not crash, and using strong will have the same problem as NSString, so we have to avoid it ourselves.

You can do it this way, just mutableCopy is a string that comes out and assigns it to the Person’s mutableStr.

person.mutableStr = [str mutableCopy];
Copy the code

NSArray and NSDictonary have the same problem

Conclusion:

  • Immutable NSString, NSArray, and NSDictonary use copy
  • Mutable NSMutableString, NSMutableArray, and NSMutableDictonary are better with strong

Use of copy in blocks

Block property modifier in MRC

typedef void(^MyBlock)(void);
@interface Person : NSObject
@property (nonatomic, retain) MyBlock myBlock;
@end

Person *person = [[Person alloc]init];
Dog *dog = [[Dog alloc]init];
NSLog(@"retainCount = %ld",[dog retainCount]);
person.myBlock = ^{
    NSLog(@"retainCount = %ld",[dog retainCount]);
    NSLog(@"dog = %@",dog);
};
NSLog(@"retainCount = %ld",[dog retainCount]);
[dog release];
person.myBlock();
[person release];

Copy the code
**2021-11-11 08:57:57.991547+0800 Copy basic usage [26038:2068864] retainCount = 1** **2021-11-11 08:57:57.992204+0800 Basic Use of Copy [26038:2068864] retainCount = 1** **2021-11-11 08:58:37.926729+0800 Basic use of copy [26038:2068864] -[Dog dealloc]** **2021-11-11 08:58:38.567806+0800 Copy basic usage [26038:2068864] *** -[Dog retainCount]: message sent to deallocated instance 0x100712db0**Copy the code

When the dog object is freed before the block is used, the program crashes when the block is called again, using the freed object.

You can see that the block is on the stack. Let’s change the modifier to copy

typedef void(^MyBlock)(void);
@interface Person : NSObject
@property (nonatomic, copy) MyBlock myBlock;
@end

Copy the code

The block is stored in the heap and will not crash if it is called again

**2021-11-11 09:08:38.115245+0800 Copy basic usage [26435:2076149] retainCount = 1** **2021-11-11 09:08:38.116298+0800 [26435:2076149] retainCount = 2** **2021-11-11 09:09:19.682681+0800 Copy [26435:2076149] retainCount = 1** **2021-11-11 09:09:22.305131+0800 copy basic usage [26435:2076149] dog = < dog: 0x10050F0A0 >** **2021-11-11 09:09:24.768995+0800 Basic use of Copy [26435:2076149] -[Person dealloc]**Copy the code

The dog object was not released because the block increased its reference count by 1. We just need to release the block when the Person object is released. When the block is released, it will send a release message to all the objects referenced by the block.

@implementation Person
- (void)dealloc {
    NSLog(@"%s", __func__ );
    Block_release(_myBlock);
    [super dealloc];
}
@end
Copy the code

It can be concluded that blocks on the stack do not increment the reference count of used objects by one. A block in the heap increments the reference to the object being used. This prevents the object from being freed prematurely and resulting in a crash before the block is called. Therefore, MRC blocks are best modified with copy

ARC block property modifier

In ARC, do we use strong blocks or copy blocks?

First modify with strong:

typedef void(^MyBlock)(void);
@interface Person : NSObject
@property (nonatomic, strong) MyBlock myBlock;
@end
Copy the code

Blocks are stored in the heap, so where do we put the copy modifier?

typedef void(^MyBlock)(void);
@interface Person : NSObject
@property (nonatomic, copy) MyBlock myBlock;
@end
Copy the code

Block is also stored in the heap when you use copy.

Previously we had to manually decorate the block with the copy property, copy the block from the stack to the heap, keep the block alive after it is out of scope, and use ARC to determine when to release the block on the heap. After a compiler optimization since September 2014, if a block is decorated with strong, the compiler automatically copies the BLCOK from the stack to the heap.

So as long as you use ARC, the current Clang compiler, you can treat blocks just like any other object. Because blocks are mutable. This means you don’t need to copy them.

After the compiler optimizes, the user should not be required to change the property to copy, as long as the user knows that blocks that are strong are managed by ARC just like normal objects.

Circular reference problems with copy and block

The reference count of the object is increased by 1. When a block is referenced to itself, this causes a circular reference problem

Person *person = [[Person alloc]init];
person.myBlock = ^{

    NSLog(@"person = %@",person);
};
[person release];
Copy the code
**2021-11-11 09:32:10.484107+0800 Copy basic use [27410:2097007] person = < person: 0x100790ab0>** **Program ended with exit code: 0**Copy the code

The Person object has not been freed, so what can be done? In this case, we just need to prefix the variable with a __block to tell the compiler not to increment the reference count by 1, but to release it when necessary

__block Person *person = [[Person alloc]init];
person.myBlock = ^{
    NSLog(@"person = %@",person);
};
person.myBlock();
[person release];
Copy the code
**2021-11-11 09:34:03.820602+0800 copy basic use [27500:2099466] person = < person: 0x100605460>** **2021-11-11 09:34:03.825047+0800 Basic use of Copy [275005:2099466] -[Person dealloc]**Copy the code

In ARC, we qualify with the __weak keyword

Person *person = [[Person alloc]init];

__weak Person *weakPerson = person;
person.myBlock = ^{
    NSLog(@"person = %@",weakPerson);
};
person.myBlock();
Copy the code

Custom classes implement copy

A custom class implementing copy needs to meet the following conditions

  • followNSCopyingagreement
  • Implement NSCopying insidecopyWithZone:methods
NS_ASSUME_NONNULL_BEGIN
typedef void(^MyBlock)(void);

@interface Person : NSObject <NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSArray *arr;
@property (nonatomic, strong) MyBlock myBlock;

@end
NS_ASSUME_NONNULL_END
Copy the code
- (id)copyWithZone:(NSZone *)zone {// create object id obj = [[[**self** class] allocWithZone:zone]init]; // Object assignment [obj setName:_name]; [obj setArr:_arr]; [obj setMyBlock:_myBlock]; // return object obj; }Copy the code

Subclasses customize copy

When the parent class complies with the NSCopying protocol and implements copyWithZone:, the child class can also call the parent’s copy method or override the method to initialize its own unique properties

- (id)copyWithZone:(NSZone *)zone {// call the parent class copy method, initialize the parent class id obj = [**super** copyWithZone:zone]; // initialize your own attributes [obj setAge:_age]; // return object obj; }Copy the code

Summary: Copy method is to generate a new object exactly like the source object. And they don’t affect each other