#property retain OR copy - objective-c

First I read this article
I think I should use "copy" in my programe.
Problem is using NSMutableDictionary copy it will terminate.
***** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary removeAllObjects]: mutating method sent to immutable object'**
I have no idea about "mutating method sent to immutable object".
I didn't set NSDictionary to NSMutabledictionary pointer.
Here is my code
.h file
#interface Button : NSObject {
#private
NSString* gID;
NSString* gBackColor;
NSString* gIconImage;
int gIndex;
BOOL gEnable;
BOOL gVisible;
NSString* gText;
NSMutableDictionary* gEvents;
BOOL gUseCircle;
}
#property (nonatomic,copy) NSString *ID;
#property (nonatomic,copy) NSString *BackColor;
#property (nonatomic,copy) NSString *IconImage;
#property int Index;
#property BOOL Enable;
#property BOOL Visible;
#property (nonatomic,copy) NSString *Text;
#property (nonatomic,getter=getEvents,retain) NSMutableDictionary *Events;
#property BOOL UseCircle;
#end
.m file
#implementation Button
#synthesize ID = gID;
#synthesize BackColor = gBackColor;
#synthesize IconImage = gIconImage;
#synthesize Index = gIndex;
#synthesize Enable = gEnable;
#synthesize Visible = gVisible;
#synthesize Text = gText;
#synthesize Events = gEvents;
#synthesize UseCircle = gUseCircle;
-(NSMutableDictionary*) getEvents
{
if (!gEvents)
{
gEvents = [[NSMutableDictionary alloc] initWithCapacity:20];
}
return gEvents;
}
- (id) init
{
self = [super init];
if (self != nil)
{
gID = #"";
gBackColor = #"";
gIconImage = #"";
gIndex = 0;
gText = #"";
gUseCircle = NO;
}
return self;
}
- (void) dealloc
{
[gID release];
[gBackColor release];
[gIconImage release];
[gText release];
[gEvents removeAllObjects];
[gEvents release];
gEvents = nil;
[super dealloc];
}
And implement
tBtnXML.Events = [self SplitEvents:tNode];
SplitEvents function:
-(NSMutableDictionary*) SplitEvents:(NSDictionary*)pEvents
{
NSMutableDictionary *tEvents = [[NSMutableDictionary alloc] initWithCapacity:5];
// code blabla
//.
//.
//.
[tEvents setObject:tEvent forKey:[NSNumber numberWithInt:tEventName]];
[tEvent release];
return [tEvents autorelease];
}
But I chage NSMutableDictionary* gEvents property from copy to retain , it execute normal.
Colud anyone tell me what's wrong with my code?
If my code is incorrect with dealloc,please tell me.
Thank you appriciate.
Yes, So I fixed my setter:
-(void) setEvents:(NSMutableDictionary*) pEvents
{
NSMutableDictionary* tNewDict = [pEvents mutableCopy];
[gEvents removeAllObjects];
[gEvents release];
gEvents = tNewDict;
}
This work with no error.
It helps me a lot.
But I can't vote up >"<~
So thank you Bavarious :)

In general, mutable properties should be retain instead of copy. When you declare a property as being copy, the synthesised setter method sends -copy to the object that’s being assigned to the property. In the case of mutable objects (e.g. NSMutableDictionary), sending -copy to them makes an immutable copy, effectively creating an object of immutable type (e.g. NSDictionary) instead.
So in:
tBtnXML.Events = [self SplitEvents:tNode];
the synthesised setter sends -copy to [self SplitEvents:tNode], thus creating an immutable copy of that dictionary (i.e., an NSDictionary instance), and assign it to gEvents. This is the cause of your error: gEvents is declared as NSMutableDictionary but points to an NSDictionary instead.
For the record, mutable classes usually declare a -mutableCopy method that does make a mutable copy. It is not used by declared properties, though. If you do not want to use retain, you need to implement a custom setter that uses -mutableCopy.

Related

Checking NSMutableArray for 0 elements causes a crash; why?

