setObjectForKey: object cannot be nil in mutableArray - objective-c

Greeting. I have code like below, I want to add multiple value to the same key in the dictionary.
if ([_sectionContents objectForKey:AddKey] != nil) {
//Already exist a value for the key
id object = [_sectionContents objectForKey:AddKey];
NSMutableArray *objectArray;
if ([object isKindOfClass:[NSMutableArray class]]) {
objectArray = (NSMutableArray *)object;
} else {
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
}
[objectArray addObject:course];
[_sectionContents setObject:objectArray forKey:AddKey];
} else {
//No value for the key
[_sectionContents setObject:[NSArray arrayWithObjects:course,nil] forKey:AddKey];
}
However, I got the error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: object cannot be nil
I figure out it is because of line
[_sectionContents setObject:objectArray forKey:AddKey];
And also I got the warning like objectArray is not used
I don't understand it because I did use objectArray for adding element in it. Why is that please?

When you write
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
you're creating a new variable local to the inside of that 'else' statement. As such, it is created then immediately released, because there is nothing following it in the else statement.
You want to update the variable rather than re-create it - try replacing the rogue line with
objectArray = [[NSMutableArray alloc] init];
(When we don't specify the type e.g. 'NSMutableArray *', we assign to an existing variable, rather than establishing a new one.)

This is because you have two objectArray instead of one
if ([_sectionContents objectForKey:AddKey] != nil) {
...
// first one
NSMutableArray *objectArray;
if ([object isKindOfClass:[NSMutableArray class]]) {
objectArray = (NSMutableArray *)object;
} else {
// second one (*)
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
// *** Correct Solution ***
objectArray = [[NSMutableArray alloc] init];
}
...
} else {
...
}
(*) You've redefined your object array incorrectly in your else clause of your if inside the main if ([_sectionContents objectForKey:AddKey] != nil) {. If object isn't kinda NSMutableArray you get to the else which initialize new ObjectArray and not the one defined at the top of your if statement.

Related

"unrecognized selector sent to instance" for NSArrayI

I have the following code which gives the "unrecognized selector sent to instance" error for the NSArray. I've not been able to figure this out and feel its something simple I'm missing here. I can post more if needed.
Quote *myQuote;
NSArray *myQuotes = theSubject.quotes;
//START LOOP HERE
for (myQuote in myQuotes){
NSLog(#" excerpt = %#", myQuote.excerpt);
NSLog(#" desc2 = %#", myQuote.desc2);
NSLog(#" quote_date = %#", myQuote.quote_date);
NSLog(#" myQuote = %#", myQuote);
I believe the problem is in this function which returns an array of Quotes:
- (NSArray *) getQuotesFromSubId:(NSInteger )subId {
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
self.quoteMaps = [appDelegate quoteMaps];
self.quotes = [appDelegate quotes];
//get the quote_ids from quote_map for this subject_id
NSString *stringOfSubjectId = [NSString stringWithFormat:#"%ld", (long)subId];
NSPredicate *filterSubjectId = [NSPredicate predicateWithFormat:#"subject_id == %#", stringOfSubjectId];
NSArray *quoteMapSection = [self.quoteMaps filteredArrayUsingPredicate:filterSubjectId];
NSMutableArray *quoteSection = [[NSMutableArray alloc] init];
NSArray *quoteToAdd = [[NSArray alloc] init];
for (QuoteMap *qm in quoteMapSection){
//get the quote_ids from quote_map for this subject_id
NSPredicate *filter = [NSPredicate predicateWithFormat:#"quote_id == %#", qm.quote_id];
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter];
[quoteSection addObject:quoteToAdd];
}
return quoteSection;
}
This is where I call it:
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *myQuotes = [appDelegate getQuotesFromSubId:selectedSubject.subject_id];
NSMutableArray *mArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mArray;
NSMutableArray *mutableArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mutableArray;
I got the following error
2016-02-23 00:24:20.383 Quotes[10631:3698114] -[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0
2016-02-23 00:24:29.164 Quotes[10631:3698114] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0'
*** First throw call stack:
(0x182b55900 0x1821c3f80 0x182b5c61c 0x182b595b8 0x182a5d68c 0x100078b2c 0x1000642d0 0x187cb17f4 0x187cb1f8c 0x187b9fc90 0x187ba2e88 0x187977284 0x187883394 0x187882e90 0x187882d18 0x185259c00 0x10011dbb0 0x100123658 0x182b0cbb0 0x182b0aa18 0x182a39680 0x183f48088 0x1878b0d90 0x100040398 0x1825da8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
You are sending -excerpt to the members (myQuote) of myQuotes. The runtime says that NSArray (NSArrayI is an internal subclass) instances cannot understand -excerpt.
So the type of the member is NSArray. We cannot know, why you have instances of NSArray in the array MyQuotes, because we do not see that code. Likely that happened when you tried to add new quotes to the quotes property and incidentally added the whole array instead of its members.
To your edit:
This is wrong:
NSArray *quoteToAdd = [[NSArray alloc] init]; // This is an array. It identifier should be quote*s*ToAdd
// BTW: This above code is meaningless, because you do not need to create an array instance. Simply omit "[[NSArray alloc] init]". But this is not your problem.
for (QuoteMap *qm in quoteMapSection){
…
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter]; // filtered array returns an *array*
[quoteSection addObject:quoteToAdd]; // You add the *array* instead of the member of the array.
}
What you get back is an array. Then you add the array itself (not its members) to the existing array. As result you get an array that contains an array.
Simply change …
[quoteSection addObject:quoteToAdd];
… to:
[quoteSection addObjectsFromArray:quoteToAdd];
(And change the reference name to a plural form for better readability.)

Remove object from an array stored in a singleton

Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?
That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)

NSInvalidArgumentException: object cannot be nil

I am relatively new to Xcode and am trying to write an app that will play a video from iTunes from a tutorial but my app keeps crashing.
It is giving me the error code:
'NSInvalidArgumentException', reason: '* -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
I used breakpoints and found where the problem is but I'm not sure how to fix it. The line that is giving me problems is:
[list addObject:[item valueForProperty:MPMediaItemPropertyAssetURL]];
Does anyone have any ideas? Below is the .m file code:
#implementation iPodLibrary
+(NSArray *) movieList {
NSMutableArray *list =
[[[NSMutableArray allocWithZone:NULL] init] autorelease];
MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:[MPMediaPropertyPredicate
predicateWithValue:[NSNumber numberWithInteger:(MPMediaTypeAny ^ MPMediaTypeAnyAudio)]
forProperty:MPMediaItemPropertyMediaType]];
for (MPMediaItem* item in [query items]) {
[list addObject:[item valueForProperty:MPMediaItemPropertyAssetURL]];
}
[query release];
return list;
}
+ (NSString *) movieTitle:(NSURL *)aURL {
NSString *aTitle = nil;
if (aURL) {
AVAsset* aAsset = [[AVURLAsset allocWithZone:NULL]
initWithURL:aURL options:nil];
for (AVMetadataItem* metadata in [aAsset commonMetadata]) {
if ([[metadata commonKey] isEqualToString:AVMetadataCommonKeyTitle]) {
aTitle = [metadata stringValue];
break;
}
}
}
return aTitle;
}
#end
Check that [item valueForProperty:MPMediaItemPropertyAssetURL] is actually returning a value. NSMutableArray does not allow the addition of a nil value with the addObject method. See NSMutableArray addObject: documentation
You can check if it is null like so:
for (MPMediaItem* item in [query items]) {
id itemValue = [item valueForProperty:MPMediaItemPropertyAssetURL];
[list addObject:itemValue]; // break point this line and see what itemValue is.
}
In Objective C you cannot put nil values into NSArray or NSDictionary.

addObjectsFromArray: not copying into global NSMutableArray

So here is a partial sample of the relevant code.
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
- (IBAction)lookForRadioButtons:(id)sender {
// NSLog(#"Testing");
NSError *error;
NSString *radiostr = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"getRadios" ofType:#"txt"] encoding:NSASCIIStringEncoding error: &error] ;
if (radiostr == nil)
{
NSLog (#"Error! %#", error);
}
else
{
NSLog(#"%#",radiostr);
NSString *radiotxt= [webView stringByEvaluatingJavaScriptFromString:radiostr];
NSLog(#"%#", radiotxt);
NSArray *myRadios = [radiotxt componentsSeparatedByString:#"::"];
[radioInputArray addObjectsFromArray:myRadios];
NSLog(#"%d", myRadios.count);
NSLog(#"Number of buttons in global radio array %d", radioInputArray.count);
NSLog(#"%d", scrollViewer.subviews.count);
}
}
So it throws no exceptions and seems to work properly except after addObjectsFromArray:, my count in the global NSMutableArray is 0 (the count in the myRadios = 56). I am pretty sure they should be equal at this point but are not. I have declared my NSMutableArray up near the top so that it can be globally accessed. Am I missing something such as allocating and initializing this? Does it not do that automatically like in C#? Again, this is my first foray into the Objective-C world from Windows programming so please be gentle yet feel free to be critical.
Your two global arrays are not initialized.
The lines
static NSMutableArray *radioInputArray;
static NSMutableArray *buttonsArray;
just define the two variables as pointers to NSMutableArray, so you need to get them to point at an actual instance of the class NSMutableArray.
Somewhere in your initialization code, or through an accessor (best if a class method), you should set the variables to an empty, newly allocated NSMutableArray.
Here is a way to do it:
+ (NSMutableArray*)radioInputArray
{
if (!radioInputArray) {
radioInputArray = [[NSMutableArray alloc] init];
}
return radioInputArray;
}
Then use the accessor in your code instead of the global variable.
It may happen if your radioInputArray is nil,
you didn't initialize the array
you need to add
[[radioInputArray alloc] init];
before you do anything with radioInputArray
Good place for initialising object is "init" method in Global class
Ex.
-(id)init
{
if (self=[super init]) {
self.globalAllArtworkArray=[[NSMutableArray alloc] init];
self.globalCollectionArray=[[NSMutableArray alloc] init];
self.globalLookbookArray=[[NSMutableArray alloc] init];
}
return self;
}
+(ASNGlobalClass *)shareManager
{
static ASNGlobalClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}

Initialization of an NSDictionary in Objective C (for iOS)

I am relatively new to Objective-C and now I have a problem in my iPhone app that I don't fully understand.
I try to use a NSMutableDictionary, this does not seem to work as i expect for some reason. When I run the debugger and do po numberToCallerMap to see the dictionary, I get an exception. I have read the documentation for NSMutableDictionary on how to initialize it, but I can not see what I am doing wrong. Help and advice are appreciated. The variable causing me problem is numberToCallerMap, here is the relevant function:
- (void)setData:(NSString*)value{
[list release];
list = [[NSMutableArray alloc] init];
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSMutableDictionary* numberToCallerMap;
CallerInfo* caller;
NSDictionary* callerInfo;
#try {
NSArray *array = (NSArray*)[json objectWithString:value];
// reading all the items in the array one by one
numberToCallerMap = [NSMutableDictionary dictionary];
for (id *item in array) {
// if the item is NSDictionary (in this case ... different json file will probably have a different class)
NSDictionary *dict2 = (NSDictionary *) item;
CallInfo *data = [CallInfo alloc];
[data initFromDictionary:dict2];
callerInfo = (NSDictionary*)[dict2 valueForKey:#"caller"] ;
//Here, we want the phonenumber to be part of the CallerInfo object instead.
// It is sent from the server as part of the Call-object
NSString* number = (NSString*)[dict2 valueForKey:#"phoneNumber"];
[callerInfo setValue:number forKey:#"phoneNumber"];
caller = (CallerInfo*)[numberToCallerMap valueForKey:number];
if(caller == nil || [caller isKindOfClass:[NSNull class]]){
caller = [CallerInfo alloc];
[caller initFromDictionary:callerInfo];
[numberToCallerMap setValue:caller forKey:number];
[list insertObject:caller atIndex:0];
}
[caller addRecentCall:data];
}
}
#catch (NSException * e) {
[list release];
list = [[NSMutableArray alloc] init];
}
#finally {
[numberToCallerMap release];
}
}
This is probably not the only problem, but you are not alloc-ing your numberToCallerMap dictionary, you are getting it from a convenience class method -- [NSMutableDictionary dictionary] -- that returns it autoreleased. So you should not call release on it yourself.