如果要用copy或mutableCopy方法复制自己定义的类对象,那么该类必须要实现<NSCopying>或协议。否则将会导致程序崩溃:
控制台输出为:
2014-02-01 01:11:09.087 Chocolate[951:303] -[Desserts copyWithZone:]: unrecognized selector sent to instance 0x1001099e0 2014-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‘
实现<NSCopying>协议就必须要实现copyWithZone方法,例如B= [A copy],其实现过程为:
首先调用[A copyWithZone:zone];方法创建对象的副本及该对象的引用obj,然后将obj返回并赋值给B。
如果没有实现copyWithZone:方法,copy方法将会发送copyWithZone:nil消息给类,这样将导致程序的崩溃。
如果要复制的类的父类实现了<NSCopying>协议,那么在子类实现复制协议时,必须在copyWithZone方法中首先调用父类的copyWithZone方法。
下面来看个例子:
Desserts类文件
#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文件
#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; }
控制台输出:
2014-01-31 17:56:40.178 Chocolate[3093:303] About desserts:Producer = huizhou hello factory, Price = 101 2014-01-31 17:56:40.179 Chocolate[3093:303] About sweets: Producer = huizhou hello factory, Price = 100
需要特别注意的是,在copy协议中:
desserts.producer = self.producer; desserts.price = self.price;
在复制后,sweets和desserts指向两个不同的Desserts对象。
由于price是基本数据类型变量,所以这里的赋值使得sweets和desserts的price属性在内存中是不同的变量。所以desserts.price++并不会对sweets的price造成影响。
由于producer是指针变量,而在copy时,复制成员producer只是简单的赋值,因此desserts.producer和sweets.producer指向同一个NSMutableString对象,故两个对象的producer输出一致。
明显,这并不能达到我们想要的深复制对象内容的目的,因此对于可变对象,应该在复制属性时进行copy操作:
// desserts.producer = self.producer; desserts.producer = [self.producer copy];
2014-01-31 18:01:14.803 Chocolate[3106:303] About desserts:Producer = huizhou hello factory, Price = 101 2014-01-31 18:01:14.805 Chocolate[3106:303] About sweets: Producer = huizhou, Price = 100
如果属性是不可变对象,如NSString,NSArray等,由于这些对象不可更改,所以我们不用担心这些对象被修改,而且额外的对象会增加内存的开销,所以我们只需要简单的赋值就可以了,而不需要调用copy方法。
对于mutableCopyWithZone方法同理。
实现copyWithZone方法时复制属性小结:
1.基本数据类型的变量复制时直接赋值。
2.对于不可变对象,直接赋值。
3.对于strong特性的可变对象:如果要求浅复制,直接赋值。如果要求深复制,则要调用copy或mutableCopy方法。对于copy特性的属性,使用self.property = theProperty会默认调用copy方法进行复制。
下面新建一个Chocolate类,该类继承自Desserts类。如果要实现Chocolate类对象的复制,同样要实现<NSCopying>或<NSMutableCopying>协议中的方法。由于该类继承自Desserts类,所以Chocolate类的copyWithZone方法必须首先调用父类的copyWithZone方法:
#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; return chocolate; } @end
对于copy特性的属性,与@synthesize一起使用
@property (copy, nonatomic) NSMutableString *brand;
- (void)setBrand:(NSMutableString *)theBrand { if (brand != theBrand) { brand = [theBrand copy]; } }
对于可变类型的属性(如NSMutableString,NSMutableArray等),copy特性比strong特性更加安全。而对于不可变对象如NSString,NSArray等只需要指定strong特性就可以了。
例如,对于Desserts的producer属性,它是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);
2014-01-31 18:54:07.013 Chocolate[3480:303] Huizhou 2014-01-31 18:54:07.015 Chocolate[3480:303] Huizhou hello factory
以上代码中,将desserts.producer赋值给一个用于临时任务的mstr,并且对mstr进行修改,那么desserts.producer将同样被修改。
如果我们不希望类中的属性被修改,可以将producer的特性改为copy:
@property (copy, nonatomic) NSMutableString *producer;
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:‘
复制对象(二)<NSCopying>协议和属性的copy特性
原文:http://blog.csdn.net/u010962810/article/details/18888333