I have declared a NSMutableArray as a singleton; when I try to check for the array count, the app crashes! Here is the code:
// clear array that holds selected servcies
SingletonArrayOfSelectedRows *arrayOfSelectedRows = [SingletonArrayOfSelectedRows sharedArrayOfSelectedRows];
if([arrayOfSelectedRows count] > 0)
[arrayOfSelectedRows removeAllObjects];
This code is the same code I have found all over SO and Google. Using XCode5, I have checked to make sure the singleton is allocated (and it is), and there is a valid count (0) for the singleton.
UPDATE
Here is the code for the singleton.h file:
#interface SingletonArrayOfSelectedRows : NSMutableArray {
}
#property (nonatomic, retain) NSMutableArray *arrayOfSelectedRows;
+ (id)sharedArrayOfSelectedRows;
#end
Here is the code for the singleton.m file:
#implementation SingletonArrayOfSelectedRows {
}
#synthesize arrayOfSelectedRows; // rename
// sharedSelectedCellIndexes
+ (id)sharedArrayOfSelectedRows {
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) {
arrayOfSelectedRows = [[NSMutableArray alloc] init];
}
return self;
}
#end
Don't subclass NSMutableArray to do this. NSMutableArray is a class cluster. All of the actual array implementation is inside subclasses of NSMutableArray. If you subclass NSMutableArray then your subclass won't actually implement any array behavior unless you write it yourself.
According to the documentation :
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:.
Since you are subclassing NSMutableArray you will need to override the following NSMutableArray primitive methods as well:
insertObject:atIndex:
removeObjectAtIndex:
addObject:
removeLastObject
replaceObjectAtIndex:withObject:

this method crashes if I use a property and works fine if I declare the variable within the method--why?

