I'm fairly new to Objective-C and am not sure how to correctly deal with memory management in the following scenario:
I have a Core Data Entity with a to-many relationship for the key "children". In order to access the children as an array, sorted by the column "position", I wrote the model class this way:
#interface AbstractItem : NSManagedObject
{
NSArray * arrangedChildren;
}
#property (nonatomic, retain) NSSet * children;
#property (nonatomic, retain) NSNumber * position;
#property (nonatomic, retain) NSArray * arrangedChildren;
#end
#implementation AbstractItem
#dynamic children;
#dynamic position;
#synthesize arrangedChildren;
- (NSArray*)arrangedChildren
{
NSArray* unarrangedChildren = [[self.children allObjects] retain];
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"position" ascending:YES];
[arrangedChildren release];
arrangedChildren = [unarrangedChildren sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
[unarrangedChildren release];
return [arrangedChildren retain];
}
#end
I'm not sure whether or not to retain unarrangedChildren and the returned arrangedChildren (first and last line of the arrangedChildren getter). Does the NSSet allObjects method already return a retained array? It's probably too late and I have a coffee overdose.
I'd be really thankful if someone could point me in the right direction. I guess I'm missing vital parts of memory management knowledge and I will definitely look into it thoroughly.
-allObjects returns an autoreleased instance, there is no need to retain and release it.
As for arrangedChildren, it will be retained only if you use the synthesized setter:
self.arrangedChildren = [unarrangedChildren sortedArrayUsingDescriptors:/*...*/];
Directly assigning to the instance variable as you do does not invoke the synthesized setter.
Finally, you shouldn't retain the return value here - your method isn't named starting with alloc, new or create and callers thus have to take ownership explicitly.
I recommend reading the "Cocoa Memory Management Guide" and the section on "Declared Properties" in the Objective-C language description.
The retain in your #property for arrangedChildren should take care of that. You will need to release it in your dealloc.
Related
I have a class, say the class Person. In than class I have several NSMutableArrays.
#property NSMutableArray *arrayOne;
#property NSMutableArray *arrayTwo;
...
Now I want to copy that Class so I can return it from a function and use the copy and change it's data. I want to have a new copy of the object in memory not another reference to the same address.
To do that I have implemented this in my Person.m:
-(id)copyWithZone:(NSZone *)zone
{
Person *copy = [[Person allocWithZone:zone] init;
copy.arrayOne = [NSMutableArray arrayWithArray:self.arrayOne];
copy.arrayTwo = [NSMutableArray arrayWithArray:self.arrayTwo];
...
return copy;
}
So far this works just like I want it to, but when I try to sort the arrays of the copy, I get an error:
-[__NSArrayI sortUsingSelector:]: unrecognized selector sent to instance 0x100108a30
I have noticed that the original arrays are of the type '_NSArrayM' but the copy is '_NSArrayI'...
So what did I wrong? I have heard of deep copying using NSArchiver and NSUnarchiver... Do I have to use that? Sorry, I am quite new to C and Objective-C... :D
I hope you can help me out of this. =)
Sorry about my 'school-english'....
Cheers,
Nick
EDIT: The arrays consist of NSString objects, so I can use
sortUsingSelector:#selector(caseInsensitiveCompare:)
to sort the array.
How are your mutable array properties being declared in your header file?
// Wrong
#property (copy) NSMutableArray *array;
#property (assign) NSMutableArray *array;
// Right
#property (strong) NSMutableArray *array;
NB If you're just doing
#property NSMutableArray *array;
then I'm amazed your code has got this far :) The default property memory semantics is assign - which won't tell ARC to retain your arrays in any way at all :) You need to specify strong.
This is for an app that allows users to tag things. Tags are just strings.
An array of TagHolder objects holds a list of all tags in use in the app, with a boolean telling if the tag is selected, but this is an implementation detail.
The external interface calls for two methods, selectedTags, and setSelectedTags: which return and accept an arrays of strings.
I would like these two methods to work as accessors for a declared property selectedTags.
Now, my question is:
What would be the correct memory management semantics to declare for that property?
The code pattern that I have in mind is this (code not tested, so please bear with typos):
#interface TagInfo : NSObject
#property (strong, nonatomic) NSString *tag;
#property (nonatomic) BOOL selected;
#end
#interface SomeClass : NSObject
#property (memorytype, nonatomic) NSArray *selectedTags;
#end
#implementation TagHolder
- (NSArray *)selectedTags
{
// tagInfoArray is an array of all TagInfo objects
NSPredicate *selPred = [NSPredicate predicateWithFormat: #"selected == YES"];
NSArray *selectedTagInfoObjects = [[self tagInfoArray] filteredArrayUsingPredicate: selPred];
NSArray *selectedTags = [selectedTagInfoObjects valueForKey: #"tag"];
return selectedTags;
}
- (void)setSelectedTags: (NSArray *)selectedTags
{
for (TagInfo *tagInfo in [self tagInfoArray]) {
tagInfo.selected = [selectedTags containsObject: tagInfo.tag];
}
}
#end
What should memorytype be? Obviously not strong or weak, but I think it could be any one of assign, copy or even unsafe_unretained, but which one is the most correct for a computed property with an object value?
I normally use ARC, but I guess the question is the same in an environment with manual retain count.
memorytype is significant only when you #synthesize your property accessors. Since you are providing your own implementation for both the getter and the setter, the things you put in parentheses after #property are ignored; I usually put readonly or readwrite there, just to remind myself of what kind of access is available on these properties.
Your code is correct, it will work without creating memory issues with or without ARC.
I'm not sure I understood how alloc and retain work.
Recently I discovered that the NSString properties were not retained and I had to add [myString copy] when I set them. Which makes me wonder if I misunderstood the whole way of using retain/alloc
Please, may someone tell me if I'm doing it correctly? I read a lot and had a look on open source projects, this let me thing that I may have been wrong since the beginning.
Here is my way of doing it:
/**** VIEW.h *****/
#import "MyClass.h"
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
#property (nonatomic, retain) MyClass *myObject;
- (void)defineObject;
#end
.
/**** VIEW.m *****/
#import "VIEW.h"
#implementation MyViewController
#dynamic myObject;
- (void)viewDidLoad
{
[super viewDidLoad];
[self defineObject];
NSLog(#"My object's name is: %#", myObject.name);
}
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
.
/**** MyClass.h *****/
#interface MyClass : NSObject {
//not sure if this line is still necessary
NSString *name;
}
#property (nonatomic, retain) NSString *name;
- (id)initPersonalised;
- (void)setName:(NSString *)name;
- (NSString *)name;
#end
.
/**** MyClass.m *****/
#import "MyClass.h"
#implementation MyClass
#dynamic name;
(id)initPersonalised{
self = [super init];
name = #"Undefined";
}
- (void)setName:(NSString *)name{
self.name = [name copy];
}
- (NSString *)name{
return [self.name copy];
}
#end
I hope you can bring a bit of light, after months of programming this way, I'm less and less sure of doing it well.
This is indeed a topic that every Objective C programmer stumbles upon. There are a few things one needs to know:
Instance variable vs. property access
Within MyViewController,
myObject = xxx;
and
self.myObject = xxx;
are two different things. The first directly assigns to the instance variable and does neither release to old referenced insance nor retain the newly assigned instance. The latter one uses the property setter and thus releases the old and retains the new value.
Deallocation
Even when you have declared an implemented a property that takes care of retaining and releases the values, it won't take care of deallocation when your object (MyViewController in your case) is released. So you must explicitly release it in dealloc:
-(void) dealloc {
[myObject release];
[super dealloc];
}
Now to your code:
The snippet:
myObject = [[MyClass alloc] initPersonalised];
is perfectly okay. When you create an object, you use the pair of alloc and initXXX. The always create an instance with the reference count set to 1. So by directly assigning it to the instance variable, you create a clean constellation. I don't see no other way of creating the instance.
In MyClass you could use #synthesize name instead of #dynamic. Then the compiler would implement name and setName: automatically and you wouldn't need to do it yourself.
Finally, your missing dealloc.
Update:
If you use:
self.myObject = [[MyClass alloc] initPersonalised];
then you have a memory leak because initPesonalised sets the reference count to 1 and the setter of myObject increases it to two. If you want to use the setter, then I has to be:
MyClass* mo = [[MyClass alloc] initPersonalised];
self.myObject = [[MyClass alloc] initPersonalised];
[mo release];
It would be different if you weren't using initXXX to create a new instance. The class NSString for example has many methods called stringXXX, which create a new instance (or return a shared one) that has (conceptually) a reference count of 1 that will later automatically decreased by one. Then you better use the setter:
self.name = [NSString stringWithFormat: #"instance %d", cnt];
If you want to use copy instead of retain for your string property (which is good practice), then you can simply declare your property like this:
#property (nonatomic, copy) NSString *name;
When you then use #synthesize to implement the getter and setter, the compiler will generate them using copy instead of retain.
And NSString *name; is necessary even if you use #property and/or #synthesize to implement the property.
Alloc and init are methods that always go hand-in-hand. alloc allocates space for your object, and init initializes your object to some value. When you call alloc, you are responsible for freeing that object later. If you call copy, you are also responsible for releasing that object later. It's considered good practice to always initialize your objects right after you allocate them.
Now, to answer the questions I found in your code.
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
So is that line necessary? That depends. Does it make sense that your object has a MyClass as a property? This is a question only you can answer based on your design. I recommend you to study Object-Oriented Programming in more depth.
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
Not necessarily. You are just providing a pointer to an object of the specified kind. The moment you set your property, depending on the property modifiers, your class will know what to do with MyObject.
In that way, there's no need to call [yourObject copy]. In this way your properties will be copied instead of being retained. Just don't forget to release it later in your -dealloc method, like you would with retain properties.
All in all, this is what I recommend you to study a bit more:
Object-Oriented Programming (not related to your issue, but I can tell you are not comfortable using it. Objective-C is heavily object oriented, so you want to understand OOP).
iOS Memory Management.
You can have a look at the Memory Management Guide. It will help you to better understand the alloc &Â retain concepts; hope this helps you.
Is there any generic implementation which converts any Object into NSDictionary, sets all variable names as keys and values as dictionary values?
In order to achieve your objective, you can use Key-Value Coding. This protocol provides a mechanism to set values of object properties based on the names of the properties represented as NSString's rather than calling the accessors directly.
In order for it to work, you need to have defined your objects with accessors that follow the naming conventions (easy enough using properties). You can see the NSKeyValueCoding protocol guide here:
http://bit.ly/es6kyH
And the Key-Value Coding programming guide here:
http://bit.ly/fBY3Qa
You'll still have to do the iteration, but it's a good start.
Solved using SBJSONParser, converted NSObject to JSON Representation and then fetched NSDictionary out of it.
The perfect way to do this is by using a library for serialization/deserialization
many libraries are available but one i like is
JagPropertyConverter
https://github.com/jagill/JAGPropertyConverter
it can convert your Custom object into NSDictionary and vice versa
Assuming
#interface Person : NSObject
#property (nonatomic, retain) NSNumber * age
#property (nonatomic, retain) NSString * address
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * cellNo;
#end
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc]init];
converter.classesToConvert = [NSSet setWithObjects:[Person class], nil];
Person *person = [[Person alloc]init];
person.name = #"name";
person.address = #"address";
person.age = #27;
person.cellNo = #"XXXXXXXXXX";
//For Object to Dictionary
NSDictionary *dictPerson = [converter convertToDictionary:person];
//For Dictionary to Object
Person *person = [[Person alloc]init];
[converter setPropertiesOf:person fromDictionary:dictPerson];
No, you could have everything in your class, e.g. references to other objects and primitives. NS-Dictionary can serialize itself to a NSString and NSString can recreate the dictionary from the string. Best will be you supply your own methods to serialize it.
I am curious what is happening with setValue:forKey: in the code snippet below: is it just setting the pointer to point to each array, similar to ...
[self setMyArray_1: animalArray];
[self setMyArray_2: animalArray];
[self setMyArray_3: animalArray];
Also: does setValue:forKey retain the array? I am guessing it does (as the above would)
Code Snippet:
// INTERFACE
#property(nonatomic, retain) NSArray *myArray_1;
#property(nonatomic, retain) NSArray *myArray_2;
#property(nonatomic, retain) NSArray *myArray_3;
// IMPLEMENTATION
#synthesize myArray_1;
#synthesize myArray_2;
#synthesize myArray_3;
for(counter=1; counter<=3; counter++) {
NSArray *animalArray = [[NSArray alloc] initWithObjects:#"cat", #"rat", nil];
NSString *propertyName = [[NSString alloc] initWithFormat:#"myArray_%d", counter];
[self setValue:animalArray forKey:propertyName];
[animalArray release];
[propertyName release];
}
gary
The answer is yes, the two code snippets essentially do the same thing. setValue:forKey doesn't retain the array, but it finds the synthesized setMyArray_x method which in turn retains the array. iVarName should better be called propertyName or keyName. However, if you hadn't declared and synthesized the properties, but instead just had four ivars, setValue:forKey would still be able to set them to point to animalArray but it wouldn't be retained.
First of all [self setMyArray_1: animalArray]; does not just set pointers, but also retains input array - as it calls automatically generated setter method and its behaviour is defined in corresponding property attributes:
#property(nonatomic, retain) NSArray *myArray_1; // retain !
How accessor method is searched described in Accessor Search Implementation Details in "KVC Coding Guide":
When the default implementation of
setValue:forKey: is invoked for a
property the following search pattern
is used:
The receiver’s class is searched for
an accessor method whose name matches
the pattern -set<Key>:.
So as your class has necessary accesor method (declared via property) this method (setMyArray_i) will get called (and retain your array).