Best practice when implementing copyWithZone: - iphone

I am trying to clear up a few things in my head about implementing copyWithZone:, can anyone comment on the following ...
// 001: Crime is a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
Crime *newCrime = [[[self class] allocWithZone:zone] init];
if(newCrime) {
[newCrime setMonth:[self month]];
[newCrime setCategory:[self category]];
[newCrime setCoordinate:[self coordinate]];
[newCrime setLocationName:[self locationName]];
[newCrime setTitle:[self title]];
[newCrime setSubtitle:[self subtitle]];
}
return newCrime;
}
// 002: Crime is not a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
Crime *newCrime = [super copyWithZone:zone];
[newCrime setMonth:[self month]];
[newCrime setCategory:[self category]];
[newCrime setCoordinate:[self coordinate]];
[newCrime setLocationName:[self locationName]];
[newCrime setTitle:[self title]];
[newCrime setSubtitle:[self subtitle]];
return newCrime;
}
In 001:
Is it best to write the class name directly [[Crime allocWithZone:zone] init] or should I use [[[self Class] allocWithZone:zone] init]?
Is it ok to use [self month] for copying the iVars or should I be accessing the iVars directly i.e. _month?

You should always use [[self class] allocWithZone:zone] to make sure you are creating a copy using the appropriate class. The example you give for 002 shows exactly why: Subclasses will call [super copyWithZone:zone] and expect to get back an instance of the appropriate class, not an instance of the super class.
I access the ivars directly, so I don't need to worry about any side effects I might add to the property setter (e.g., generating notifications) later on. Keep in mind, subclasses are free to override any method. In your example, you are sending two extra messages per ivar. I would implement it as follows:
Code:
- (id)copyWithZone:(NSZone *)zone {
Crime *newCrime = [super copyWithZone:zone];
newCrime->_month = [_month copyWithZone:zone];
newCrime->_category = [_category copyWithZone:zone];
// etc...
return newCrime;
}
Of course, whether you copy the ivars, retain them, or just assign them should mirror what the setters do.

The default copy behavior of copyWithZone: method with SDK provided objects is "shallow copy". That means if you call copyWithZone: on NSString object, it will create a shallow copy but not deep copy. Difference between shallow and deep copy are :
A shallow copy of an object will only copy the references to the objects of the original array and place them into the new array.
A deep copy will actually copy the individual objects contained in the object. This done by sending each individual object the copyWithZone: message in your custom class method.
INSHORT : To get shallow copy you call retain or strong on all instance variables. To get deep copy you call copyWithZone: on all instance variables in your custom class copyWithZone: implementation. Now it's your choice to choose.