I'm working on a programmable calculator, and for the life of me I can't understand what I'm doing wrong.
Here are the relevant parts of the code. (The code is unfinished, so I know there's extra stuff floating around.)
CalculatorViewController.m
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic) BOOL userIsEnteringNumber;
#property (nonatomic) BOOL numberIsNegative;
#property (nonatomic,strong) CalculatorBrain *brain;
#property (nonatomic) NSArray *arrayOfDictionaries;
#property (nonatomic) NSDictionary *dictionary;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize history = _history;
#synthesize userIsEnteringNumber = _userIsEnteringNumber;
#synthesize numberIsNegative;
#synthesize brain = _brain;
#synthesize arrayOfDictionaries;
#synthesize dictionary;
-(CalculatorBrain *)brain
{
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}
/*snip code for some other methods*/
- (IBAction)variablePressed:(UIButton *)sender
{
NSString *var = sender.currentTitle;
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[NSNumber numberWithDouble:3],#"x",[NSNumber numberWithDouble:4.1],#"y",[NSNumber numberWithDouble:-6],#"z",[NSNumber numberWithDouble:8.7263],#"foo",nil];
[self.brain convertVariable:var usingDictionary:dict];
self.display.text = [NSString stringWithFormat:#"%#",var];
self.history.text = [self.history.text stringByAppendingString:sender.currentTitle];
[self.brain pushOperand:[dict objectForKey:var] withDictionary:dict];
}
#end
And here's CalculatorBrain.m.
#import "CalculatorBrain.h"
#interface CalculatorBrain ()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
-(void)pushOperand:(id)operand withDictionary:(NSDictionary *)dictionary
{
NSNumber *operandAsObject;
if (![operand isKindOfClass:[NSString class]])
{
operandAsObject = operand;
}
else
{
operandAsObject = [dictionary objectForKey:operand];
}
[self.operandStack addObject:operandAsObject];
}
-(double)popOperand
{
NSNumber *operandAsObject = [self.operandStack lastObject];
if (operandAsObject) [self.operandStack removeLastObject];
return [operandAsObject doubleValue];
}
-(double)convertVariable:(NSString *)variable usingDictionary:dictionary
{
double convertedNumber = [[dictionary objectForKey:variable] doubleValue];
return convertedNumber;
}
#end
The thing I'm having trouble understanding is in the CalculatorViewController.m method - (IBAction)variablePressed:(UIButton *)sender. This line crashes the program:
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[list of objects and keys]];
But if I make it
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
then everything works fine. But if I try to do
NSDictionary *dict = [[self.dictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
which seems to me the right thing to do, then XCode won't let me, so I'm obviously not understanding something.
Any thoughts?
+alloc allocates memory for an object. -init... methods initialize the object.
[self.dictionary initWithObjectsAndKeys:... calls -dictionary which is either going to return a dictionary set in that property or nil and then attempts to call init... on it. If the dictionary exists then you are attempting to initialize an object more than once which is not valid. If the property has not been set then the getter will return nil and sending an init... message to nil will do nothing. Either way this is not what you want to do.
[[self.dictionary alloc] init... is also invalid, as the compiler warns you. Now you try to obtain an object from -dictionary and then call the class method +alloc on it.
There seems to be some fundamental confusion here about how objects are created and what property accessors do. I'm not sure how to address that besides suggesting looking at object creation and dot syntax.

Releasing ivars in dealloc gives "pointer being freed was not allocated" error

I have the following properties defined:
#property (nonatomic, retain) NSString *name_;
#property (nonatomic, retain) NSString *profilePicture_;
#property (nonatomic, retain) NSString *username_;
#property (nonatomic, retain) NSString *id_;
and I set them up in my init... like this:
-(id)initWithData:(NSDictionary *)data
{
self = [super init];
if (!self) {
return nil;
}
name_ = [data valueForKey:#"full_name"];
profilePicture_ = [data valueForKey:#"profile_picture"];
username_ = [data valueForKey:#"username"];
id_ = [data valueForKey:#"id"];
return self;
}
with the following dealloc:
-(void)dealloc
{
[name_ release];
[username_ release];
[profilePicture_ release];
[id_ release];
[super dealloc];
}
However the dealloc gives me an error:
pointer being freed was not allocated
Why is this? Do I have to do [[NSString alloc] init...] or [NSString stringWithString:]?
valueForKey: will return an autoreleased object, therefore you have no ownership. As they are all strings you can just call copy like this
name_ = [[data valueForKey:#"full_name"] copy];
profilePicture_ = [[data valueForKey:#"profile_picture"] copy];
username_ = [[data valueForKey:#"username"] copy];
id_ = [[data valueForKey:#"id"] copy];
you should also change your #property declarations to use copy as this is generally recommended for strings.
The other alternative is to go through the synthesised accessors but I generally avoid doing this in either init or dealloc
This is because you are assigning to backing variables in your initWithData. You should use rewrite your code as follows:
self.name_ = [data valueForKey:#"full_name"];
self.profilePicture_ = [data valueForKey:#"profile_picture"];
self.username_ = [data valueForKey:#"username"];
self.id_ = [data valueForKey:#"id"];
This would assign values through properties, which calls [retain] for you. The way your code is written now, the pointer is simply copied into ivars without calling [retain], which ultimately causes the issue that you describe.
Since you are using data from dictionary, you should set values through properties using self keyword.
If it doesn't solve, then problem is probably not inside your class but perhaps where you create the instance of it. Try examining the Code where you allocate and release the instance of this Class.
You should also profile your app using Simulator & NSZombies n Determine where you over-release the object.

Could there be an issue with the max. memory capacity? Or why is this variable partly released?

I'm developing an iPad app and I ran into a really weird issue here. I'll try to explain it as good as possible.
I have a class named TranslationObject which is holding a key and a textual value. I have created this class as the following:
#interface TranslationObject : NSObject {
NSNumber *_key;
NSString *_value;
}
#property (nonatomic, retain) NSNumber *key;
#property (nonatomic, retain) NSString *value;
- (id) initWithKey:(NSNumber *) key andValue:(NSString *) value;
#end
The translations will be pulled from a XML or DB in the future, but for now I do the following:
#interface Translation : NSObject {
NSMutableArray *m_extfeat;
}
#property (nonatomic, retain) NSMutableArray *extfeat;
+ (Translation *) getInstance;
- (id) init;
- (NSMutableArray *) getExtFeat;
#end
Implementation:
#implementation Translation
#synthesize extfeat = m_extfeat;
- (id) init {
self = [super init];
if (self) {
m_extfeat = [[self getExtFeat] retain];
}
return self;
}
- (NSMutableArray *) getExtFeat {
TranslationObject *obj1 = [[[TranslationObject alloc] initWithKey:[NSNumber numberWithInt: 0] andValue:#"Animal house"] autorelease];
.... more items declared ....
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16, obj17, nil];
return [array autorelease];
}
#end
These translations are being used in a UITableViewController and are being fetched in the viewDidLoad method as:
- (void)viewDidLoad
{
_data = [[Translation getInstance].extfeat retain];
}
I use these values at its cellForRowAtIndexPath, where I call a method to configure the cell:
- (void) configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *) indexPath {
TranslationObject *object = (TranslationObject *) [_data objectAtIndex:indexPath.row];
//Crashes here at 13th item:
NSLog("Object key: %#", [object.key stringValue]);
}
As the snippet above says, strangely, the app crashes when fetching the key value only if the _data array contains more than 12 items. So if I only fill the _data variable with 12 items or less, my code works fine. If I add more than 12, the app crashes as soon as it fetches the 13th object.
So I enabled NSZombies and so when I check the 13th item in that method, the value is still fine, but it's only the key that turned into a Zombie. And again.. Only from the 13th item on!
Does anyone know how this is possible? Is it maybe so that there is a maximum number of items that can be stored in the memory? Is the memory full at the 12 item? But if that'd be the case, then why would the value still be there. How would it be possible that it's just the key that is being released before?! And how?!
I hope this explanation makes sense and someone can shine a light over this case.. =/
Thanks!
EDIT: Here's the implementation of the initWithKey method of the TranslationObject:
- (id) initWithKey:(NSNumber *) key andValue:(NSString *) value {
self = [super init];
if (self) {
_key = key;
_value = value;
}
return self;
}
Make sure your using the property accessors in the TranslationObject or retaining the number:
#implementation TranslationObject
#synthesize key=_key, value=_value;
- (id) initWithKey:(NSNumber *) key andValue:(NSString *) value {
self = [super init];
if (!self) return nil;
self.key = key; // ensures key is retained
self.value = value;
return self;
}
…
#end
Specifics:
self.key = key;
is the syntax for calling the accessor methods for the property; in this case the set accessor. Given you declared your property with the nonatomic and retain attributes as follows:
#property (nonatomic, retain) NSNumber *key;
the set accessor will look something like
- (void)setKey:(NSNumber *)value {
if (value != _key) {
id old = _key;
[value retain];
_key = value
[old release];
}
}
The set accessor is automatically generated by the compiler when you added:
#synthesize key=_key;
Conversely, calling
_key = key;
simply copies the value of the pointer in key to _key, but does not retain the object referred to by key. TranslationObject does not assume ownership of key. If you did not want to use the accessor, the correct implementation would be
_key = [key retain];

Using self.objectname causes profiler to report a memory leak

Please help;
Header File
#import <Foundation/Foundation.h>
#interface MyClass : NSObject {
NSMutableString * myString;
}
#property (nonatomic, retain) NSMutableString * myString;
-(id) init;
-(void) dealloc;
#end
Implementation File
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
-(id) init {
if ((self = [super init])) {
self.myString = [[NSMutableString alloc] init];
}
return self;
}
-(void) dealloc {
[super dealloc];
[self.myString release];
}
#end
Usage
MyClass * m = [[MyClass alloc] init];
[m release];
//-- Xcode 4 profiler reports a memory leak here.
However, when the code in implementation file of the class is changed to not use the [self.myString .....] notation, then no memory leak is reported.
So,
-(id) init {
if ((self = [super init])) {
myString = [[NSMutableString alloc] init];
}
return self;
}
}
and
-(void) dealloc {
[super dealloc];
[myString release];
}
works fine. No memory leaks reported.
Any ideas - is it profiler or is it me (be nice)?
Your memory leak is not caused by using your setter. Your memory leak is caused by you not managing your memory correctly!
If you declare the following property
#property (nonatomic, retain) id value;
That means that the compiler generates methods that look something like this (highly simplified):
- (id)value {
return value;
}
- (void)setValue:(id)aValue {
[value autorelease];
value = [aValue retain];
}
When you use dot-notation, self.value = obj is desugared into [self setValue:obj]. Thence, you are actually causing obj to be retained within the setter. If you initially create an owning reference to obj (by using an +alloc without a corresponding -release or -autorelease), you'll have over-retained obj, and it will never be deallocated. Hence, you need to do something like this:
id obj = [[[NSObject alloc] init] autorelease];
self.value = obj;
or
id obj = [[NSObject alloc] init];
self.value = [obj autorelease];
or
id obj = [[NSObject alloc] init];
self.value = obj;
[obj release];
Whatever you do, you need to make sure that when you assert ownership of an object (by retaining it), you also release it.
Setter methods in Objective-C equate to a reatain of the new object and release of the old object. In your case the compiler will generate a setter method for your myString property that looks something like...
- (void)setMyString:(NSMutableString*)aString {
[myString autorelease];
myString = [aString retain];
}
When you invoke self.myString = in your init method this translates to a call to the setter. The setter in turn retains the object you pass to it. Because you've directly alloc'd the NSString it begins life with a retain count of one, you then call the setter and the retain count becomes two.
There's two approaches to fixing the problem, the first would be to add a call to [myString autorelease] after you alloc it. Or secondly switch your init method to directly assign the ivar...
// in your init method...
myString = [[NSMutableString alloc] init];
It's a good idea to avoid setter usage in init methods, not because of retain counts but because the object as a whole is not yet fully initialized.
#property (nonatomic, RETAIN)
you are retaining my friend. You have to release the object twice then because the retain count is 2
here is what you should do in the INIT method:
NSString *str = [[NSString alloc] initWithString:#"Hello World!"];
self.myString = str;
[str release]; // dont leak
Also I do not recommend using self.someProperty in the class itself. Doing so requires 1 extra objc_msgSend() to be done to access your variable and will slow down your application.