ObjectiveC allocation and init? - objective-c

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.

Related

Singleton set in AppDelegate loses it value when allocated in another class

I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.

How can I make a deep copy in 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.

Potential problems in objective-c code

Here is a small piece of code. Posted by Russian company Yandex as a part of their interview. What are potential problems here? It looks very simple, should be hidden problems I can not see.
First header
//Foo.h
#import <Cocoa/Cocoa.h>
#interface Foo : NSObject
{
NSString* str;
static int i = 0;
}
- (NSString*) str;
#end
Another file
//Foo.m
#import "Foo.h"
#implementation
- (id) init
{
return [self initWithStr:"number:" someInt:6];
}
- (id) initWithStr:(NSString*)theStr someInt:(int)value
{
self = [super init];
str = [NSString stringWithFormat:#"%#%d", theStr, value];
return self;
}
- (NSString*) str
{
return str;
}
- (void) setStr:(NSString*)theStr
{
str = theStr;
}
#end
And the last file
//main.m
#import <Cocoa/Cocoa.h>
#import "Foo.h"
int main(int argc, char *argv[])
{
Foo objA;
NSLog([objA str]);
[objA setStr:#"hello world!"];
NSLog([objA str]);
Foo* objB = [[Foo alloc] init];
Foo* objC = [[Foo alloc] initWithStr:#"My magic number:" value:265];
objB = objC;
NSLog([objB str]);
[objA release];
[objB release];
[objC release];
return 0;
}
In another file:
#implementation
implementation of what? must specify.
In the last file:
Foo objA;
NSLog([objA str]);
[objA setStr:#"hello world!"];
NSLog([objA str]);
This will crash, local variable Foo objA is not initialized, it would be fine it was set to nil, since messages to nil are ok in objective c but it is not.
Here:
[objA setStr:#"hello world!"];
That method will give a compile warning since that method is not declared in the interface, but it will still call the method.
Here:
- (id) init
{
return [self initWithStr:"number:" someInt:6];
}
Missing # for the string #"number:"
Here:
objB = objC;
You just leaked objB, since there is now no valid reference to release the previous allocation.
[objA release];
This was never allocated!
[objB release];
[objC release];
The second one will crash since they both refer to the same object, and the retain count is only 1.
The first file also has some potential issues such as declaring a method that appears to be a getter without declaring a property for the ivar, same with the setter, would be better to just declare a property.
#interface Foo : NSObject
{
NSString* str;
static int i = 0;
}
You cann't define static int i = 0; here. Type name does not allow storage class to be specified Foo.h
Also, the setter needs to release the previous string and retain the new one.
- (void) setStr:(NSString*)theStr
{
if(str) {
[str release];
}
str = [theStr retain];
}

Error: "Unrecognized selector" when using addObjects: on an NSMutableArray

When executing
[self.blockViews addObject:curBlockView];
I get an error
2011-07-01 13:35:26.240 Block Breaker[42061:207] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x4e037a0
I am pretty new to Objective-C. Is it something in my init method?
//
// GameEngine.h
// Block Breaker
//
// Created by Chris Muench on 7/1/11.
// Copyright 2011 N/A. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface GameEngine : NSObject {
NSMutableArray *blockViews;
int numBlockRows;
int score;
}
#property (nonatomic, copy) NSMutableArray *blockViews;
#property int numBlockRows;
#property int score;
- (void) setup;
- (void) setupBlocks;
#end
//
// GameEngine.m
// Block Breaker
//
// Created by Chris Muench on 7/1/11.
// Copyright 2011 N/A. All rights reserved.
//
#import "GameEngine.h"
#import "Block.h"
#import "BlockView.h"
#implementation GameEngine
#synthesize blockViews;
#synthesize numBlockRows;
#synthesize score;
- (id) init
{
if ((self = [super init]))
{
self.blockViews = [[NSMutableArray alloc] init];
self.numBlockRows = 2;
self.score = 0;
}
return self;
}
- (void) setup
{
[self setupBlocks];
}
- (void) setupBlocks
{
float blockWidth = 10;
float blockHeight = 10;
float rowSpacing = 2;
float colSpacing = 2;
float currentX = 0;
float currentY=10;
float screenWidth = 200;
for (int rowCounter=0;rowCounter<self.numBlockRows;rowCounter++)
{
while(currentX <=screenWidth)
{
Block *curBlock = [[Block alloc] initWithWidth:blockWidth height:blockHeight];
BlockView *curBlockView = [[BlockView alloc] initWithFrame:CGRectMake(currentX, currentY, curBlock.width, curBlock.height)];
curBlockView.block = curBlock;
[self.blockViews addObject:curBlockView];
currentX+=blockWidth+colSpacing;
[curBlock release];
[curBlockView release];
}
currentX=0;
currentY+=blockHeight+rowSpacing;
}
}
#end
When you copy an NSMutableArray using the copy method, or a synthesized setter for which the property was specified as copy, you get an immutable copy, which means you essentially end up with a plain NSArray.* There is a method mutableCopy which will preserve the mutability, but I don't believe there's any way to specify that for a property.
If you want your array to be mutably copied when you set it, you'll have to write your own setter method, and specify that method in the property declaration.
#property (nonatomic, copy, setter=setBlockViewsByCopying) NSMutableArray * blockViews;
- (void) setBlockViewsByCopying: (NSMutableArray *)newBlockViews {
NSMutableArray * tmp = [newBlockViews mutableCopy];
[blockViews release];
blockViews = tmp;
}
A side note, as #InsertWittyName mentioned in a comment, your code initializing blockViews will create a leak, because you have two claims of ownership on the array you're creating -- one for the alloc and one for the retain or copy that you get using the property:
self.blockViews = [[NSMutableArray alloc] init];
// ^ One claim ^ Two claims
// Only one release later, when the property is set again!
// LEAK!
You should instead do:
self.blockViews = [NSMutableArray array];
// Creates an object you don't own which you then make one claim
// on using the setter
*Though as it is a class cluster, you really get some unspecified concrete subclass.
Copy should retain, If I remember correctly.
Anyway using copying cannot be optimal, every time you call the accessor (set), you got a copy, and, as well pointed out above, is immutable.
Adding is fine, but remember to release the array when done.
Did you retain your array ??

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.