如何在Objective-C中制作深度副本?

如何在Objective-C中制作深度副本?,objective-c,deep-copy,Objective C,Deep Copy,我正在学习ios开发,我对Objective-C中的深度复制感到困惑。 例如,我下面有三节课。现在我想深度复制ClassA,有人能教我完成复制方法吗 A: @接口类A:NSObject @属性(非原子,赋值)int; @属性(非原子,保留)B类*B类; @结束 B: @接口类B:NSObject @属性(非原子,赋值)int bInt; @属性(非原子,保留)C类*C类; @结束 C: @接口类C:NSObject @属性(非原子,赋值)int-cInt; @属性(非原子,复制)NSStri

我正在学习ios开发,我对Objective-C中的深度复制感到困惑。 例如,我下面有三节课。现在我想深度复制ClassA,有人能教我完成复制方法吗

A:

@接口类A:NSObject
@属性(非原子,赋值)int;
@属性(非原子,保留)B类*B类;
@结束
B:

@接口类B:NSObject
@属性(非原子,赋值)int bInt;
@属性(非原子,保留)C类*C类;
@结束
C:

@接口类C:NSObject
@属性(非原子,赋值)int-cInt;
@属性(非原子,复制)NSString*str;
@结束

iOS上的Objective-C不提供任何直接语言或库结构来在浅拷贝和深拷贝之间切换。每个类定义了“获取其副本”的含义:

@implementation ClassA

- (id) copyWithZone: (NSZone*) zone
{
    ClassA *copy = [super copyWithZone:zone];
    [copy setBClass:bClass]; // this would be a shallow copy
    [copy setBClass:[bClass copy]]; // this would be a deep copy
    return copy;
}

@end

当然,在B类和C类中,你必须做同样的决定。如果我没有弄错的话,Objective-C中副本的通常语义是返回一个浅层副本。有关该主题的更多讨论,请参见。

您应该在希望可复制的每个类中添加
copyWithZone:
方法

注:这是我亲手写的,小心打字错误

-(id) copyWithZone:(NSZone *) zone
{
    ClassA *object = [super copyWithZone:zone];
    object.aInt = self.aInt;
    object.bClass = [self.bClass copyWithZone:zone];
    return object;
}

-(id) copyWithZone:(NSZone *) zone
{
    ClassB *object = [super copyWithZone:zone];
    object.bInt = self.bInt;
    object.cClass = [self.cClass copyWithZone:zone];
    return object;
}

-(id) copyWithZone:(NSZone *) zone
{
    ClassC *object = [super copyWithZone:zone];
    object.cInt = self.cInt;
    object.str = [self.str copy];
    return object;
}

按照

“这可以通过将对象及其组成元素写入存档,然后读回新对象来实现。”


这可能会有所帮助。该链接显示了如何使用
NSKeyedArchiver


我有一个自定义类,其中包含很长的属性列表,因此我对它们进行了迭代:

@interface MyClass : NSObject <NSCopying>

#import <objc/runtime.h>

