Objective-C的copy和copyWithZone规范是虚假和危险的,不应该使用。
--!-- 至少在使用ARC(自动引用计数)时不应该使用(2016-08-23)--!--
该代码将导致写出内存边界/缓冲区溢出。
相反,我提供了一种安全复制对象的方法initAsShallowCopy和deepCopy。
请参见下面代码中的我的测试结果:
#import <Foundation/Foundation.h>
@interface ClassA : NSObject
{
@public
NSMutableString* A_Name;
NSInteger A_NSInteger;
long int A_int;
float A_float;
}
-(id)init;
-(id)copyWithZone:(NSZone *) zone;
-(id)initAsShallowCopy:(ClassA *)original;
-(void)deepCopy;
@end
@interface ClassB : ClassA
{
@public
NSMutableString* B_Name;
NSInteger B_NSInteger;
long int B_int;
float B_float;
}
-(id)init;
-(id)copyWithZone:(NSZone *) zone;
-(id)initAsShallowCopy:(ClassB *)original;
-(void)deepCopy;
-(void)print;
@end
@interface ClassCWithoutCopy : NSObject
{
@public
NSMutableString* C_Name;
NSInteger C_NSInteger;
long int C_int;
float C_float;
}
-(id)init;
-(void)print;
@end
@implementation ClassA
-(id)init
{
if ( self = [super init] ) {
A_Name = [NSMutableString stringWithString:@"I am inited to A"];
A_NSInteger = 1;
A_int = 1;
A_float = 1.0;
return self;
}
return nil;
}
-(id) copyWithZone:(NSZone *) zone
{
ClassA *CopiedObject = [[ClassA alloc] init];
if(CopiedObject){
CopiedObject->A_Name = [A_Name copy];
CopiedObject->A_NSInteger = A_NSInteger;
CopiedObject->A_int = A_int;
CopiedObject->A_float = A_float;
return CopiedObject;
}
return nil;
}
-(id)initAsShallowCopy:(ClassA *)original
{
if ( self = [super init] ) {
A_Name = original->A_Name;
A_NSInteger = original->A_NSInteger;
A_int = original->A_int;
A_float = original->A_float;
return self;
}
return nil;
}
-(void)deepCopy;
{
A_Name = [A_Name copy];
}
@end
@implementation ClassB
-(id)init
{
if ( self = [super init] ) {
B_Name = [NSMutableString stringWithString:@"I am inited to B"];
B_NSInteger = 2;
B_int = 2;
B_float = 2.0;
return self;
}
return nil;
}
-(id) copyWithZone:(NSZone *) zone
{
ClassB *CopiedObject = [super copyWithZone:zone];
if(CopiedObject){
CopiedObject->B_Name = [B_Name copy];
CopiedObject->B_NSInteger = B_NSInteger;
CopiedObject->B_int = B_int;
CopiedObject->B_float = B_float;
return CopiedObject;
}
return nil;
}
-(id)initAsShallowCopy:(ClassB *)original
{
if ( self = [super initAsShallowCopy:original] ) {
B_Name = original->B_Name;
B_NSInteger = original->B_NSInteger;
B_int = original->B_int;
B_float = original->B_float;
return self;
}
return nil;
}
-(void)deepCopy;
{
[super deepCopy];
B_Name = [B_Name copy];
}
-(void)print
{
NSLog(@"A_Name=\"%@\", A_NSInteger=%ld,A_int=%ld,A_float=%f",A_Name,A_NSInteger,A_int,A_float);
NSLog(@"B_Name=\"%@\", B_NSInteger=%ld,B_int=%ld,B_float=%f",B_Name,B_NSInteger,B_int,B_float);
}
@end
@implementation ClassCWithoutCopy
-(id)init
{
if ( self = [super init] ) {
C_Name = [NSMutableString stringWithString:@"I am inited to C"];
C_NSInteger = 3;
C_int = 3;
C_float = 3.0;
return self;
}
return nil;
}
-(void)print
{
NSLog(@"C_Name=\"%@\", C_NSInteger=%ld,C_int=%ld,C_float=%f",C_Name,C_NSInteger,C_int,C_float);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ClassB *OriginalB;
ClassB *CopiedB;
#define USE_CORRECT_DEEP_COPY_AND_SHALLOW_COPY 1
#define USE_CLASSC_WITHOUT_COPY_TEST 0
#if(USE_CLASSC_WITHOUT_COPY_TEST)
ClassCWithoutCopy *OriginalC;
ClassCWithoutCopy *CopiedC;
OriginalC = [[ClassCWithoutCopy alloc] init];
CopiedC = [OriginalC copy];
*/
NSLog(@"OriginalC print:1");
[OriginalC print];
NSLog(@"CopiedC print:1");
[CopiedC print];
[OriginalC->C_Name appendString:@" and Appended as the original"];
OriginalC->C_NSInteger = 30;
OriginalC->C_int = 30;
OriginalC->C_float = 30.0;
NSLog(@"OriginalC print:2");
[OriginalC print];
NSLog(@"CopiedC print:2");
[CopiedC print];
#endif
#if(USE_CORRECT_DEEP_COPY_AND_SHALLOW_COPY)
OriginalB = [[ClassB alloc] init];
CopiedB = [[ClassB alloc] initAsShallowCopy:OriginalB];
NSLog(@"OriginalB print:1");
[OriginalB print];
NSLog(@"CopiedB print:1");
[CopiedB print];
[OriginalB->A_Name appendString:@" and Appended as the original"];
OriginalB->A_NSInteger = 10;
OriginalB->A_int = 10;
OriginalB->A_float = 10.0;
[OriginalB->B_Name appendString:@" and Appended as the original"];
OriginalB->B_NSInteger = 20;
OriginalB->B_int = 20;
OriginalB->B_float = 20.0;
NSLog(@"OriginalB print:2");
[OriginalB print];
NSLog(@"CopiedB print:2");
[CopiedB print];
[CopiedB deepCopy];
[OriginalB->A_Name appendString:@" and Appended twice as the original"];
OriginalB->A_NSInteger = 100;
OriginalB->A_int = 100;
OriginalB->A_float = 100.0;
[OriginalB->B_Name appendString:@" and Appended twice as the original"];
OriginalB->B_NSInteger = 200;
OriginalB->B_int = 200;
OriginalB->B_float = 200.0;
NSLog(@"OriginalB print:3");
[OriginalB print];
NSLog(@"CopiedB print:3");
[CopiedB print];
#else
OriginalB = [[ClassB alloc] init];
CopiedB = [OriginalB copy];
NSLog(@"OriginalB print:1");
[OriginalB print];
NSLog(@"CopiedB print:1");
NSLog(@"A_Name=\"%@\", A_NSInteger=%ld,A_int=%ld,A_float=%f",CopiedB->A_Name,CopiedB->A_NSInteger,CopiedB->A_int,CopiedB->A_float);
NSLog(@"B_Name=\"%@\", B_NSInteger=%ld,B_int=%ld,B_float=%f",CopiedB->B_Name,CopiedB->B_NSInteger,CopiedB->B_int,CopiedB->B_float);
[OriginalB->A_Name appendString:@" and Appended as the original"];
OriginalB->A_NSInteger = 10;
OriginalB->A_int = 10;
OriginalB->A_float = 10.0;
[OriginalB->B_Name appendString:@" and Appended as the original"];
OriginalB->B_NSInteger = 20;
OriginalB->B_int = 20;
OriginalB->B_float = 20.0;
NSLog(@"OriginalB print:2");
[OriginalB print];
NSLog(@"CopiedB print:2");
NSLog(@"A_Name=\"%@\", A_NSInteger=%ld,A_int=%ld,A_float=%f",CopiedB->A_Name,CopiedB->A_NSInteger,CopiedB->A_int,CopiedB->A_float);
CopiedB->A_NSInteger = 100;
CopiedB->A_int = 100;
CopiedB->A_float = 100.0;
CopiedB->B_NSInteger = 200;
CopiedB->B_int = 200;
CopiedB->B_float = 200.0;
NSLog(@"OriginalB print after modification of CopiedB:");
[OriginalB print];
NSLog(@"CopiedB print after modification of CopiedB:");
#endif
}
return 0;
}
PS-1: 来自:
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/ObjectCopying.html
-- 对象复制 --
深拷贝会复制被引用的对象,而浅拷贝只会复制对这些对象的引用。因此,如果将对象A浅拷贝到对象B,则对象B引用与对象A相同的实例变量(或属性)。特别是对于值对象,深拷贝对象比浅拷贝更可取。
注意:
这个表述不太清楚,尤其是伴随的插图,它给出了错误的解释。
这个表述让人觉得两个指向同一对象的引用算作浅拷贝。这是不正确的。它根本就没有进行拷贝。
明确的表述应该是:
-对象的浅拷贝具有从其父对象复制的所有值和引用,但本身在内存中是一个独特的对象。
-对象的深拷贝具有从其父对象复制的所有值,并且本身在内存中也是一个独特的对象,但现在所有的引用都指向它们自己 - 原始引用对象的副本。
尽管深度复制的确切实现可能不会完全产生深度副本。
指向外部引用的对象(例如硬件项目或图形驱动程序)无法复制,但只能增加引用计数。
有些深度复制没有功能意义。一个对象可能引用它所在的窗口,但复制窗口是没有意义的。
一个对象也可能引用被认为是不可变的数据,因此复制它将不是有效的。
PS-2:你本可以在我手动格式化所有代码之前给我ctrl-K的提示。
PS-3:Apple-Z(撤消)会撤消所有我的格式设置而不是最后一次,我无法重做它。
copy
方法的开头... - Wain