This question already has answers here:
Having trouble adding objects to NSMutableArray in Objective C
(2 answers)
Closed 8 years ago.
For some reason I can't get an array added to a nsmutabledictionary. I have it declared as a property in my .h file here:
#interface TCMExhibitFeedStore : NSObject
#property (nonatomic, strong) NSMutableDictionary *allLevels;
#property (nonatomic, strong) NSMutableDictionary *storedLevels;
+ (TCMExhibitFeedStore *)sharedStore;
- (void)fetchExhibtFeedWithCompletion:(void (^)(NSMutableDictionary *obj, NSError *err))block;
- (TCMLevel *)createLevel:(TCMLevelRemote *)l;
- (TCMExhibit *)createExhibit:(TCMExhibitRemote *)e;
- (BOOL)saveChanges;
#end
Then, I'm trying to add an empty array to it in a function in the .m file like this
[_storedLevels setObject:[[NSMutableArray alloc] init] forKey:#"levels"];
However, when I step through the application this never gets added. Looking in the debugger shows it as
_storedLevels NSMutableDictionary * 0x00000000
NSMutableDictionaries are nothing new to me. I'm just going a bit crazy trying to find my error because this all looks normal to me.
The following lines confuses me...
[_storedLevels setObject:[[NSMutableArray alloc] init] forKey:#"levels"];
Instead of above Use:
self.storedLevels = [[NSMutableDictionary alloc] init];
[self.storedLevels setObject:... forKey:#"levels"];
NOTE: Whenever you see 0x00000000
This means your object is not alloc-ated.
Therefore you need to alloc+init them, before setting any array/value to them.
0x00000000 means nil. All properties and instance variables of Objective C objects are initialized to nil. Hence, we can tell that you have never initialized your properties.
You can to initialize the dictionaries in the designated initializer for TCMExhibitFeedStore. (Or before you access them to add elements)
self.storedLevels = [NSMutableDictionary dictionary];
self.allLevels = [NSMutableDictionary dictionary];
Related
In my root view controller (named rootViewController.h) I have a property
#property (nonatomic, strong) NSDictionary *contactList;
Im trying to store value in this dictionary. Im passing the property to my function
AddContact *addContact = [[AddContact alloc]init];
NSString *result = #"";
result = [addContact addContact:user :[self contactList]];
When I go to my AddContact Function and breakpoint, the contactList is null even though I added 5x on the contactList using this code.
User *newUser = [[User alloc]init];
newUser.name = user.name;
newUser.phoneNumber = user.phoneNumber;
newUser.companyName = user.companyName;
[contactList setValue:newUser forKey:newUser.name];
Help
Try to use NSMutableDictionary, if you want to change it. NSDictionary can't be edited after initialization.
If you want to change your dictionary you need to create is as mutable like
#property (nonatomic, strong) NSMutableDictionary *contactList;
as per rule if you are using Mutable objects you need to alloc ,init it
so in view's didLoad method
_contactList = [[NSMutableDictionary alloc] init] ;
You have to initialise contactList. You have not initialise contactList and so the value is not set. And you can use mutable dictionary if you want to change values later on. Please make sure the values being added to contactList are not null else they will not be added to contactList and so dictionary will not have any objects in it.
In my ARC app for iOS 7.1, I have a singleton class that has a NSMutableDictionary (property is nonatomic, retain) where the key is a string and the value is a NSMutableArray. The class sets this dictionary in a callback from a NSOperation subclass. Everything seems to work fine until some time later (could be several minutes or several hours), the objects in the NSMutableDictionary are gone. Usually the app was in the background and brought to the foreground but it's been nearly impossible to find a reproducible test case. The problem, however, happens all the time.
How can I go about debugging this? I've seen tools for finding leaks but nothing to detect a premature release.
CODE:
#interface MyManager : NSObject
#property (nonatomic, retain) NSOperationQueue * queue;
#property (nonatomic, retain) NSMutableDictionary * allObjectsByCategory;
#property (nonatomic, retain) NSMutableDictionary * allObjectsByName;
+ (MyManager *)default;
- (void)loadWithCompletion:(void (^)(BOOL succeeded))aBlock;
#end
#implementation MyManager
#synthesize queue, allObjectsByCategory, allObjectsByName;
- (void)loadWithCompletion:(void (^)(BOOL succeeded))aBlock {
self.queue = [[NSOperationQueue alloc] init];
MyFetchObjectsOperation * op = [[MyFetchObjectsOperation alloc] init];
op.successBlock = ^(NSMutableDictionary * allByCategory, NSMutableDictionary * allByName) {
self.allObjectsByCategory = allByCategory;
self.allObjectsByName = allByName;
aBlock(YES);
};
op.failureBlock = ^(NSError * err) {
aBlock(NO);
};
[self.queue addOperation:op];
}
#end
You have to give memory allocation from your class
- (void)loadWithCompletion:(void (^)(BOOL succeeded))aBlock {
self.queue = [[NSOperationQueue alloc] init];
MyFetchObjectsOperation * op = [[MyFetchObjectsOperation alloc] init];
op.successBlock = ^(NSMutableDictionary * allByCategory, NSMutableDictionary * allByName) {
self.allObjectsByCategory = [[NSMutableDictionary alloc] initWithDictionary:allByCategory];
self.allObjectsByName = [[NSMutableDictionary alloc] initWithDictionary:allByName];
aBlock(YES);
};
op.failureBlock = ^(NSError * err) {
aBlock(NO);
};
[self.queue addOperation:op];
}
I feel enabling Zombies will be the best way to debug this and identify the reason behind premature release of the object. Here's what zombie object does (quoting from apple docs):
"Replace deallocated objects with a “zombie” object that traps any attempt to use it. When you send a message to a zombie object, the runtime logs an error and crashes. You can look at the backtrace to see the chain of calls that triggered the zombie detector."
To enable Zombies, Press CMD+Shift+,(comma) then go to diagnostics tab and tick "Enable Zombie Objects"
Click here for a Screenshot.
Hope this helps!
It seems to me that you want to use a more persistent way of storing this NSMutableDictionary. Storing it on a singleton does not guarantee that the data will be kept around forever. Just as long as the memory is allocated for your application (really your singleton itself). When the application goes into the background the OS has the ability to free this memory as needed (as #CodaFi has mentioned).
I would suggest you either store this data using Core Data or save it to a file to be read for later. There are other options as well (NSUserDefaults for example) but I'd probably have to know more about why you want to keep this data around to really know what the best approach would be.
A easy way to save this NSMutableDictionary to a file would use the following code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"myPlistFile.plist"];
[yourDictionary writeToFile:plistPath atomically:YES];
You could retrieve the data using the following code (assuming you use the same way of generating your plistPath from above):
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
Maybe you're doing something like:
NSMutableDictionary *newDict = yourDictionary;
and then removing objects of newDict
newDict = nil;
which will also remove the values from yourDictionary.
I have two classes - BNRItem and BNRContainer. BNRContainer is a subclass of BNRItem. In order to cut down the amount of code I paste, assume the following that I have tested and know works:
+(BNRItem * ) randomItem; // allocate and init a random item.
#property(nonatomic, readwrite, copy) NSMutableArray * subitems; // This is a property of BNRContainer class
main.m:
NSMutableArray * rand_items = [NSMutableArray alloc] init];
for (int i = 0; i < 10; i++) {
[rand_items addObject: [BNRItem randomItem]];
}
[rand_items addObject: #"HELLO"];
BNRContainer * rand_container_of_items = [BNRContainer randomItem];
rand_container_of_items.subitems = rand_items;
[rand_container_of_items.subitems addObject: #"THERE"]; // ERROR SIGABRT
NSLog(#"------------------------------------------------------");
NSLog(#"%#", rand_container_of_items);
rand_container_of_items = nil;
If I NSLog without adding #"THERE", I see "HELLO" in my description so I know that I am able to call addObject: at that point. Why do I get SIGABRT when I am trying to access ivar "subitems" of rand_container_of_items? I just can't figure this one out.
The problem seems to be the copy modifier in your declaration.
#property (nonatomic, readwrite, copy) NSMutableArray *subitems;
In the documentation, the NSCopying protocol conformance is inherited form NSArray, so my suspicion is that in this line
rand_container_of_items.subitems = rand_items;
subitems contains an immutable copy of the original array. Try removing copy from your declaration. If you need a copy use the mutableCopy method.
Problem lies here
property(nonatomic, readwrite, copy) NSMutableArray * subitems;
You should not use copy here since it will return immutable copy of the object. So that you cannot add objects to it. It could be
property(nonatomic, strong) NSMutableArray * subitems;
This line is giving sigbart as when you allocate an array to mutable array, it becomes mutable.
So, when you are copying rand_items to rand_container_of_items.subitem, it is becoming mutable.
So, to make it immutable, try following :
BNRContainer * rand_container_of_items = [BNRContainer randomItem];
rand_container_of_items.subitems = [rand_items mutablecopy];
[rand_container_of_items.subitems addObject:#"THERE"]; // ERROR SIGABRT
This question already has answers here:
objects conforming to nscoding will not writetofile
(2 answers)
Closed 8 years ago.
I would like to have an array of model and a field of this model is a mutable array.
Let's say, for example ,in my Model.h I have :
#interface myModel : NSObject
#property (assign,nonatomic) NSString* name;
#property (assign,nonatomic) NSString* time;
#property (assign,nonatomic) NSMutableArray * songs;
#end
then my view controller I have:
NSMutableArray * Storage;
myModel * arr;
arr =[[alarmModel alloc] init];
arr.name=#"pippo";
arr.time=#"01:00:00";
arr.songs=[NSMutableArray arrayWithObjects:#"pippo",#"pluto",#"paperino", nil];
[storage addObject:arr];
[storage writeToFile:filePath atomically:YES]
the last command return "NO" it means that write to file failed.
Is it possible to do what I want to do?
you should init your NSMutableArray *storage, try:
NSMutableArray *storage = [[NSMutableArray alloc] init];
I have looked at numerous posts which state various ways in which to remove an object from an array correctly, but I am not sure which method is best to use in my instance. I am loading a dictionary from a plist, this dictionary contains numerous arrays, and these arrays contain another dictionary. So I have 3 storage devices setup, 1 to hold the overall dictionary, another for an array, and finally a dictionary to hold the object from the array:
Header:
NSMutableDictionary *questionsDictionary;
NSMutableArray *questionsArray;
NSDictionary *currentQuestion;
#property (nonatomic, retain) NSMutableDictionary *questionsDictionary;
#property (nonatomic, retain) NSMutableArray *questionsArray;
#property (nonatomic, retain) NSDictionary *currentQuestion;
So my first question is to do with the above, are (nonatomic, retain) the right things to use for the following code.
Next I load in my dictionary from the plist:
.m:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"MultipleChoice.plist"];
self.questionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
I then setup my question array based upon type based upon my question type:
- (void)setupQuestionType : (NSString *)qType
{
if ([self.questionsDictionary objectForKey:qType])
{
self.questionsArray = [self.questionsDictionary objectForKey:qType];
[self pickRandomQuestion];
}
}
Finally (this is where I get the error), I want to grab the a question at random from this question category:
// Pick a random question number based upon amount of questions
int randomQuestionNum = [[NSNumber numberWithInt:(arc4random() % [self.questionsArray count])] intValue];
// Grab the dictionary entry for that question
currentQuestion = [self.questionsArray objectAtIndex:randomQuestionNum];
// Remove the question from the available questions
[self.questionsArray removeObjectAtIndex:randomQuestionNum]; (Error here)
// Set the question text
self.question.text = [currentQuestion objectForKey:kQuestionkey];
Now if I comment out the removeObjectAtIndex line then the code runs fine, and my question is displayed on the screen. This leads me to believe that it isn't a null pointer. So the logical answer points to the fact that self.questionsArray isn't a NSMutableArray. So I tried the following when setting the array:
- (void)setupQuestionType : (NSString *)qType
{
if ([self.questionsDictionary objectForKey:qType])
{
NSMutableArray *temp = (NSMutableArray *)[self.questionsDictionary objectForKey:qType];
self.questionsArray = (NSMutableArray *)temp;
[self pickRandomQuestion];
}
}
Purely to see if I could type_cast it but the error still occurs. Can anyone shed some light on what I'm doing wrong, or the best approach to take?
Don't typecast NSArray to NSMutableArray. Instead:
NSArray *temp = [self.questionsDictionary objectForKey:qType];
self.questionsArray = [NSMutableArray arrayWithArray:temp];
// code not tested.