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

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];

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:

NSMutableDictionary crashes with "mutating message sent to immutable object"

I have a class that has a NSMutableDictionary as a property:
#interface Alibi : NSObject <NSCopying>
#property (nonatomic, copy) NSMutableDictionary * alibiDetails;
#end
With the following constructor:
- (Alibi *)init
{
self = [super init];
_alibiDetails = [NSMutableDictionary dictionary];
return self;
}
and copy method:
- (Alibi *)copyWithZone:(NSZone *)zone
{
Alibi *theCopy = [[Alibi alloc] init];
theCopy.alibiDetails = [self.alibiDetails mutableCopy];
return theCopy;
}
When I try to call setObject:ForKey: I get a runtime error mutating method sent to immutable object.
I have the Alibi object declared in the view controller as #property (copy, nonatomic) Alibi * theAlibi; and I initialize it with self.theAlibi = [[Alibi alloc] init]; in viewDidLoad.
The line which crashes is:
NSString * recipient;
recipient = #"Boss";
[self.theAlibi.alibiDetails setObject:recipient forKey:#"Recipient"];
Please let me know what I am doing wrong here. I am coding for iOS 5 on iPhone.
You have a 'copy' property, which means exactly that - your NSMutableDictionary will get the -copy method called and return a regular NSDictionary before being assigned to the synthesized instance variable. This thread provides some information on some of your options as to solving this.
For the sake of completing this thread I will include my revised Alibi class below, this works as I require it to. If anyone notices any memory leaks or other issues, that would be appreciated.
#implementation Alibi
NSMutableDictionary *_details;
- (Alibi *)init
{
self = [super init];
_details = [NSMutableDictionary dictionary];
return self;
}
- (NSMutableDictionary *)copyDetails
{
return [_details mutableCopy];
}
- (NSMutableDictionary *)setDetails:(NSMutableDictionary *)value
{
_details = value;
return value;
}
- (void)addDetail:(id)value forKey:(id)key
{
[_details setObject:value forKey:key];
}
- (id)getDetailForKey:(id)key
{
return [_details objectForKey:key];
}
- (Alibi *)copyWithZone:(NSZone *)zone
{
Alibi *theCopy = [[Alibi alloc] init];
theCopy.serverId = [self.serverId copyWithZone:zone];
theCopy.user = [self.user copyWithZone:zone];
theCopy.startTime = [self.startTime copyWithZone:zone];
theCopy.endTime = [self.endTime copyWithZone:zone];
[theCopy setDetails:[self copyDetails]];
return theCopy;
}
#end

Custom Object becoming _NSCFString upon entry into NSMutableArray

I'm having issues placing a custom object (WSWCMPost) into an NSMutableArray and then accessing the data stored in it later. Below is the relevant code.
Here is "WSWCMPost.h"
#import <Foundation/Foundation.h>
#interface WSWCMPost : NSObject
{
NSString *postBody;
NSString *postTitle;
NSString *postID;
}
#property (nonatomic, retain) NSString *postBody, *postTitle, *postID;
- init;
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title;
- (NSString*)postBody;
- (NSString*)postTitle;
- (NSString*)postID;
Here is "WSWCMPost.m"
#import "WSWCMPost.h"
#implementation WSWCMPost
#synthesize postBody, postTitle, postID;
- (id)init {
self = [super init];
if(self) {
postID = #"none";
postBody = #"none";
postTitle = #"none";
}
}
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {
postTitle = title;
postID = ID;
postBody = body;
}
#end
And here is the "viewDidLoad" method that is causing my issues
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.detailViewController = (WSWCMDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
// getting an NSString
NSLog(#"Pulling saved blogs...");
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"wswcmt1"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
_objects = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
_objects = [[NSMutableArray alloc] init];
}
NSLog(#"Pulled saved blogs...");
NSLog(!_objects ? #"Yes" : #"No");
#try {
NSLog(#"_objects description: %#",[_objects description]);
NSLog(#"_objects[0] postID: %#",[[_objects objectAtIndex:0] postID]);
}
#catch (NSException *exception) {
NSLog(#"Caught exception %#", exception);
NSLog(#"Objects doesnt exist, allocating memory...");
_objects = [[NSMutableArray alloc] init];
WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:#"noID" AndBody:#"noBody" AndTitle:#"noTitle"];
[_objects insertObject:testPost atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:#"wswcmt1"];
}
if (!_objects ) {
NSLog(#"Objects doesnt exist...");
_objects = [[NSMutableArray alloc] init];
WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:#"dne" AndBody:#"Dne" AndTitle:#"DNe"];
[_objects insertObject:testPost atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:#"wswcmt"];
}
[self refreshButton:nil];
}
And finally, here is the output
2012-06-25 22:39:49.345 WSWCM[4406:907] Pulling saved blogs...
2012-06-25 22:39:49.352 WSWCM[4406:907] Pulled saved blogs...
2012-06-25 22:39:49.355 WSWCM[4406:907] Yes
2012-06-25 22:39:49.356 WSWCM[4406:907] _objects description: (null)
2012-06-25 22:39:49.358 WSWCM[4406:907] _objects[0] postID: (null)
2012-06-25 22:39:49.360 WSWCM[4406:907] Objects doesnt exist...
2012-06-25 22:39:49.363 WSWCM[4406:907] Refresh Triggered...
I think that is all of the relevant code. If i forgot anything let me know please. This issue has been bothering me for hours...
While I'm not positive why it's giving you NSStrings instead of just blowing up normally, the problem seems to stem from the fact that your custom class, WSWCMPost, does not conform to the NSCoding protocol. Make sure that your custom objects implement this protocol if you want to store them in NSUserDefaults, since it doesn't know how to serialize the data otherwise.
To be more exact, you'll have to add these methods to your class implementation:
- (id)initWithCoder:(NSCoder *)coder {
self = [self initWithID:[coder decodeObjectForKey:#"id"] AndBody:[coder decodeObjectForKey:#"body"] AndTitle:[coder decodeObjectForKey:#"title"]];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[encoder encodeObject:postID forKey:#"id"];
[encoder encodeObject:postBody forKey:#"body"];
[encoder encodeObject:postTitle forKey:#"title"];
}
This will allow the data to be serialized by NSCoder. Once you've done this, you should clear all the information currently stored by NSUserDefaults to make sure that it doesn't contain any more NSStrings, but then everything should work properly. Of course, you'll have to update these two methods if you change the data stored by your WSWCMPost object.
Another thing to mention, you're having collisions with your getters/setters and their respective instance variables. So your implementation is:
interface
#interface WSWCMPost : NSObject
{
NSString *postBody; // don't need to do these anymore for properties
NSString *postTitle;
NSString *postID;
}
#property (nonatomic, retain) NSString *postBody, *postTitle, *postID;
implementation
#implementation WSWCMPost
#synthesize postBody, postTitle, postID;
- (id)init {
self = [super init];
if(self) {
postID = #"none"; // not prefixing your variables with 'self' so they are not getting retained
postBody = #"none";
postTitle = #"none";
}
}
#end
Here's how you should be writing those out:
interface
/** NOTE: No need to specify your instance variables here anymore, just the properties */
#interface WSWCMPost : NSObject
#property (nonatomic, retain) NSString *postID;
#property (nonatomic, retain) NSString *postTitle;
#property (nonatomic, retain) NSString *postBody;
implementation
#implementation WSWCMPost
/** Now you specify the corresponding instance variable name alongside the property name */
#synthesize postBody=_postBody, postTitle=_postTitle, postID=_postID;
- (id)init {
self = [super init];
if(self) {
self.postID = #"none"; //getting retained
self.postBody = #"none";
self.postTitle = #"none";
}
}
That would definitely cause data to be released too soon.
So the previous way you could type in self.postID or postID and the compiler wouldn't complain. The difference is when you type postID it is actually setting the member variable and not retaining it... where self.postID will release whatever it is currently set to and retain the new value if it's different.
By declaring your properties the new way, you have to either call the setter as self.postID or set the underlying instance variable as _postID. A lot of early iPhone books had you bang out properties that way and it just ends up causing all sorts of memory issues.
Hope this helps!
UPDATE!!!
You forgot to return self in your constructor ;) I bet that's it
- (id)init {
self = [super init];
if(self) {
self.postID = #"none"; //getting retained
self.postBody = #"none";
self.postTitle = #"none";
}
return self; // THIS IS WHY, you're constructor doesn't return an instance of the class... add this please
}
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {
if(( self = [super init] ))
{
self.postTitle = title;
self.postID = ID;
self.postBody = body;
}
return self;
}
Your output definitely shows what was wrong in your code.
2012-06-25 21:51:07.691 WSWCM[4049:907] -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80
2012-06-25 21:51:07.696 WSWCM[4049:907] Caught exception -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80
These two lines tell you that NSString object does not recognize selector postID. This hint should be enough to find out where you need to see in depth.
See this Storing custom objects in an NSMutableArray in NSUserDefaults for more information.

#property retain OR copy

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.

NSArray assignment

I have two viewControllers and one has to pass an array to another, but on receiving side i am always getting count 0. here is what i did
classA.h
Class A : UIViewController
{
#private
NSMutableArray *_array;
}
#property (nonatomic, retain ) NSMutableArray *array;
#end
classA.m
#implementation
#synthesis array =_array;
-(void) nowShow
{
int objCount = [ _array count ]; // This is always coming as 0 though i tried various ways (listed below )
}
#end
classB.m
-(void) method:(id)sender {
NSMutableArray *msgArray = [[NSMutableArray alloc] initWithCapacity:1];
for ( int i = 0 ; i <objCount; i++ ){
unsigned int idMsg = msgId[i];
[msgArray addObject:[NSNumber numberWithUnsignedInt:idMsg]];
}
classA *classAController = [[classA alloc] initWithStyle:UITableViewStylePlain];
//[ classAController.array arrayWithObject
//[classAController.array addObjectsFromArray:msgArray];
[ classAController.array initWithArray:msgArray];
//[classAController.array setArray:moveArray];
[self presentModalViewController:classAController animated:YES];
}
Any suggestion guys
You shouldn't call any init method unless it's immediately after calling alloc.
With properties, all you need to use is assignment:
classAController.array = msgArray;
The keyword should be #synthesize not #synthesis
edit: also you want to synthesize the property "array", not the instance variable _array