Copy object (2) & lt; NSCopying & gt; copy feature of protocols and attributes

Source: Internet
Author: User

If you want to copy a custom Class Object using the copy or mutableCopy method, the class must implement <NSCopying> or protocol. Otherwise, the program will crash:



Console output:

2014-02-01 01:11:09.087 Chocolate[951:303] -[Desserts copyWithZone:]: unrecognized selector sent to instance 0x1001099e02014-02-01 01:11:09.089 Chocolate[951:303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Desserts copyWithZone:]: unrecognized selector sent to instance 0x1001099e0'

To implement the <NSCopying> protocol, you must implement the copyWithZone method, for example, B = [A copy]. The implementation process is as follows:

Call the [A copyWithZone: zone] method to create A copy of the object and reference obj of the object. Then, return the obj and assign it to B.

If the copyWithZone: method is not implemented, the copy method will send the copyWithZone: nil message to the class, which will cause the program to crash.


If the parent class of the class to be copied implements the <NSCopying> protocol, the copyWithZone method of the parent class must be called in the copyWithZone method when the subclass implements the replication protocol.


Here is an example:

Desserts files

#import <Foundation/Foundation.h>@interface Desserts : NSObject <NSCopying, NSMutableCopying>@property (strong, nonatomic) NSMutableString *producer;@property (assign, nonatomic) NSUInteger price;- (void)setProducer:(NSMutableString *)theProducer Price:(NSUInteger)thePrice;@end
#import "Desserts.h"@implementation Desserts- (void)setProducer:(NSMutableString *)theProducer Price:(NSUInteger)thePrice {    self.producer = theProducer;    self.price    = thePrice;}- (NSString *)description {    return [NSString stringWithFormat:@"Producer = %@, Price = %lu", self.producer, self.price];}- (id)copyWithZone:(NSZone *)zone {    Desserts *desserts = [[Desserts allocWithZone:zone] init];    desserts.producer  = self.producer;    desserts.price     = self.price;    return desserts;}- (id)mutableCopyWithZone:(NSZone *)zone {    Desserts *desserts = [[Desserts allocWithZone:zone] init];    desserts.producer  = self.producer;    desserts.price     = self.price;    return desserts;}@end


Main. m file

#import <Foundation/Foundation.h>#import "Desserts.h"int main(int argc, const char * argv[]){    @autoreleasepool {                Desserts *desserts = [[Desserts alloc] init];        NSMutableString *huizhou = [NSMutableString stringWithString:@"huizhou"];        [desserts setProducer:huizhou Price:100];                Desserts *sweets = [desserts copy];                [desserts.producer appendString:@" hello factory"];        desserts.price++;                NSLog(@"About desserts:%@", desserts);        NSLog(@"About sweets:  %@", sweets);            }    return 0;}

Console output:

2014-01-31 17:56:40.178 Chocolate[3093:303] About desserts:Producer = huizhou hello factory, Price = 1012014-01-31 17:56:40.179 Chocolate[3093:303] About sweets:  Producer = huizhou hello factory, Price = 100


Note that in the copy protocol:

desserts.producer = self.producer;desserts.price    = self.price;

Here, the producer is the object of the strong feature, and the price is the basic data type variable of the assing feature.

After replication, sweets and desserts point to two different Desserts objects.

Because price is a variable of the basic data type, the value assignment here makes the price attribute of sweets and desserts different variables in memory. Therefore, desserts. price ++ does not affect the price of sweets.

Because producer is a pointer variable, while copying a member producer is a simple assignment, desserts. producer and sweets. producer points to the same NSMutableString object, so the output of the two objects is consistent.

Obviously, this does not achieve the purpose of deep copying object content we want. Therefore, for a mutable object, copy operations should be performed when copying attributes:

//    desserts.producer = self.producer;    desserts.producer = [self.producer copy];

After modification, because the copy of the NSMutableString object is a deep copy, the copied desserts and sweets point to two different objects:

2014-01-31 18:01:14.803 Chocolate[3106:303] About desserts:Producer = huizhou hello factory, Price = 1012014-01-31 18:01:14.805 Chocolate[3106:303] About sweets:  Producer = huizhou, Price = 100