How about this one that implement deep copy:
/// Class Foo has two properties: month and category
- (id)copyWithZone:(NSZone *zone) {
Foo *newFoo;
if ([self.superclass instancesRespondToSelector:#selector(copyWithZone:)]) {
newFoo = [super copyWithZone:zone];
} else {
newFoo = [[self.class allocWithZone:zone] init];
}
newFoo->_month = [_month copyWithZone:zone];
newFoo->_category = [_category copyWithZone:zone];
return newFoo;
}

This is my model.
#import <Foundation/Foundation.h>
#interface RSRFDAModel : NSObject
#property (nonatomic, assign) NSInteger objectId;
#property (nonatomic, copy) NSString *name;
#property (nonatomic, strong) NSArray<RSRFDAModel *> *beans;
#end
#import "RSRFDAModel.h"
#interface RSRFDAModel () <NSCopying>
#end
#implementation RSRFDAModel
-(id)copyWithZone:(NSZone *)zone {
RSRFDAModel *model = [[[self class] allocWithZone:zone] init];
model.objectId = self.objectId;
model.name = self.name;
model.beans = [self.beans mutableCopy];
return model;
}
#end

Related

To define and use convenient constructors without defining init methods is bad practice?

Normally, you define init or initWith... methods and call them inside convenient constructors like this:
#implementation MyClass1 : NSObject
#synthesize n, s;
-(id)init
{
self = [super init];
if (self) {
self.n = 1;
}
return self;
}
-(id)initWithString:(NSString *)s
{
self = [self init];
if (self) {
self.s = s;
}
return self;
}
+(id)myClass
{
return [[self alloc] init];
}
+(id)myClassWithString:(NSString *)s
{
return [[self alloc] initWithString:s];
}
#end
But I think it is possible to define convenient constructors without defining init or initWith... methods like this:
#implementation MyClass2
#synthesize n, s;
+(id)myClass
{
MyClass2 *obj = [[self alloc] init];
obj.n = 1;
return obj;
}
+(id)myClassWithString:(NSString *)s
{
MyClass2 *obj = [self myClass];
obj.s = s;
return obj;
}
#end
Is it bad practice to define convenient constructors without defining init method?
If it is bad practice, could you tell me the disadvantage or problems?
I'm not sure if it's actually a bad practice. Generally, when I write convenience constructors they look like this:
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
{
FSClub *club = [[FSClub alloc] init];
if (club)
{
club.identifier = [element integerValueWithPath:#"id"];
club.name = [element valueWithPath:#"naam"];
club.referer = [element URLWithPath:#"referer"];
}
return club;
}
The code still takes into account possible memory issues (initialisation failure) like in a 'normal' init constructor. Values will only be set if initialisation is successful.
The interface file is defined as such:
#interface FSClub : NSObject
#property (nonatomic, assign, readonly) NSInteger identifier;
#property (nonatomic, copy, readonly) NSURL *referer;
#property (nonatomic, copy, readonly) NSString *name;
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
#end
Please note the properties are readonly. I prefer creating immutable objects, since they're easier to deal with in e.g. threaded code. The only way to have the properties set in this situation is by using the convenience constructor.
When I create the convenience constructors, these are generally the only methods I use to instantiate the objects. That is to say, the -init method will not be used most of the time. Also, writing lots of initialisers even if you don't use them takes lots of developer time. I wouldn't create methods that I don't use.
When you create code as part of a framework -code that you share with lots of people that you don't know- in such situations you might want to write both the convenience constructors as well as all the normal constructors, because you can't be sure how the code will be used in people's own project. For example creating lots of objects using convenience constructors in tight loops might be bad for performance, since the objects are added to the autorelease pool. I think this is also true in an ARC scenario. In such situations one might have the option to use 'normal' constructors to create objects.
//I think it is BAD. First of all, you confused CLASS and OBJECT, you get an object like this
// [[CLASSNAME alloc] init];
//not like this:
// [[OBJECT alloc] init];
//so, what you want to do shall be like this:
// +(id)myClass
// {
// MyClass2 *obj = [[[self class] alloc] init];
// obj.n = 1;
// return obj;
// }
PLEASE ignore above all, thanks to #W'rkncacnter.
however, here you are returning an object owned by yourself without autorelease, it's not recommended.
Maybe what you really want is something like factory method?
+(id)myObjectWithString:(NSString *)string
{
MyClass2 *obj = [[[MyClass2 alloc] init] autorelease];
obj.s = string;
return obj;
}
Doing it like you did with MyClass1 makes it easier to define a designated initializer. Apple recommends these; they help reducing code repetition.

Correct way to create/use a Singleton NSMutableArray for Xcode 4

I've reviewed (and tried) a bunch of the threads here regarding Singletons and NSMutableArrays. I'm new to Objective-C so please bear with me.
I simply want to create a few arrays that can be accessed from any view/.m file.
What is the best (or most concise) coding for a Singleton?
Below is what I have now and I get
1 warning at .m '#implementation' - "Incomplete implementation"
1 error at usage in a view .m file - "initializer element is not a compile-time constant"
This is the code I have now - my GlobalData.h file:
#import <Foundation/Foundation.h>
#interface GlobalData : NSObject {
NSMutableArray *listOfHeadings;
NSMutableArray *listOfItems1;
NSMutableArray *listOfItems2;
}
#property(nonatomic,retain)NSMutableArray *listOfHeadings;
#property(nonatomic,retain)NSMutableArray *listOfItems1;
#property(nonatomic,retain)NSMutableArray *listOfItems2;
+(GlobalData*)getInstance;
#end
My GlobalData.m file:
#import "GlobalData.h"
#implementation GlobalData
#synthesize listOfHeadings;
#synthesize listOfItems1;
#synthesize listOfItems2;
static GlobalData *instance=nil;
+(GlobalData *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [GlobalData new];
}
}
return instance;
}
#end
And in a view .m file (simplified):
#import GlobalData.h
GlobalData *globDat=[GlobalData getInstance]; //error occurs here
Can someone point out the trouble and if there's better coding, please enlighten me - thanks!
EDIT
Here's a few links I've tried to use:
Can i have a single NSMutableArray in my multiple views application?
iPhone help with singleton class
In this case, you might be doing more than you have to. Granted this certainly isn't always the best solution - but you can put your NSMutableArray as a property in your App Delegate and then easily refer to it from any view. By doing it this way - you aren't locking it in as a 'singleton' but there is a 'singleton instance' of it (this helps a great deal for testability).
I have simplified this process here:
YourAppDelegate.h
#property (nonatomic,retain) NSMutableArray *myArray;
YourAppDelegate.m
#synthesize myArray;
YourViewController.m
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *myArrayFromAppDelegate = appDelegate.myArray;
From this point - you can do any manipulation on this value.
Here's the "modern" version of a single method to turn any class into a Singleton (in this case formatted as a code snippet). It works in iOS4.x or higher:
+(<#SingletonClassName#> *) sharedInstance
{
static <#SingletonClassName#> *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] init];
});
return _sharedClient;
}
But, do you really need a singleton of a single NSMutableArray? You could use the built-on singleton - your application delegate, which is got to by calling:
MyAppDelegate * appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.myMutableArray addObject:...];
The error initializer element is not a compile-time constant is not related to how you create your singleton. The error is how you are accessing your singleton. You are doing this outside of a function:
GlobalData *globDat=[GlobalData getInstance];
This means that you are trying to initialize a global variable (globDat) as the value of the expression [GlobalData getInstance]. You can only initialize global variables to expressions that are "compile-time constants". That means things like 0 or "fred" or 8/2. The value of [GlobalData getInstance] cannot be computed at compile-time, so it cannot be used to initialize the global variable.
Instead, you need to just use [GlobalData getInstance] inside your function bodies wherever you are currently trying to use the globDat variable.
As for the warning, Incomplete implementation, I don't see what's missing. Perhaps you didn't post all of the code from GlobalData.h. Anyway, you should be able to click the warning (where it appears on the right side of the editor window) and have Xcode show you what's missing.
This is the way I create my Singleton:
Singleton.h
#import <Foundation/Foundation.h>
#interface Singleton : NSObject {
NSMutableArray *firstMutableArray;
NSMutableArray *secondMutableArray;
}
#property (nonatomic, retain) NSMutableArray *firstMutableArray;
#property (nonatomic, retain) NSMutableArray *secondMutableArray;
+ (id)sharedSingleton;
#end
Sigleton.m
#import "Singleton.h"
static Singleton *sharedMySingleton = nil;
#implementation Singleton
#synthesize firstMutableArray;
#synthesize secondMutableArray;
#pragma mark Singleton Methods
+ (id)sharedSingleton {
#synchronized(self) {
if (sharedMySingleton == nil) {
sharedMySingleton = [[super allocWithZone:NULL] init];
}
return sharedMySingleton;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedSingleton] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
- (oneway void)release {
// Never release
}
- (id)autorelease {
return self;
}
- (id)init {
if (self = [super init]) {
firstMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
secondMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
}
return self;
}
- (void)dealloc {
[firstMutableArray release];
[secondMutableArray release];
[super dealloc];
}
#end
Then, when you want to call your Singleton:
#import "Singleton.h"
Singleton *singleton = [Singleton sharedSingleton];
singleton.firstMutableArray = ...
singleton.secondMutableArray = ...

