shallow copy and deeply copy in object-c - iphone

I have question about "copy" in objective-c, my function in Car.m is like this:
- (id) copyWithZone: (NSZone *) zone
{
Car *carCopy;
carCopy = [[[self class] allocWithZone: zone] init];
carCopy.name = self.name; //not [name copy]
return (carCopy);
} // copyWithZone
int the main.m:
Car *car = [[Car alloc] init];
car.name = #"abc";
Car *copyCar = [car copy];
[copyCar print];
car.name = #"123";
[copyCar print];
for the property name , i think both the car and copyCar point to the same NSString Object so that when I do "car.name = #"123" " , copyCar.name should also be changed. But when i print the copyCar twice as in the code, they both print the name as "abc". I want to know why it is not a shallow copy ?

Two things:
1) they may both start out pointing to the same NSString, but when you do car.name = #"123", you are now making car.name point someplace else. You are changing the pointer, not the object it points to, and changing the pointer in car does not affect the pointer in copyCar. If you (hypothetically) did something like [car.name appendString:#"xxx"], that would change the object. But, you can't, because
2) NSStrings are immutable -- you can't change the NSString object once it's been created

You're not mutating the string for the name property. You're replacing it outright. Since car and copyCar are not, in fact, the same object, assigning to car.name cannot possibly affect copyCar.name.

Both car and copyCar point to the same string initially, but they both have different references to the same string. If you change the reference in one object, it doesn't affect the reference in the other object, so that's how car can point to the new string but not the carCopy.

Related

creating an object out of multiple variable types