Modification to the objects directed to by desserts. producer does not affect the objects directed to by sweets. producer.

If the attribute is an unchangeable object, such as NSString and NSArray, because these objects cannot be changed, we do not have to worry about these objects being modified, and the extra objects will increase the memory overhead, therefore, we only need to assign values, instead of calling the copy method.

The mutableCopyWithZone method is the same.


Copy the attribute summary when the copyWithZone method is implemented:

1. assign a value directly when copying a variable of the basic data type.

2. assign values directly to immutable objects.

3. For objects with the strong feature: If the shortest copy is required, assign a value directly. If deep replication is required, you need to call the copy or mutableCopy method (of course, for arrays or dictionaries, it is still shortest copy ). For the properties of the copy feature, using self. property = theProperty will call the copy method by default for copying.


Create a Chocolate class, which inherits from the Desserts class. To copy Chocolate objects, you must also implement methods in the <NSCopying> or <NSMutableCopying> protocol. Because this class inherits from the Desserts class, the copyWithZone method of the Chocolate class must first call the copyWithZone method of the parent class:

#import "Desserts.h"@interface Chocolate : Desserts <NSCopying>@property (copy, nonatomic) NSMutableString *brand;@property (strong, nonatomic) NSString *details;- (void)setProducer:(NSMutableString *)theProducer              Price:(NSUInteger)thePrice              Brand:(NSMutableString *)theBrand            Details:(NSString *)theDetails;@end
#import "Chocolate.h"@implementation Chocolate- (void)setProducer:(NSMutableString *)theProducer              Price:(NSUInteger)thePrice              Brand:(NSMutableString *)theBrand            Details:(NSString *)theDetails {    [super setProducer:theProducer Price:thePrice];    self.brand   = theBrand;    self.details = theDetails;}- (NSString *)description {    return [NSString stringWithFormat:@"\n\tProducer = %@\n\tPrice = %lu\n\tBrand = %@\n\tDetails = %@", self.producer, self.price, self.brand, self.details];}- (id)copyWithZone:(NSZone *)zone {    [super copyWithZone:zone];        Chocolate *chocolate = [[Chocolate allocWithZone:zone] init];    chocolate.producer   = [self.producer copy];    chocolate.price      = self.price;    chocolate.brand  = self.brand;    chocolate.details  = [self.details copy];    return chocolate;}@end


For the copy attribute, use it with @ synthesize

@property (copy, nonatomic) NSMutableString *brand;

The merged setter method calls the copy method, and its implementation is similar:

- (void)setBrand:(NSMutableString *)theBrand {    if (brand != theBrand) {        brand = [theBrand copy];    }}

Because the copy method is called instead of the mutableCopy method, an immutable copy is created, so you don't have to worry about modifying the attribute value. You can directly assign values to the attribute variables in the copyWithZone method.

For variable-type attributes (such as NSMutableString and NSMutableArray), the copy feature is safer than the strong feature. For immutable objects such as NSString and NSArray, if the assignment source is a mutable object, the value of NSString may also be changed. If you want to protect this attribute from being modified, use the copy feature, otherwise, use the strong feature.

For example, the producer attribute of Desserts is strong:

@property (strong, nonatomic) NSMutableString *producer;
        Desserts *desserts = [[Desserts alloc] init];        [desserts setProducer:[NSMutableString stringWithString:@"Huizhou"] Price:100];        NSMutableString *mstr = desserts.producer;        NSLog(@"%@", desserts.producer);                [mstr appendString:@" hello factory"];                NSLog(@"%@", desserts.producer);

Console output:

2014-01-31 18:54:07.013 Chocolate[3480:303] Huizhou2014-01-31 18:54:07.015 Chocolate[3480:303] Huizhou hello factory


In the above Code, assign desserts. producer to an mstr for a temporary task and modify the mstr. Then, desserts. producer will be modified as well.

If we do not want the attributes in the class to be modified, we can change the producer feature to copy:

@property (copy, nonatomic) NSMutableString *producer;

Then, use the getter method to obtain and modify the producer value, which will cause the program to crash:

2014-01-31 18:56:02.206 Chocolate[3503:303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

In this way, the attribute is not modified.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.