Passing a pointer by reference to class and using it

I am trying to pass a pointer by reference to an object from class A to class B. In class B I want to assign this pointer to a ivar and read and write to it.
This is the code that gives me errors (does not matter what errors). This is my first try with pointers so please correct my understanding.
Class A
//This is the parameter I would like to pass as a pointer and be able to manipulate from class B
NSString *name = #"Cyprian";
-(void)passAParameter{
ClassB *classB = [[ClassB alloc] initWithAPointer:&name];
...
}
Class B
// ClassB.h
#interface ClassB{
NSString **nameFromClassA;
}
#property(nonatomic,assign)NSString **nameFromClassA;
-(id)initWithAPointer:(NSString **)name;
// ClassB.m
#implementation ClassB
#synthesize nameFromClassA;
-(id)initWithAPointer:(NSString **)name{
*nameFromClassA = *name;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", *nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
*nameFromClassA = newName;
}
Please, do not use double pointers here. You shouldn't handle things like that.
This is a simpler approach:
In the ClassA instance:
-(void)passAParameter{
NSString *name = #"Cyprian";
ClassB *classB = [[ClassB alloc] initWithAPointer:name];
...
}
While you define ClassB this way:
ClassB.h:
#interface ClassB{
NSString *nameFromClassA;
}
#property(nonatomic,retain) NSString *nameFromClassA; // Retaining it will give you less headaches
-(id)initWithAPointer:(NSString *)name;
#end
ClassB.m:
#implementation ClassB
#synthesize nameFromClassA;
// You should release all retained object when deallocating self
- (void) dealloc {
[nameFromClassA release];
nameFromClassA = nil;
[super dealloc];
}
-(id)initWithAPointer:(NSString *)name{
if ((self = [super init])) { // Always init the object from super!
self.nameFromClassA = name; // Retain the object calling self.
}
return self;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
self.nameFromClassA = newName; // Retain it calling self.
}
#end
The assignment in your initWithAPointer: method should be just:
nameFromClassA = name;
That said, this code pattern smells of a bad design. What high-level goal is it that you're trying to accomplish?