I have an NSDictionary that I am passing to a NSObject Class where I pull all of the values out of the dictionary and pass them into their correct types.
For instance NSInteger, BOOL, NSString, char are the types of values I am pulling out of the NSDictionary and putting into their only variables.
My question what is the best way to turn these values into one big object that can then be putt into an array?
I have heard that I can use the class itself as an Object.. But I am not really sure how to do this.
or could I just put them back into a NSDictionary?... but if thats the case do NSDictionaries allow for multiple value types?
Actually, you are in the right path. this is basically MVC architecture way. so you are questioning about M = Model.
Model in here example is class that defines all variables. cut to the point, here's you should do:
-> create a class that contain your variable, with #property & #synthesize name : ClassA.
then you could set object ClassA into dictionary.
ClassA *myClass = [[ClassA alloc] init];
myClass.myString = #"test String";
myClass.myBoolean = True;
[dictionary setObject:myClass forKey:#"myObject"];
[myClass release]; //we no longer need the object because already retain in dictionary.
then retrieve it by :
ClassA *myClass = (ClassA*)[dictionary objectForKey:#"myObject"];
NSLog(#"this is value of myString : %# & boolean : %i",myClass.myString,myClass.myBoolean);
You can put the collection of values in NSDictionary, NSArray, NSMutableArray, etc. Any of the collection types. Or if you have a fixed number of values/types, you can create a class that has a data member for each value/type and put it in that. The class solution would eliminate having to do a lot of casting, but it also only works if there are a fixed number of values/types.
Here is the AppleDoc on collections.
Yeah, You can create a class itself with these as different values as a properties.Once you pass this object to any class you can access those values there by obj.propertyName.Doing by this Lead to create Modal in MVC pattern.
Please let me know if there is doubt.
Test *object = [[Test alloc] init];
object.testBool=true;
object.testString=#"Test";
NSDictionary *passData = [[NSDictionary alloc] initWithObjectsAndKeys:object,#"testObject", nil];
Test *getObject = (Test*)[passData objectForKey:#"testObject"];
NSLog(#"%d",getObject.testBool);
NSLog(#"%#",getObject.testString);
You can customized the init method of Test Class.

Store an object into NSMutableArray

Please, correct me, where i'm wrong (i'm a begginer)
I want to store a class objects into NSArray. For example:
MySimpleClass *mscObj = [[MySimpleClass alloc] initWithSomething:#"something"];
NSMutableArray *myarr = [[NSMutableArray alloc] init];
[myarr addObject:mscObj];
mscObj = #"somethingelse";
And then my myarr index 0 change from #"something" to #"somethingelse". Why? Can i store a copy only to array?
EDIT:
In MySimpleClass:
#import <Foundation/Foundation.h>
#interface MySimpleClass : NSObject {
}
#property (strong,nonatomic) NSString *objectName;
#property (strong,nonatomic) NSString *objectTarget;
-(void)addName:(NSString*)name;
-(void)addTarget:(NSString*)target;
#end
.m file
#import "MySimpleClass.h"
#implementation MySimpleClass
#synthesize objectName;
#synthesize objectTarget;
-(void)addName:(NSString*)name{
self.objectName = name;
}
-(void)addTarget:(NSString*)target{
self.objectTarget = target;
}
-(void)flushAll {
self.objectTarget = nil;
self.objectName = nil;
}
#end
Then In other class i have:
MySimpleClass *mscObj = [[MySimpleClass alloc] initWithSomething:#"something"];
[myarr addObject:[mscObj copy]];
int testunit = [myarr count];
for(int i=0;i<testunit;i++) {
MySimpleClass *myelement = [myarr objectAtIndex:i];
NSLog(#"%# : %#",myelement.objectName,myelement.objectTarget);
}
Given the code you've posted, the first object (index 0) in the array won't change due to the fourth line, mscObj = #"something else";. That line changes the value of the pointer mscObj itself, so that it'll point to a completely different object. If the object in the array is changing, I believe that the real code you're using won't quite match what you've posted -- please check that.
However, if you use the mscObj pointer to change an attribute of the object that it points to, then you'll have changed the object in the array:
mscObj.something = #"somethingelse";
Here you're changing the something property of the object that mscObj refers to, but you're not changing the value of mscObj. This will change the contents of the object in the array, since that's the same object that mscObj points to.
Try:
[myarr addObject:[[mscObj copy] autorelease]];
When you do this
[myarr addObject:mscObj];
myarr is retaining a reference to mscObj. So any time mscObj is changed myarr will reflect that change.
When you do this
[myarr addObject:[mscObj copy]]; // I've left the autorelease out for simplicity
myarr retains a reference to a new copy of mscObj. When mscObj is then updated myarr will not reflect the change because it's reference points to a completely different object.
EDIT
For copy to work your class ** MySimpleClass** needs to implement NSCopying. See this SO answer for help on that.
It is because of the pointers. Your object at index:0 remains at the same location in the memory.It will always change.
try releagind the mscObj after adding the array and create a new MySimpleClass object with #"somethingelse"
mscObj is a reference and what the array stores are references. So if you manipulate a object from outside the container the reference reflects the changes. Think about the by value by reference difference.

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

I have a Objective C function that takes any type of object by reference. But when i pass a NSMutableArray My function does not recognise It

I have a function That takes by reference any kind of object
-(BOOL)RemoteCall:(id**)DataClass;
in the implementation i use [*DataClass isMemberOfClass:[NSMutableArray class] to find out the type of the object. The problem is it does not work with NSMUtableArrays Does anybody have a solution to this problem ? Here is the relevant code:
Implementation:
-(BOOL)RemoteCall:(id**)DataClass
{
if([*DataClass isMemberOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)*DataClass;
//do something with SW
DataClass= (id**)SW;
return TRUE;
}
}
Any help and I mean anything at all will be appreciated, I'm stuck.
Method Call:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]
Services * serv = [[Services alloc] init];
return [serv RemoteCall:&channelArray];
Pass by reference in Objective-C is almost never the right way.
There are a number of problems with that code.
(id**) is a pointer to a pointer to a pointer to an object. Probably not at all what you want.
YES and NO are BOOL return types; not TRUE
there is no reason in that code to be returning something by reference.
method names start with lower case letters. Arguments do, too.
There will never be an instance of NSMutableArray in an application; just subclasses
You can't tell the difference between a mutable and immutable array in the first place; check for isKindOfClass: or isMemberOfClass: for an NSMutableArray won't do you much good (it is useful, but misleading).
This is better:
-(BOOL)remoteCall: (id) dataThing
{
if([dataThing isKindOfClass:[NSMutableArray class]] == YES)
{
NSMutableArray *swArray = dataThing; // not strictly necessary, but good defensive practice
//do something with swArray
return YES;
}
return NO;
}
To be called like:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]; // you'll need to release this somewhere
Services * serv = [[Services alloc] init];
return [serv remoteCall:channelArray];
Since you don't return a different array in remoteCall:, channelArray's contents will be manipulated by the method and the YES/NO return value.
If there is some reason why the above seemingly won't work for you, please explain why.
Note: The code obviously requires an NSMutableArray if you are going to muck with the contents. The isKindOfClass: could be checking for NSMutableArray or NSArray and it wouldn't matter either way. When using arrays in your code and requiring a mutable array, it is up to you to make sure the data flow is correct such that you don't end up w/an immutable array where you need a mutable array.
If you don't need to reassign your variable, then don't use this. id or NSObject * is just fine and works by reference anyway. id * or NSObject ** would be references. id ** doesn't make sense at all here.
Also, learn naming conventions (like upper/lowercase).
NSArray is a class cluster. That means that every NSArray instance is actually an instance of some subclass. Only isKindOfClass: is useful for class-membership testing with class clusters.
Also... thats horrible code - please accept this:
-(BOOL)remoteCall:(id)dataClass {
if([dataClass isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *sw =(NSMutableArray *)dataClass;
return YES;
}
}
that should work.
Constructive critisism of coding: You need to adhere to coding conventions. Although your code will work... its not brilliant to read and theres a lot of unnecessary *s and such.
Function names should be camel coded with a preceeding lower-case letter as should variable names. Passing (id) into a function doesn't require *s at all. Objects you pass into a function only available throughout the scope of the method anyway and that method doesn't own it, I'm not sure what you're trying to do with all the extra *s, but just treat objects you pass into the method as if you don't own them. :)
As Eiko said before, i'd use just id and not double pointers to ID.
I'm also pretty sure that isMemberOfClass is your Problem. isMember does not check for inheritance, so you're only asking for Top level Classes. isKindOfClass is probably the better choice, as there is no guarantee that Apple doesn't use an internal subclass of NSMutableArray internally. Check the Apple Docs.
i'd write it as such:
-(BOOL)RemoteCall:(id)dataClass
{
if([dataClass isKindOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)dataClass;
//do something with SW
return TRUE;
}
}

Declaring an object type in an if statement

I'm trying to declare a variable inside an if statement. If the result of a query is YES then the object will be of one type, otherwise it will be of another type. A bit like this...
if (YES) {
ObjectTypeA *object = [[ObjectTypeA] alloc] init];
}
else {
ObjectTypeB *object = [[ObjectTypeB] alloc] init];
}
Once that's done I want to use object with the same methods no matter what type it is. I tried declaring object as an id before the if statement but get an error: member reference type 'struct objc_object *' is a pointer; maybe you meant to use '->'?
I also tried declaring both to be separate objects outside the if and then make the pointer point to whichever it was once I knew. That wouldn't work either.
I know that the compiler is trying to protect me from myself by doing this but in this circumstance I need a way round it please.
Thanks.
The most common pattern for this problem in Cocoa/Cocoa Touch is to define a protocol.
A protocol is a collection of methods that can be implemented by any object.
If you make ClassA and ClassB conform to a protocol containing the methods you need them to respond to then you don't need to worry about which type of object you get.
The idea is "if it looks like a duck and quacks like a duck, then it's probably a duck".
You can use dynamic typing and create your objects depending on the outcome of your query, but ensure that the resulting object conforms to a particular protocol, like so:
id <MyProtocol> myObject;
if (YES)
myObject = [[ClassA alloc] init];
else
myObject = [[ClassB alloc] init];
[myObject myMethod];
[myObject release];
I think this code should work well:
id obj = nil;
if (YES) {
obj = [[ObjectTypeA] alloc] init];
} else {
obj = [[ObjectTypeB] alloc] init];
}
[obj performSelector:#selector(YOUR_METHOD) withObject:YOUR_OBJECT];
You want dynamic typing :)
The way around this is to declare a third object that both of these inherit from
OR
You could use the adapter pattern and create an object that accepts both of these objects as a member and then wrap the functions you wish to call into that object
good luck!
--> These are genral OO solutions; I'm not a Objective-C developer