-(id) copyWithZone: (NSZone *) zone {

    MyClass *myCopy = [[MyClass alloc] init];

    //deepCopy
    unsigned int numOfProperties;
    objc_property_t *properties = class_copyPropertyList([self class], &numOfProperties);

    for (int i = 0; i < numOfProperties; i++) {

       objc_property_t property = properties[i];
       NSString *propertyName = [[NSString alloc]initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
       [adressCopy setValue:[[self valueForKey:propertyName] copy] forKey:propertyName];
    }
    return myCopy;
}
@接口MyClass:NSObject
#进口
-(id)copyWithZone:(NSZone*)区{
MyClass*myCopy=[[MyClass alloc]init];
//深度复制
无符号整数属性;
objc_property_t*properties=class_copyPropertyList([self class],&numOfProperties);
对于(int i=0;i

所有customClassProperties也需要实现这一点。

Objective-C的copy和copyWithZone规范都是虚假和危险的,不应使用。 --!-- 至少在与ARC(自动参考计数)一起使用时不使用(2016-08-23)——-- 该代码将导致写入超出内存/缓冲区溢出的范围。 相反,我提供了一种方法来安全地复制initashallowcopy和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;      // DON'T USE copy OR copyWithZone, unless you ignore Apple's guidelines and always make shallow copies in line with the correct example code here for initAsShallowCopy (but you return a copy instead of being a copy)
    -(id)initAsShallowCopy:(ClassA *)original;  // Correct way to make a shallow copy
    -(void)deepCopy;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
@end

@interface ClassB : ClassA
{
    @public
    NSMutableString*    B_Name;
    NSInteger           B_NSInteger;
    long int            B_int;
    float               B_float;
}
    -(id)init;
    -(id)copyWithZone:(NSZone *) zone;      // DON'T USE copy OR copyWithZone, unless you ignore Apple's guidelines and always make shallow copies in line with the correct example code here for initAsShallowCopy (but you return a copy instead of being a copy)
    -(id)initAsShallowCopy:(ClassB *)original;  // Correct way to make a shallow copy
    -(void)deepCopy;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    -(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] ) {    // initialize NSObject
            //A_Name        = [[NSMutableString alloc] init];
            //[A_Name setString:@"I am inited to A"];
            A_Name      = [NSMutableString stringWithString:@"I am inited to A"];
            A_NSInteger = 1;
            A_int       = 1;
            A_float     = 1.0;

            return self;
        }
        return nil;
    }

    /*
    FROM https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/instm/NSObject/copy

    -- NSObject Class Reference --


    - (id)copy

    Discussion
    This is a convenience method for classes that adopt the NSCopying protocol. An exception is raised if there is
    no implementation for copyWithZone:.

    NSObject does not itself support the NSCopying protocol. Subclasses must support the protocol and
    implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super first,
    to incorporate its implementation, unless the subclass descends directly from NSObject.


    + copyWithZone:

    Discussion
    This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol.
    For example, this method lets you use a class object as a key to an NSDictionary object.
    You should not override this method.

    CONCLUSION

    copy says we should incorporate the implementation of copyWithZone, while copyWithZone says we should not override it.. So what is it?
    Looking at copyWithZone, we see that it is a class method (+), meaning it has not access to its instantiated members.
    So maybe they mean, we should not override the class method (+), but we should implement its instance method -copyWithZone:
    !!In any case we should not implement copy, because it is just made for convenience by Apple!!

    FROM: https://developer.apple.com/library/tvos/documentation/Cocoa/Reference/Foundation/Protocols/NSCopying_Protocol/index.html

    -- NSCopying --

    Your options for implementing this protocol are as follows:

    1) Implement NSCopying using alloc and init... in classes that don’t inherit copyWithZone:.

    2) Implement NSCopying by invoking the superclass’s copyWithZone: when NSCopying behavior is inherited.
    If the superclass implementation might use the NSCopyObject function, make explicit assignments to
    pointer instance variables for retained objects.

    3) Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable.

    CONCLUSION:

    From 1) NSObject does not implement copyWithZone so any class that you make that should support copying should call [[Class alloc] init].

    From 2) Any subclass of a copyable object should call [super copyWithZone:zone], but NOT [[Class alloc] init] !!!!!!
    */

    -(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   // Correct way to make a shallow copy
    {
        /* Why this has to be done like this:
            It is very annoying to assign every variable explicitely.
            However this has to be done, in order for ARC (Automatic Reference Counting) (2016-08-23) to work.
            The compiler needs to be aware of any reference made to an object or reference cleared to an object in order to keep track of the
            reference counts.
            The danger is that when you add a variable to you class later on, you must not forget to update your initAsShallowCopy function and 
            possibly your DeepCopy function.
            It would be much nicer if you could just do:
            *self = *original;
            But that gives compiler error:
            /DeepCopyTest/main.m:135:9: Cannot assign to class object ('ClassA' invalid)
            So therefore there is also no raw memory copy between objects,
            so we are stuck with writing out each member variable explicitely.
        */
        if ( self = [super init] ) {    // initialize NSObject
            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;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    {
        /*  Luckily now, we only have to duplicate the objects that require a deep copy.
            So we don't have to write out all the floats, ints and NSIntegers, etcetera. Thus only the pointers (*) to objects.
            */
        A_Name  = [A_Name copy];
    }

@end

@implementation ClassB


    -(id)init
    {
        if ( self = [super init] ) {    // initialize ClassA
            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
    {
        //ClassA *CopiedObject = [[ClassA alloc] init]; We are not a direct descendant from NSObject, so don't call alloc-init
        // instead call the super copyWithZone
        ClassB *CopiedObject = [super copyWithZone:zone];   /* Using ARC (Automatic Reference Counting) 2016-08-23:
        THIS IS A MASSIVE BUFFER OVERFLOW/WRITING OUT OF BOUNDS RISK:
        Since super now allocates the object, it will now only allocate an object of size ClassA
        and effectively allocate too little memory for the ClassB. Unless memory allocation is upgraded to work with magic for
        Objective-C, DON'T USE copy or copyWithZone!!!!
        */

        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   // Correct way to make a shallow copy
    {
        if ( self = [super initAsShallowCopy:original] ) {  // initialize ClassA
            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;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    {
        /*  Luckily now, we only have to duplicate the objects that require a deep copy.
            So we don't have to write out all the floats, ints and NSIntegers, etcetera. Thus only the pointers (*) to objects.
            */
        [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] ) {    // initialize NSObject
            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]; /* Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassCWithoutCopy copyWithZone:]: unrecognized selector sent to instance 0x100100450' */
        //CopiedC       = [ClassCWithoutCopy copyWithZone:nil]; /* DeepCopyTest/main.m:283:33: 'copyWithZone:' is unavailable: not available in automatic reference counting mode
        */

        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];
        // This works as expected: The values of OriginalB and CopiedB differ, but the shallow copied strings are the same.

        // Now make a deep copy of CopiedB
        [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];
        // This works as expected: The values of OriginalB and CopiedB differ and als the deep copied strings are different.

    #else
        OriginalB   = [[ClassB alloc] init];
        CopiedB     = [OriginalB copy];             // Undefined behaviour. You will write unallocated memory

        NSLog(@"OriginalB print:1");
        [OriginalB print];
        NSLog(@"CopiedB print:1");
        /*[CopiedB print];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassA print]: unrecognized selector sent to instance 0x10010ad60' */
        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); // Undefined behaviour. You will read unallocated memory


        [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;
        // This at least works: Changing Original, does not alter the values of Copy.


        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);
        //NSLog(@"B_Name=\"%@\", B_NSInteger=%ld,B_int=%ld,B_float=%f",CopiedB->B_Name,CopiedB->B_NSInteger,CopiedB->B_int,CopiedB->B_float);   // Undefined behaviour. You will read unallocated memory

        /*[CopiedB->A_Name appendString:@" and Appended as the copy"];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:' */
        CopiedB->A_NSInteger = 100;
        CopiedB->A_int = 100;
        CopiedB->A_float = 100.0;
        /*[CopiedB->B_Name appendString:@" and Appended as the copy"];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
         *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'*/
        CopiedB->B_NSInteger = 200;                                     // Undefined behaviour. You will write unallocated memory
        CopiedB->B_int = 200;                                           // Undefined behaviour. You will write unallocated memory
        CopiedB->B_float = 200.0;                                       // Undefined behaviour. You will write unallocated memory

        /* Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
        DeepCopyTest(2376,0x7fff7edda310) malloc: *** error for object 0x10010ad98: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug */

        NSLog(@"OriginalB print after modification of CopiedB:");
        [OriginalB print];
        NSLog(@"CopiedB print after modification of CopiedB:");
        /*[CopiedB print];; / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassA print]: unrecognized selector sent to instance 0x10010ad60' */

    #endif
    }
    return 0;
}
#导入
@接口ClassA:NSObject
{
@公开的
NSMutableString*一个_名称;
NSInteger A_NSInteger;
长整数;
浮球;
}
-(id)init;
-(id)copyWithZone:(NSZone*)zone;//不要使用copy或copyWithZone,除非您忽略苹果的指导原则,并始终按照initashallowCopy的正确示例代码制作浅拷贝(但您返回的是一份拷贝,而不是一份拷贝)
-(id)原件:(A类*)原件;//制作浅拷贝的正确方法
-(无效)深度复制;//制作深度拷贝的正确方法(首先调用initAsShallowCopy)
@结束
@接口ClassB:ClassA
{
@公开的
NSMutableString*B_名称;
NSInteger B_NSInteger;
长整数B_int;
浮球B_浮球;
}
-(id)init;
-(id)copyWithZone:(NSZone*)zone;//不要使用copy或copyWithZone,除非您忽略苹果的指导原则,并始终按照initashallowCopy的正确示例代码制作浅拷贝(但您返回的是一份拷贝,而不是一份拷贝)
-(id)原件:(B类*)原件;//制作浅拷贝的正确方法
-(无效)深度复制;//制作深度拷贝的正确方法(首先调用initAsShallowCopy)
-(作废)打印;
@结束
@接口类C不复制:NSObject
{
@公开的
NSMutableString*C_名称;
NSInteger C_NSInteger;
长整数C_int;
浮球C_浮球;
}
-(id)init;
-(作废)打印;
@结束
@实现类A
-(id)init
{
if(self=[super init]){//初始化NSObject
//A_Name=[[NSMutableString alloc]init];
//[A_Name setString:@“我被初始化为A”];
A_Name=[nsmutablestringwithstring:@“我被初始化为一个”];
A_NSInteger=1;
A_int=1;
A_float=1.0;
回归自我;
}
返回零;
}
/*
从…起https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/instm/NSObject/copy
--NSObject类引用--
-(id)副本
讨论
对于采用NSCopying协议的类来说,这是一种方便的方法。如果存在异常,则会引发异常
copyWithZone:没有实现。
NSObject本身不支持NSCopying协议。子类必须支持协议和
实现copyWithZone:方法。copyWithZone:方法的子类版本应首先将消息发送给super,
合并其实现,除非子类直接从NSObject派生。
+copyWithZone:
讨论
此方法的存在使得类对象可以在需要符合NSCopying协议的对象的情况下使用。
例如,此方法允许您使用类对象作为NSDictionary对象的键。
不应重写此方法。
结论
copy说我们应该合并copyWithZone的实现,而copyWithZone说我们不应该覆盖它。。那是什么呢?
查看copyWithZone,我们看到它是一个类方法(+),这意味着它无法访问其实例化成员。
所以也许t
-(id) copyWithZone:(NSZone *) zone
{
    ClassA *object = [super copyWithZone:zone];
    object.aInt = self.aInt;
    object.bClass = [self.bClass copyWithZone:zone];
    return object;
}

-(id) copyWithZone:(NSZone *) zone
{
    ClassB *object = [super copyWithZone:zone];
    object.bInt = self.bInt;
    object.cClass = [self.cClass copyWithZone:zone];
    return object;
}

-(id) copyWithZone:(NSZone *) zone
{
    ClassC *object = [super copyWithZone:zone];
    object.cInt = self.cInt;
    object.str = [self.str copy];
    return object;
}
@implementation ClassA

- (id)copyWithZone:(NSZone*)zone{
    NSData *buffer;
    buffer = [NSKeyedArchiver archivedDataWithRootObject:self];
    ClassA *copy = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
    return copy;
}
@end
@interface MyClass : NSObject <NSCopying>

#import <objc/runtime.h>

-(id) copyWithZone: (NSZone *) zone {

    MyClass *myCopy = [[MyClass alloc] init];

    //deepCopy
    unsigned int numOfProperties;
    objc_property_t *properties = class_copyPropertyList([self class], &numOfProperties);

    for (int i = 0; i < numOfProperties; i++) {

       objc_property_t property = properties[i];
       NSString *propertyName = [[NSString alloc]initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
       [adressCopy setValue:[[self valueForKey:propertyName] copy] forKey:propertyName];
    }
    return myCopy;
}
#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;      // DON'T USE copy OR copyWithZone, unless you ignore Apple's guidelines and always make shallow copies in line with the correct example code here for initAsShallowCopy (but you return a copy instead of being a copy)
    -(id)initAsShallowCopy:(ClassA *)original;  // Correct way to make a shallow copy
    -(void)deepCopy;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
@end

@interface ClassB : ClassA
{
    @public
    NSMutableString*    B_Name;
    NSInteger           B_NSInteger;
    long int            B_int;
    float               B_float;
}
    -(id)init;
    -(id)copyWithZone:(NSZone *) zone;      // DON'T USE copy OR copyWithZone, unless you ignore Apple's guidelines and always make shallow copies in line with the correct example code here for initAsShallowCopy (but you return a copy instead of being a copy)
    -(id)initAsShallowCopy:(ClassB *)original;  // Correct way to make a shallow copy
    -(void)deepCopy;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    -(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] ) {    // initialize NSObject
            //A_Name        = [[NSMutableString alloc] init];
            //[A_Name setString:@"I am inited to A"];
            A_Name      = [NSMutableString stringWithString:@"I am inited to A"];
            A_NSInteger = 1;
            A_int       = 1;
            A_float     = 1.0;

            return self;
        }
        return nil;
    }

    /*
    FROM https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/instm/NSObject/copy

    -- NSObject Class Reference --


    - (id)copy

    Discussion
    This is a convenience method for classes that adopt the NSCopying protocol. An exception is raised if there is
    no implementation for copyWithZone:.

    NSObject does not itself support the NSCopying protocol. Subclasses must support the protocol and
    implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super first,
    to incorporate its implementation, unless the subclass descends directly from NSObject.


    + copyWithZone:

    Discussion
    This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol.
    For example, this method lets you use a class object as a key to an NSDictionary object.
    You should not override this method.

    CONCLUSION

    copy says we should incorporate the implementation of copyWithZone, while copyWithZone says we should not override it.. So what is it?
    Looking at copyWithZone, we see that it is a class method (+), meaning it has not access to its instantiated members.
    So maybe they mean, we should not override the class method (+), but we should implement its instance method -copyWithZone:
    !!In any case we should not implement copy, because it is just made for convenience by Apple!!

    FROM: https://developer.apple.com/library/tvos/documentation/Cocoa/Reference/Foundation/Protocols/NSCopying_Protocol/index.html

    -- NSCopying --

    Your options for implementing this protocol are as follows:

    1) Implement NSCopying using alloc and init... in classes that don’t inherit copyWithZone:.

    2) Implement NSCopying by invoking the superclass’s copyWithZone: when NSCopying behavior is inherited.
    If the superclass implementation might use the NSCopyObject function, make explicit assignments to
    pointer instance variables for retained objects.

    3) Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable.

    CONCLUSION:

    From 1) NSObject does not implement copyWithZone so any class that you make that should support copying should call [[Class alloc] init].

    From 2) Any subclass of a copyable object should call [super copyWithZone:zone], but NOT [[Class alloc] init] !!!!!!
    */

    -(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   // Correct way to make a shallow copy
    {
        /* Why this has to be done like this:
            It is very annoying to assign every variable explicitely.
            However this has to be done, in order for ARC (Automatic Reference Counting) (2016-08-23) to work.
            The compiler needs to be aware of any reference made to an object or reference cleared to an object in order to keep track of the
            reference counts.
            The danger is that when you add a variable to you class later on, you must not forget to update your initAsShallowCopy function and 
            possibly your DeepCopy function.
            It would be much nicer if you could just do:
            *self = *original;
            But that gives compiler error:
            /DeepCopyTest/main.m:135:9: Cannot assign to class object ('ClassA' invalid)
            So therefore there is also no raw memory copy between objects,
            so we are stuck with writing out each member variable explicitely.
        */
        if ( self = [super init] ) {    // initialize NSObject
            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;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    {
        /*  Luckily now, we only have to duplicate the objects that require a deep copy.
            So we don't have to write out all the floats, ints and NSIntegers, etcetera. Thus only the pointers (*) to objects.
            */
        A_Name  = [A_Name copy];
    }

@end

@implementation ClassB


    -(id)init
    {
        if ( self = [super init] ) {    // initialize ClassA
            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
    {
        //ClassA *CopiedObject = [[ClassA alloc] init]; We are not a direct descendant from NSObject, so don't call alloc-init
        // instead call the super copyWithZone
        ClassB *CopiedObject = [super copyWithZone:zone];   /* Using ARC (Automatic Reference Counting) 2016-08-23:
        THIS IS A MASSIVE BUFFER OVERFLOW/WRITING OUT OF BOUNDS RISK:
        Since super now allocates the object, it will now only allocate an object of size ClassA
        and effectively allocate too little memory for the ClassB. Unless memory allocation is upgraded to work with magic for
        Objective-C, DON'T USE copy or copyWithZone!!!!
        */

        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   // Correct way to make a shallow copy
    {
        if ( self = [super initAsShallowCopy:original] ) {  // initialize ClassA
            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;                            // Correct way to make a deep copy (Call initAsShallowCopy first)
    {
        /*  Luckily now, we only have to duplicate the objects that require a deep copy.
            So we don't have to write out all the floats, ints and NSIntegers, etcetera. Thus only the pointers (*) to objects.
            */
        [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] ) {    // initialize NSObject
            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]; /* Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassCWithoutCopy copyWithZone:]: unrecognized selector sent to instance 0x100100450' */
        //CopiedC       = [ClassCWithoutCopy copyWithZone:nil]; /* DeepCopyTest/main.m:283:33: 'copyWithZone:' is unavailable: not available in automatic reference counting mode
        */

        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];
        // This works as expected: The values of OriginalB and CopiedB differ, but the shallow copied strings are the same.

        // Now make a deep copy of CopiedB
        [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];
        // This works as expected: The values of OriginalB and CopiedB differ and als the deep copied strings are different.

    #else
        OriginalB   = [[ClassB alloc] init];
        CopiedB     = [OriginalB copy];             // Undefined behaviour. You will write unallocated memory

        NSLog(@"OriginalB print:1");
        [OriginalB print];
        NSLog(@"CopiedB print:1");
        /*[CopiedB print];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassA print]: unrecognized selector sent to instance 0x10010ad60' */
        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); // Undefined behaviour. You will read unallocated memory


        [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;
        // This at least works: Changing Original, does not alter the values of Copy.


        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);
        //NSLog(@"B_Name=\"%@\", B_NSInteger=%ld,B_int=%ld,B_float=%f",CopiedB->B_Name,CopiedB->B_NSInteger,CopiedB->B_int,CopiedB->B_float);   // Undefined behaviour. You will read unallocated memory

        /*[CopiedB->A_Name appendString:@" and Appended as the copy"];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:' */
        CopiedB->A_NSInteger = 100;
        CopiedB->A_int = 100;
        CopiedB->A_float = 100.0;
        /*[CopiedB->B_Name appendString:@" and Appended as the copy"];  / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
         *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'*/
        CopiedB->B_NSInteger = 200;                                     // Undefined behaviour. You will write unallocated memory
        CopiedB->B_int = 200;                                           // Undefined behaviour. You will write unallocated memory
        CopiedB->B_float = 200.0;                                       // Undefined behaviour. You will write unallocated memory

        /* Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
        DeepCopyTest(2376,0x7fff7edda310) malloc: *** error for object 0x10010ad98: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug */

        NSLog(@"OriginalB print after modification of CopiedB:");
        [OriginalB print];
        NSLog(@"CopiedB print after modification of CopiedB:");
        /*[CopiedB print];; / * Thread 1: signal SIGABRT: libc++abi.dylib: terminating with uncaught exception of type NSException
        *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ClassA print]: unrecognized selector sent to instance 0x10010ad60' */

    #endif
    }
    return 0;
}