singleton class in objective-C

I want to have one object that is initialized in the delegate and I want to be able to use this object anywhere across view controllers (doesn't depend on what view I am currently at). I am guessing the solution to this would be to have a singleton class, so far I have the following:
#interface LocationManager : NSObject <CLLocationManagerDelegate>{
NSDate *enter;
NSDate *exit;
CLLocationManager * manager;
}
#property (nonatomic, retain) NSDate * enter;
#property (nonatomic, retain) NSDate * exit;
- (BOOL)registerRegionWithLatitude:(double)latitude andLongitude:(double)longitude;
+ (LocationManager *)instance;
#end
#import "LocationManager.h"
#implementation LocationManager
#synthesize enter;
#synthesize exit;
#pragma mark - CLLocationManager delegate
static LocationManager *gInstance = NULL;
+ (LocationManager *)instance
{
#synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
#end
Is this correct? So all I need to do to access this is just to call instance? Inside LocationManager I also want to have only one CLLocationManager, called manager.. however, where do I initialize it so I only have one? Can I do the following? Most other singleton examples doesn't have any variables in the class, so that's where I got confused
+ (LocationManager *)sharedLocationManager
{
#synchronized(self)
{
if (lm == NULL){
lm = [[self alloc] init];
lm.manager = [[CLLocationManager alloc] init];
lm.manager.delegate = lm;
}
}
return(lm);
}
Basically -- yes.
Just a couple of small things:
static LocationManager *gInstance = NULL;
instead of NULL, you should use nil, it's a convention in Objective-C.
You should also overwrite alloc, new, copyWithZone:, and mutableCopyWithZone:. From Buck/Yacktman: "Cocoa Design Patterns", p. 153:
+ (id)hiddenAlloc
{
return [super alloc];
}
+ (id)new
{
return [self alloc];
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
[self retain];
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [self copyWithZone:zone];
}
This way, your singleton object cannot be copied. You need to call hiddenAlloc from your instance method (by the way, the method to access a Singleton object is often called sharedInstance in Objective-C).
For other singleton styles with their pros and cons, check out this question.
Personally, I prefer this style (copied from one of the answers on that link):
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
In fact, there's a tried-and-true method to create singletons already. Download the SynthesizeSingleton.h file (from a Cocoa with Love article). It contains a massive amount of pre-processor code which will generate any singleton for you. Hit the article for more details.
Since the factory method "instance" is a class-level method, the #synchronized block should be
#synchronized([LocationManager class]) {
//}

iphone - trying to understand #property

Suppose I have two classes. In the first one I declare this in Class1.h
#interface Class1 : UIViewController {
NSString *myString;
id myObject;
}
On the second class I go beyond that I declare it like
#interface Class2 : UIViewController {
NSString *myString;
id myObject;
}
#property (nonatomic, retain) NSString *myString;
#property (nonatomic, retain) id myObject;
and then I #synthesize myString, myObject on Class2.m
Then, on my main program, I create two objects: one based on Class1 and another one based on Class2.
What effect the #property of class2 will have? Will every value assigned to both values on Class2 be always retained? If so, do I need to "release" them? How?
Thanks.
Please read Declared Properties section of The Objective-C programming language
for a full explanation on properties ;)
In Class2:
In this case you set retain attribute to your property it is supposed to be retained in the implementation. This is done automatically when you synthesize a property.
This means that you should have
- (void) dealloc{
[myString release];
[myObject release];
[super dealloc];
}
and everything should be fine
In Class1, you don't have properties so myString and myObject is not visible from outside. But this does not mean that you shouldn't release them. It depends on the way you initialize them and/or if you send retain messages to them.
BTW, if you set assign a property you don't release it, just set it to nil in the dealloc method. If you set copy to it then you must release it.
EDIT
You said: *But suppose I have this *
#property (nonatomic, retain) UIView *myView;
and
myView = [[UIView alloc] initWithFrame:myFrame];
[self.view addSubview:myView];
[myView release];
? I am already releasing myView... do I have to release it again???
First, since you have your property defined that way, you should have dealloc method as:
- (void) dealloc{
[myView release];
[super dealloc];
}
So, the answer is NO you should not release it but actually is not correct.
Please take a look:
myView = [[UIView alloc] initWithFrame:myFrame]; //myView retainCount is 1
[self.view addSubview:myView]; //retainCount is 2
[myView release]; //retainCount is 1 again
later in dealloc method
- (void) dealloc{
[myView release]; // retainCount becomes 0, is deallocated
[super dealloc]; // subviews of self.view are released but myView was already deallocated!, so you have over released myView once ;(
}
This is the correct way: (Use your properties ;) )
UIView *aView = [[UIView alloc] initWithFrame:myFrame]; // init, retainCount is 1
self.myView = aView; // retainCount becomes 2
[aView release]; // retainCount becomes 1 again and we are fine.
[self.view addSubview:self.myView]; //retainCounts becomes 2 again.
even if it is 2 there is no problem because when self.view is deallocated its subviews also will be released. Hence self.myView retainCount will become 1 again later when self is deallocated.
- (void) dealloc{
[myView release]; //retainCounts becomes 1
[super dealloc]; // all its subviews of self.view are released hence myView retaincount becomes 1 and is released corretly
}
What is the difference?
Suppose self.myView is also retained by other object X and with the former approach, X's view will be pointing to an invalid object, because it was already released.
Hope it helps
EDIT2
As bbum's indication, this is a mini-mini-short tutorial on properties:
when you have
#property (... retain) NSObject *retainVar;
#property (... assign) NSObject *assignVar;
#property (... copy) NSObject *copyVar;
and you #synthesize them
is like having the following setters:
// retain
-(void)setRetainVar:(NSObject *)var {
if (retainVar != var) {
[retainVar release];
retainVar = [var retain];
}
}
//assign
-(void)setAssignVar:(NSObject *)var {
assignVar = var;
}
//copy
-(void)setCopyVar:(NSObject *)var {
if (copyVar != var) {
[copyVar release];
copyVar = [var copy];
}
}
(this means that if you assign directly an object you have to make sure is something equivalent to above setters, from the memory management point of view)
and your dealloc method should be something like:
- (void) dealloc{
[retainVar release];
assignVar = nil;
[copyVar release];
[super dealloc];
}
When setting your ivars
for example, inside of init:
- (id) init{
if ((self = [super init])){
//this is ok
retainVar = [[NSObject alloc] init];//but is retainVar was not nil we will have a leak ;(
//This is better
NSObject *obj = [NSObject alloc] init];
self.retainVar = obj;
[obj release];
//this is BAD
assignVar = [[NSObject alloc] init];//because this is like retaining it, later it will leak
//below is correct
NSObject *obj = [[[NSObject alloc] init] autorelease];
assignVar = obj;
//copy is pretty much like retain,
//this is ok
copyVar = [[NSObject alloc] init]; //but, if copyVar was not nil is a leak!
//below is better
NSObject *obj = [NSObject alloc] init]:
self.retainVar = obj;
[obj release];
}
return self;
}
Apple's "Learning Objective C - A Primer" tells you about that and more:
http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/