How can I make a deep copy in Objective-C? - objective-c
I'm learning ios development and I'm confused with deep copying in Objective-C.
For example,I have three class below. Now I want to deep copy ClassA, can anybody teach me to finish the copy method?
A:
#interface ClassA : NSObject <NSCopying>
#property (nonatomic, assign) int aInt;
#property (nonatomic, retain) ClassB *bClass;
#end
B:
#interface ClassB : NSObject <NSCopying>
#property (nonatomic, assign) int bInt;
#property (nonatomic, retain) ClassC *cClass;
#end
C:
#interface ClassC : NSObject <NSCopying>
#property (nonatomic, assign) int cInt;
#property (nonatomic, copy) NSString *str;
#end
Following the explanation at http://www.techotopia.com/index.php/Copying_Objects_in_Objective-C
"This can be achieved by writing the object and its constituent elements to an archive and then reading back into the new object."
#implementation ClassA
- (id)copyWithZone:(NSZone*)zone{
NSData *buffer;
buffer = [NSKeyedArchiver archivedDataWithRootObject:self];
ClassA *copy = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
return copy;
}
#end
You should add the copyWithZone: method in each class you want to be copiable.
NB: I wrote this by hand, watch out for typos.
-(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;
}
Objective-C on iOS doesn’t offer any direct language or library construct to switch between a shallow and a deep copy. Each class defines what it means to “get its copy”:
#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
Of course you would have to do the same decision in ClassB and ClassC. If I am not mistaken, the usual semantics for a copy in Objective-C is to return a shallow copy. See also this question about copying arrays for more discussion of the topic.
I had custom classes with long lists of properties, so I iterated over them:
#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;
}
All customClassProperties will need to implement this as well.
This could be of some help. The link shows how to do the deep copy using NSKeyedArchiver
http://iphonecodecenter.wordpress.com/2013/08/26/difference-between-shallow-copy-and-deep-copy/
Objective-C's copy and copyWithZone specifications are bogus and dangerous and should not be used.
--!-- At least not when used with ARC (Automatic Reference Counting) (2016-08-23) --!--
The code will lead to writing out of the bounds of memory / buffer overflows.
Instead I present a method to safely copy objects initAsShallowCopy and deepCopy.
See my test results in code below:
#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;
}
PS-1: FROM:
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/ObjectCopying.html
-- Object copying --
A deep copy duplicates the objects referenced while a shallow copy duplicates only the references to those objects. So if object A is shallow-copied to object B, object B refers to the same instance variable (or property) that object A refers to. Deep-copying objects is preferred to shallow-copying, especially with value objects.
NOTE:
This is unclear formulation, especially with the accompanied illustration, which suggests a wrong explanation.
This formulation makes it appear that two references to the same object count as a shallow copy. This is not true. It isn't a copy at all.
The clear formulation would be that:
-A shallow copy of an object has all the values and references copied from its parent, but is itself a unique object in memory.
-A deep copy of an object has all the values copied from its parent and is itself a unique object in memory, but all the references now reference to -deep themselves - copies of the original references objects.
Although the exact implementation of deep copying might not 100% give deep copies.
Objects that point to external references (suchs as a hardware item or graphics driver can't be duplicated, but only increase the reference count)
Some deep copying has no functional sense. An object might reference its window it is in, but it makes no sense to duplicate the window.
An object might also reference data that is considered immutable, so it would not be efficient to duplicate that.
PS-2: You could have give me the hint of ctrl-K before I tried to format all my code manually.
PS-3: Apple-Z (undo) undoes all my formatting instead of the last one and I can't redo it.
Related
Relying on the (copy) attribute to copy NSMutableDictionary causes crash
The following program relies on the copy attribute to copy a NSMutableDictionary. The copy is apparently ok, but, if I try to add a new element to the copy, the program crashes. Is it some kind of bug? PS. If it matters it's NON ARC #import <Foundation/Foundation.h> #interface Dog: NSObject #property (copy) NSMutableDictionary *dict; #end #implementation Dog #synthesize dict; - (id) init { if ( (self = [super init]) ) { dict = [[NSMutableDictionary alloc] init]; } return self; } - (void) print { for (id key in dict) { printf("%s --> %s\n", [key UTF8String], [dict[key] UTF8String] ); } } #end //------------------------------------------------------ int main() { Dog *dog1 = [[Dog alloc] init]; Dog *dog2 = [[Dog alloc] init]; dog1.dict[#"color"] = #"black"; dog2.dict = dog1.dict; [dog2 print]; // the print shows that dog2.dict is indeed a copy of dog1.dict // lldb shows that it is a shallow copy, which I guess is ok // since values are immutable. // ... so far so good. dog1.dict[#"tail"] = #"long"; // This goes smoothly // // But... dog2.dict[#"tail"] = #"long"; // here program crashes with the following message // // -[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance 0x100108ee0 //*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance 0x100108ee0' return(0); } EDIT: If I replace the line dog2.dict = dog1.dict; with [dog2.dict addEntriesFromDictionary: dog1.dict]; then it works. There is no crash. So, OK, this is the correct way of doing it. But my point is: don't I deserve at least a warning from the compiler?
You've broken some rules here, which is why it's going badly for you. As a rule, you should not return or accept NSMutableDictionary as a property. There are exceptions (and you have to code around that), but generally you should not. To code this correctly, you need to separate your interface from your implementation. The correct interface is: #interface Dog: NSObject #property (copy) NSDictionary *dict; #end This object promises to accept and return NSDictionary, and it promises that the accepted and returned NSDictionaries will be independent copies, so the caller doesn't need to worry about mutability issues. We then need to implement those promises: #implementation Dog { NSMutableDictionary *mutableDict; } - (id) init { if ( (self = [super init]) ) { mutableDict = [[NSMutableDictionary alloc] init]; } return self; } - (void) print { for (id key in mutableDict) { printf("%s --> %s\n", [key UTF8String], [mutableDict[key] UTF8String] ); } } - (NSDictionary *)dict { return [mutableDict copy]; // Add an -autorelease if this is MRC } - (void)setDict: (NSDictionary *)newValue { // I'm going to pretend this is ARC; for MRC, code this in your style. mutableDict = [newValue mutableCopy]; } #end On the other hand, if you really do want to share state with an NSMutableDictionary, you should not copy it, you should retain it to make clear the semantics. #interface Dog: NSObject #property (retain) NSMutableDictionary *dict; #end And this makes it (hopefully) clear to the caller that this is shared state and should be treated as such, and the caller should make copies if they so desire. But you generally should avoid this. And if you do go this way, I would call the property mutableDict or something like that to make it clear that this is unusual. For an example, see -[NSAttributedString mutableString]. There's not really a "mutable, but independent copy" semantic in ObjC property annotations, and I wouldn't create one unless you have a very strong need (generally performance related). I would generally just return an immutable copy and let the caller make their own mutable copy. One more side note: Sometimes Cocoa implements "returns an immutable copy" as "just return the mutable one, cast to an immutable type." This improves performance by avoiding the copy, at the cost of sometimes the data changing behind your back. As an example, look at the docs for -[NSView subviews]. In my opinion you should avoid this pattern unless it is critical for performance (and even then, I'd make a special method like -subviewsBackingStore or something silly like that to make it clear "this is weird."
copy attribute creates an NSDictionary instance, not NSMutableDictionary, that's why it's crashing. First, change the property to retain from copy: #property (retain) NSMutableDictionary* duct; Then you can do the following: Dog *dog2 = [[Dog alloc] init]; dog2.dict = [dog1.dict mutableCopy];
Why concurrently setting ivar makes bad access error?
I want to directly assign value to an ivar of class concurrently. I know there will be problem using setter method (self.target = ...) since ARC inner retain and release stuff for strong property. But I'm trying to use ivar. Is it because the implicit qualifier __strong? But _target is an ivar, so it won't be released outside each dispatch_async block, right? And if you make the string shorter, iOS system will apply a tagged pointer to _target, why in this case no bad access error happens anymore? #interface ClassA () #property (nonatomic, strong) NSString *target; #end #implementation ClassA - (void)test { dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10000 ; i++) { dispatch_async(queue, ^{ _target = [NSString stringWithFormat:#"aaaaaaaaaaaaaa-%d",i]; //Bad Access Error in releasing, an NSCFString //_target = [NSString stringWithFormat:#"aa-%d",i]; //No problem, an NSTaggedPointerString }); } } #end int main(int argc, char * argv[]) { ClassA *obj = [[ClassA alloc] init]; [obj test]; return 0; }
Using ivar doesn't makes difference: compiler just add retain/release instead of you. You need unsafe_unretained property to disable insertion of retain/release
ObjectiveC allocation and init?
I have just made a sample short demo program for fun when I was playing with Objective-C: Some piece of code: // TestClass.h: #interface TestClass : NSObject { int someNumber; float someFloat; } #property int someNumber; #property float someFloat; // Returns String containing some instance values: -(NSString *)getNiceString; // Returns always the same string: -(NSString *)getAnotherString; -(id)init; #end -- //TestClass.m: #import "TestClass.h" #implementation TestClass #synthesize someFloat; #synthesize someNumber; -(NSString*) getNiceString{ return [NSString stringWithFormat: #"Float number: %f and the number is: %d", self.someFloat, self.someNumber]; } -(NSString *) getAnotherString{ return [NSString stringWithString:#"TEST STRING"]; } -(id)init{ self = [super init]; if(self){ self.someFloat = 100.34; self.someNumber = 324; return self; } return nil; } #end And some main stuff: #import <Foundation/Foundation.h> #import "TestClass.h" int main (int argc, const char * argv[]) { #autoreleasepool { TestClass* instance = [TestClass alloc]; // Version 2: // TestClass* instance = [[TestClass alloc]init]; NSLog(#"%#", [instance getNiceString]); NSLog(#"%#", [instance getAnotherString]); } return 0; } When I use TestClass* instance = [TestClass alloc]; in main the output is: 2013-03-05 09:56:34.767 ObjectiveTest[8367:903] Float number: 0.000000 and the number is: 0 2013-03-05 09:56:34.770 ObjectiveTest[8367:903] TEST STRING When the second version is used instead (TestClass* instance = [[TestClass alloc]init];): 2013-03-05 10:06:46.743 ObjectiveTest[8421:903] Float number: 100.339996 and the number is: 324 2013-03-05 10:06:46.750 ObjectiveTest[8421:903] TEST STRING The question is if [TestClass alloc] makes any initialization stuff (String is returned properly and values are zeros)... It is worth to mention that if I remove the -(id)init: implementation from TestClass.m the outputs for versions with init and without it are exactly the same... Is there any default initialization?
alloc will zero out the memory region. More detail can be found here What happens when alloc or allocWithZone is called?
The question is if [TestClass alloc] makes any initialization stuff (String is returned properly and values are zeros)... It is worth to mention that if I remove the -(id)init: implementation from TestClass.m the outputs for versions with init and without it are exactly the same... Is there any default initialization? alloc doesn't initialize any member, so if you just call alloc, then someFloat will not be initialized (default value will be 0.0). If you keep away your alloc method from your class implementation the same happens: someFloat will not be initialized and it will have a default value of 0.0 . But calling just alloc and not init has many disadvantages: all the subclass initializers will not be called, thus you will not be able to use some NSObject's attributes, you shouldn't call just alloc. alloc-init is always used by convention.
alloc doesn't initialize the object correctly, and so must always be used. The float isn't initialized correctly (0.000 != 100.34) and the string is the result of calling a method which returns a string literal, not an instance variable.
NSArray of weak references (__unsafe_unretained) to objects under ARC
I need to store weak references to objects in an NSArray, in order to prevent retain cycles. I'm not sure of the proper syntax to use. Is this the correct way? Foo* foo1 = [[Foo alloc] init]; Foo* foo2 = [[Foo alloc] init]; __unsafe_unretained Foo* weakFoo1 = foo1; __unsafe_unretained Foo* weakFoo2 = foo2; NSArray* someArray = [NSArray arrayWithObjects:weakFoo1, weakFoo2, nil]; Note that I need to support iOS 4.x, thus the __unsafe_unretained instead of __weak. EDIT (2015-02-18): For those wanting to use true __weak pointers (not __unsafe_unretained), please check out this question instead: Collections of zeroing weak references under ARC
As Jason said, you can't make NSArray store weak references. The easiest way to implement Emile's suggestion of wrapping an object inside another object that stores a weak reference to it is the following: NSValue *value = [NSValue valueWithNonretainedObject:myObj]; [array addObject:value]; Another option: a category that makes NSMutableArray optionally store weak references. Note that these are "unsafe unretained" references, not self-zeroing weak references. If the array is still around after the objects are deallocated, you'll have a bunch of junk pointers.
The solutions to use a NSValue helper or to create a collection (array, set, dict) object and disable its Retain/Release callbacks are both not 100% failsafe solutions with regard to using ARC. As various comments to these suggestions point out, such object references will not work like true weak refs: A "proper" weak property, as supported by ARC, has two behaviors: Doesn't hold a strong ref to the target object. That means that if the object has no strong references pointing to it, the object will be deallocated. If the ref'd object is deallocated, the weak reference will become nil. Now, while the above solutions will comply with behavior #1, they do not exhibit #2. To get behavior #2 as well, you have to declare your own helper class. It has just one weak property for holding your reference. You then add this helper object to the collection. Oh, and one more thing: iOS6 and OSX 10.8 supposedly offer a better solution: [NSHashTable weakObjectsHashTable] [NSPointerArray weakObjectsPointerArray] [NSPointerArray pointerArrayWithOptions:] These should give you containers that hold weak references (but note matt's comments below). An example (updated 2 Feb 2022) #import <Foundation/Foundation.h> static BOOL didDealloc = NO; #interface TestClass : NSObject #end #implementation TestClass -(void)dealloc { didDealloc = YES; } #end int main(int argc, const char * argv[]) { NSPointerArray *pa = [NSPointerArray weakObjectsPointerArray]; #autoreleasepool { TestClass *obj = TestClass.new; [pa addPointer:(__bridge void * _Nullable)(obj)]; // stores obj as a weak ref assert([pa pointerAtIndex:0] != nil); assert(!didDealloc); } // at this point the TestClass obj will be deallocated assert(didDealloc); assert([pa pointerAtIndex:0] == nil); // verify that the weak ref is null now return 0; } If you run this you'll find that after adding the TestClass object to the pointer array pa, then releasing that object again, the pointer (which is internally a weak object ref) is now set to null as desired. However, note that calling [pa compact] at the end will not remove the nil pointer as I'd have expected.
I am new to objective-C, after 20 years of writing c++. In my view, objective-C is excellent at loosely-coupled messaging, but horrible for data management. Imagine how happy I was to discover that xcode 4.3 supports objective-c++! So now I rename all my .m files to .mm (compiles as objective-c++) and use c++ standard containers for data management. Thus the "array of weak pointers" problem becomes a std::vector of __weak object pointers: #include <vector> #interface Thing : NSObject #end // declare my vector std::vector<__weak Thing*> myThings; // store a weak reference in it Thing* t = [Thing new]; myThings.push_back(t); // ... some time later ... for(auto weak : myThings) { Thing* strong = weak; // safely lock the weak pointer if (strong) { // use the locked pointer } } Which is equivalent to the c++ idiom: std::vector< std::weak_ptr<CppThing> > myCppThings; std::shared_ptr<CppThing> p = std::make_shared<CppThing>(); myCppThings.push_back(p); // ... some time later ... for(auto weak : myCppThings) { auto strong = weak.lock(); // safety is enforced in c++, you can't dereference a weak_ptr if (strong) { // use the locked pointer } } Proof of concept (in the light of Tommy's concerns about vector reallocation): main.mm: #include <vector> #import <Foundation/Foundation.h> #interface Thing : NSObject #end #implementation Thing #end extern void foo(Thing*); int main() { // declare my vector std::vector<__weak Thing*> myThings; // store a weak reference in it while causing reallocations Thing* t = [[Thing alloc]init]; for (int i = 0 ; i < 100000 ; ++i) { myThings.push_back(t); } // ... some time later ... foo(myThings[5000]); t = nullptr; foo(myThings[5000]); } void foo(Thing*p) { NSLog(#"%#", [p className]); } example log output: 2016-09-21 18:11:13.150 foo2[42745:5048189] Thing 2016-09-21 18:11:13.152 foo2[42745:5048189] (null)
If you do not require a specific order you could use NSMapTable with special key/value options NSPointerFunctionsWeakMemory Uses weak read and write barriers appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release.
I believe the best solution for this is to use NSHashTable or NSMapTable. the Key or/and the Value can be weak. You can read more about it here: http://nshipster.com/nshashtable-and-nsmaptable/
To add weak self reference to NSMutableArray, create a custom class with a weak property as given below. NSMutableArray *array = [NSMutableArray new]; Step 1: create a custom class #interface DelegateRef : NSObject #property(nonatomic, weak)id delegateWeakReference; #end Step 2: create a method to add self as weak reference to NSMutableArray. But here we add the DelegateRef object -(void)addWeakRef:(id)ref { DelegateRef *delRef = [DelegateRef new]; [delRef setDelegateWeakReference:ref] [array addObject:delRef]; } Step 3: later on, if the property delegateWeakReference == nil, the object can be removed from the array The property will be nil, and the references will be deallocated at proper time independent of this array references
The simplest solution: NSMutableArray *array = (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil); NSMutableDictionary *dictionary = (__bridge_transfer NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil); NSMutableSet *set = (__bridge_transfer NSMutableSet *)CFSetCreateMutable(nil, 0, nil); Note: And this works on iOS 4.x too.
No, that's not correct. Those aren't actually weak references. You can't really store weak references in an array right now. You need to have a mutable array and remove the references when you're done with them or remove the whole array when you're done with it, or roll your own data structure that supports it. Hopefully this is something that they'll address in the near future (a weak version of NSArray).
I've just faced with same problem and found that my before-ARC solution works after converting with ARC as designed. // function allocates mutable set which doesn't retain references. NSMutableSet* AllocNotRetainedMutableSet() { CFMutableSetRef setRef = NULL; CFSetCallBacks notRetainedCallbacks = kCFTypeSetCallBacks; notRetainedCallbacks.retain = NULL; notRetainedCallbacks.release = NULL; setRef = CFSetCreateMutable(kCFAllocatorDefault, 0, ¬RetainedCallbacks); return (__bridge NSMutableSet *)setRef; } // test object for debug deallocation #interface TestObj : NSObject #end #implementation TestObj - (id)init { self = [super init]; NSLog(#"%# constructed", self); return self; } - (void)dealloc { NSLog(#"%# deallocated", self); } #end #interface MainViewController () { NSMutableSet *weakedSet; NSMutableSet *usualSet; } #end #implementation MainViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization weakedSet = AllocNotRetainedMutableSet(); usualSet = [NSMutableSet new]; } return self; } - (IBAction)addObject:(id)sender { TestObj *obj = [TestObj new]; [weakedSet addObject:obj]; // store unsafe unretained ref [usualSet addObject:obj]; // store strong ref NSLog(#"%# addet to set", obj); obj = nil; if ([usualSet count] == 3) { [usualSet removeAllObjects]; // deallocate all objects and get old fashioned crash, as it was required. [weakedSet enumerateObjectsUsingBlock:^(TestObj *invalidObj, BOOL *stop) { NSLog(#"%# must crash here", invalidObj); }]; } } #end Output: 2013-06-30 00:59:10.266 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.267 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.581 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.582 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.881 not_retained_collection_test[28997:907] constructed 2013-06-30 00:59:10.882 not_retained_collection_test[28997:907] addet to set 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.883 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.884 not_retained_collection_test[28997:907] deallocated 2013-06-30 00:59:10.885 not_retained_collection_test[28997:907] * -[TestObj respondsToSelector:]: message sent to deallocated instance 0x1f03c8c0 Checked with iOS versions 4.3, 5.1, 6.2. Hope it will be useful to somebody.
If you need zeroing weak references, see this answer for code you can use for a wrapper class. Other answers to that question suggest a block-based wrapper, and ways to automatically remove zeroed elements from the collection.
If you use a lot this comportment it's indicated to your own NSMutableArray class (subclass of NSMutableArray) which doesn't increase the retain count. You should have something like this: -(void)addObject:(NSObject *)object { [self.collection addObject:[NSValue valueWithNonretainedObject:object]]; } -(NSObject*) getObject:(NSUInteger)index { NSValue *value = [self.collection objectAtIndex:index]; if (value.nonretainedObjectValue != nil) { return value.nonretainedObjectValue; } //it's nice to clean the array if the referenced object was deallocated [self.collection removeObjectAtIndex:index]; return nil; }
I think an elegant solution is what Mr. Erik Ralston propose on his Github repository https://gist.github.com/eralston/8010285 this are the essential steps: create a category for NSArray and NSMutableArray in the implementation create a convenience class with a weak property. Your category will assign the objects to this weak property. .h #import <Foundation/Foundation.h> #interface NSArray(WeakArray) - (__weak id)weakObjectForIndex:(NSUInteger)index; -(id<NSFastEnumeration>)weakObjectsEnumerator; #end #interface NSMutableArray (FRSWeakArray) -(void)addWeakObject:(id)object; -(void)removeWeakObject:(id)object; -(void)cleanWeakObjects; #end .m #import "NSArray+WeakArray.h" #interface WAArrayWeakPointer : NSObject #property (nonatomic, weak) NSObject *object; #end #implementation WAArrayWeakPointer #end #implementation NSArray (WeakArray) -(__weak id)weakObjectForIndex:(NSUInteger)index { WAArrayWeakPointer *ptr = [self objectAtIndex:index]; return ptr.object; } -(WAArrayWeakPointer *)weakPointerForObject:(id)object { for (WAArrayWeakPointer *ptr in self) { if(ptr) { if(ptr.object == object) { return ptr; } } } return nil; } -(id<NSFastEnumeration>)weakObjectsEnumerator { NSMutableArray *enumerator = [[NSMutableArray alloc] init]; for (WAArrayWeakPointer *ptr in self) { if(ptr && ptr.object) { [enumerator addObject:ptr.object]; } } return enumerator; } #end #implementation NSMutableArray (FRSWeakArray) -(void)addWeakObject:(id)object { if(!object) return; WAArrayWeakPointer *ptr = [[WAArrayWeakPointer alloc] init]; ptr.object = object; [self addObject:ptr]; [self cleanWeakObjects]; } -(void)removeWeakObject:(id)object { if(!object) return; WAArrayWeakPointer *ptr = [self weakPointerForObject:object]; if(ptr) { [self removeObject:ptr]; [self cleanWeakObjects]; } } -(void)cleanWeakObjects { NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init]; for (WAArrayWeakPointer *ptr in self) { if(ptr && !ptr.object) { [toBeRemoved addObject:ptr]; } } for(WAArrayWeakPointer *ptr in toBeRemoved) { [self removeObject:ptr]; } } #end
Getting error when added NSNumber to an array
When my program gets to the line: [userNumSequence addObject:[NSNumber numberWithInteger: sequenceNumber]]; it gets the error: Program received signal: “EXC_BAD_ACCESS”. All I'm wanting to do is to store an integer in the array. // JBNumberGeneration.m #import "JBNumberGeneration.h" #implementation JBNumberGeneration - (id) init{ if (self = [super init]){ userNumSequence = [NSMutableArray arrayWithCapacity:0]; } return self; } -(IBAction)logSequenceNumber:(id)sender{ NSString *titleOfButton = [sender title]; int sequenceNumber = [titleOfButton integerValue]; i=0; [userNumSequence addObject:[NSNumber numberWithInteger: sequenceNumber]]; //int currentNum = [((NSNumber*)[userNumSequence objectAtIndex: i]) integerValue]; //NSLog(#"%i", currentNum); int count = [userNumSequence count]; NSLog(#"Array size: %i", count); i++; } #end // JBNumberGeneration.h #import <Cocoa/Cocoa.h> #interface JBNumberGeneration : NSObject { IBOutlet NSTextField *displayLabel; int randNum; int level; int i; NSMutableArray* userNumSequence; } -(IBAction)logSequenceNumber:(id)sender; #end
EXC_BAD_ACCESS usually occurs when you try to access a member that has already been deallocated. Because you are calling [NSMutableArray arrayWithCapacity:] in your init function, it may have already been released by the time logSequenceNumber:(id)sender is called. Try adding #property (nonatomic, retain) NSMutableArray* userNumSequence to your #interface and #synthesize userNumSequence to your #implementation. Then call self.userNumSequence = [NSMutableArray arrayWithCapacity:0] in your init method. Don't forget to set it to nil in dealloc. EDIT: Also, just to be clear the Cocoa memory management naming standards are like this: If you call [[Object alloc] initSomehow], or [object retain] you are responsible for releasing it (calling init methods will automatically call retain). If you call methods like [Object objectWithSomething:something], these are usually autoreleased and will be released sometime in the future. You should never assume these exist beyond the scope in with they are created. According to the Cocoa documentation, scope includes the call stack. If a: calls b: which calls c:, and c: returns an autoreleased object, it can be passed safely all the way back up for a: to use. Beyond that it is released. This is at least my interpretation of the explanation of autorelease. If you need to use something for the lifetime of your object, retain it when you get it and release it in dealloc.