I am a beginner in Objective-C and do struggle a bit with dictionaries and arrays.
My goal is to create a table view where every cell contains a picture from a server. The url for the pic comes from a JSON call. I downloaded the example from Apple called "lazy table view" and tried to merge it with the one I found inside a Standford iOS Class. So far everything works, besides the picture. Xcode throws the following exception, once I try to access the icon:
-[__NSCFDictionary Icon]: unrecognized selector sent to instance 0x10b845ff0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary Icon]: unrecognized selector sent to instance 0x10b845ff0'
The code in question looks like this:
#interface FlickrPhotosTVC : UITableViewController
#property (nonatomic, strong) NSArray *photos;
#end
#interface ClRecord : NSObject
#property (nonatomic, strong) UIImage *Icon;
#define FLICKR_PHOTO_TITLE #"title"
ClRecord *clRecord = self.photos[indexPath.row];
cell.textLabel.text = [clRecord valueForKeyPath:FLICKR_PHOTO_TITLE]; // works
if (clRecord.Icon) NSLog(#"test"); // throws exeption
Somehow this seems to be releated with the types. Trying to access an array, while accesing a dictionory or similar. I found some releated posts on stack, but could not solve it so far.
Thank you for any help!
The objects stored in self.photos seems to be dictionaries objects and not ClRecord objects. The error you get say that you are calling Icon method on a dictionary object.
Check where you fill your photos array to make sure you put ClRecord objects in it and not dictionaries objects.
You could do NSLog(#"My photos array is ---%#",self.photos);
This will give you what exactly you have in your array.
Case 1: If you have a dictionary objects then :- You should do NSDictionary *dict = self.photos[indexPath.row]; ClRecord *clRecord = [dict objectForKey:#"YourKeyForImage"];
Case2: If you have images as you expect, then make sure you import ClRecord header file, you have non nil object at your specified [indexPath.row] index and other necessary initializations.
Hope this helps. Link1
Related
I am getting this error when trying to add the NSString object named object to my array :
erminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x7fc48ae52ba0'
Not sure why this is so. I was originally using NSArray to changed it to NSMutableArray but I am still having problems with it. Code is below:
-(NSMutableArray *)getReplyArrayForMessage: (NSString *)message {
//get the array
NSMutableArray *replies = [self.messageDictionary objectForKey:message];
NSLog(#"%#",replies);
//if no replies we init the base set
if([replies count]==0) {
//get the base array
//this also works if a key just isn't in the dictonary
return replies=[self getBaseArray];
}
else {
//add the other message
NSString *object = #"Send a different message";
[replies addObject:object];
return replies;
}
}
If anyone could give me a pointer to why this is happening I would appreciate it. Noob here.
The array is immutable (NSArray), not mutable (NSMutableArray). You can create a mutable array from an immutable array using mutableCopy:
NSMutableArray *replies = [[self.messageDictionary objectForKey:message] mutableCopy];
I know this from the exception text:
-[__NSArrayI addObject:]: unrecognized selector sent to instance ...
^^^^^^^^^^
EDIT I've missed off some important information from my original answer.
Having used mutableCopy you now have a copy of the array and the original array (from self.messageDictionary objectForKey:message) will remain unchanged after you add your element. This is almost certainly not what you intended.
As I've mentioned in the comments below, you probably want to create a custom object to hold these details (or an array of custom objects) that should be created from the message dictionary as soon as you receive it. The effort required to create a custom object will pay for itself tenfold in the long term.
You are trying to add an NSString to an NSArray (which looks like an NSMutableArray) since you initialized it as one. This means that the object that you store in your messageDictionary is actually of type NSArray, and not NSMutabelArray. So, assigning it to an object of type NSMutableArray won't actually change its type and make it mutable.
It's an easy fix:
NSMutableArray *replies = [[NSMutableArray alloc] initWithArray:(NSArray *) [self.messageDictionary objectForKey:message]];
Or you can go with mutableCopy (as suggested by #trojanfoe) which is a shortcut but will lead to the same result.
I can tell because the error message says -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x7fc48ae52ba0'. Notice the I at the end of __NSArrayI, this means that this array is actually immutable, so you can't add objects to it.
I am using a NSArray to store the names of pictures that I want to use in a UITableViewCell. When I pull the table view too much to scroll the app crashes and gives me:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xc).
This is what I'm using to initialize my Array:
In my .h:
#property (retain, nonatomic) NSArray *imagesToDisplay;
In my .m:
(viewDidLoad)
_imagesToDisplay = #[#[#"one.png", #"two.png", #"three.png"],
#[#"four.png", #"five.png", #"six.png"],
#[#"seven.png", #"eight.png", #"nine.png"],
#[#"ten.png", #"eleven.png", #"twelve.png"],
#[#"thirteen.png", #"fourteen.png", #"fifteen.png"],
#[#"sixthteen.png", #"seventeen.png", #"eighteen.png"]
];
This is what I'm doing to retrieve the image and set the image view as those images:
int row = [indexPath row];
cell.image1.image = [UIImage imageNamed:self.imagesToDisplay[row][0]];
cell.image2.image = [UIImage imageNamed:self.imagesToDisplay[row][1]];
cell.image3.image = [UIImage imageNamed:self.imagesToDisplay[row][2]];
Even when I comment out the different cell.image lines they still give me the thread 1 error. My specific error is
Thread 1: EXC_BAD_ACCESS (code=2, address=0xc).
I'm new to objective c so anything helps. Thank you in advance!
I'm not sure if this is merely an exercise in multi-dimensional arrays but I would recommend against this.
Your first step should be to create a proper data model (class object) for your table. For example:
#interface ImageRow : NSObject
#property (nonatomic, copy) NSArray *imagePaths;
- (id)initWithArray:(NSArray *)array;
- (NSString)imagePathAtIndex:(NSUInteger)index;
#end
Then you would store an NSArray of ImageRows instead of the multi-dimensional array. Fundamentally, you are accomplishing the same thing but it's both more clear and more extensible.
Your crash EXC_BAD_ACCESS is typically when you try to access an object that is nil when you're expecting it not to be. In most cases, Obj-C will handle it gracefully but there are times where it does not.
The crash is because you are not retaining an autoreleased object. When you say a property is retain, the memory management is only done for you if you use its setter, typically by using self.property =. You are instead accessing the backing ivar directly. Because array literals are autoreleased and you have failed to retain it, the array is autoreleased and _imagesToDisplay points to garbage. Add a retain call or use its setter to fix this.
Also SiLo is right about the data model.
I am building my project using a master detail VC template with core data.
I am trying to set the values for my attributes in the detail window.
When transitioning from the MasterViewController to the FoodDetailViewController, my NSString attribute works perfectly.
When I add code to add the calorie value attribute (set up as integer 16) my app crashes.
I generated a class file for my entity and the calories property is declared like this:
#property (nonatomic, retain) NSNumber * calories;
This is how I set up the transition.
In MasterViewController.m in prepareForSegue, I set the keyString as such:
foodDetailVC.calorieKeyString = #"calories”;
Then in FoodDetailViewController.h I did:
#property (nonatomic, strong) NSString *calorieKeyString;
in FoodDetailViewController.m I did:
-(void)configureView
{
if (self.managedNutritionValuesObject) {
//refresh content to ensure most up to date data
[self.managedObjectContext refreshObject:self.managedNutritionValuesObject
mergeChanges:YES];
//covert NSNumber values to NSString values
NSString *caloriesString = [NSString stringWithFormat:#"%#",
self.managedNutritionValuesObject.calories];
//set nutrition values in text fields
//these first two lines are for food text field
self.foodNameTextField.text = [self.managedNutritionValuesObject
valueForKey:self.foodNameKeyString];
self.foodNameTextField.clearsOnBeginEditing = YES;
self.caloriesTextField.text = [caloriesString valueForKey:self.calorieKeyString];
self.caloriesTextField.text = caloriesString;
}
}
the crash log says it is crashing on configure view and I get this message:
* Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
'[<__NSCFString 0x8b44e20> valueForUndefinedKey:]: this class is not key value coding-compliant for the key calories.’
Any help would be greatly appreciated,
Thanks
self.caloriesTextField.text = [self.managedNutritionValuesObject valueForKey:#"calories"];
I have a UITableView with several sections. Each section contains a different set of data: phoneNumbers, addresses....
For each of those sets I have a model: PhoneNumber, Address. They're completely different but have some methods in common.
In my UITableView I have an array containing those models/classnames:
NSMutableArray *classNames;
In the viewDidLoad of my UITableView I do some initializations for all those sections:
//section 1: PhoneNumbers
phoneNumbers = [PhoneNumbers getAllIDs];
if (phoneNumbers && (phoneNumbers.count >0)) {
[classNames addObject:#"PhoneNumber"];
[dataIDs addObject:phoneNumbers];
}
I do this again for all the other sections/models:
//section 2: Addresses
addresses = [Address getAllIDs];
if (addresses && (addresses.count >0)) {
[classNames addObject:#"Address"];
[dataIDs addObject:addresses];
}
// section 3: .....
Ok so far for initialization. This looks good and works fine.
Then later on in my cellForRowAtIndexPath I'm retrieving the actual data via those ID's
NSInteger section = [indexPath section];
NSInteger row = [indexPath row];
NSArray *rows = [dataIDs objectAtIndex:section];
NSNumber *recordID = [rows objectAtIndex:row];
I then figure out in what class we have to fetch the actual data:
Class displayedDataClass = NSClassFromString ([classNames objectAtIndex:section]);
and get the data to populate the cell.
id displayedRecord = [[displayedDataClass alloc] init];
[displayedRecord getByID:recordID];
I can then set the labels in my cell using :
[cell.someLabel setText:[displayRecord fullDesciption]];
So far so good, I succesfully abstracted everything, the cellForRowAtIndexPathdoesn't need to know where things come from, as long as those classes respond to the methods for retrieving the data for the labels (in the case above fullDesciption)
Now I need an actionButton in every Cell performing some kind of action
To make sure I understood the concept of selectors and performSelection I just quick and dirty made in action in my TableView Class:
- (void) buttonTarget {
NSLog (#"yes");
}
And in my cellForRowAtIndexPath method created a button with the following target:
button addTarget:self action:#selector(buttonTarget) forControlEvents:UIControlEventTouchUpInside];
Ok, so far so good, things work like expected. But this is not what I really wanted. The action should not be performed here, but in the actual class (PhoneNumber,Address,...).
To keep things clean I made a model Action, containing the icon for the button, a description and the selector:
#interface Action : NSObject
#property (nonatomic, strong) NSString *description;
#property (nonatomic, strong) UIImage *icon;
#property (nonatomic ) SEL selector;
#end
In my PhoneNumber class (and similar classes) the action is set to the correct selector:
Action *phoneAction = [[Action alloc] init];
phoneAction.description = NSLocalizedString(#"Call", #"Call button description");
phoneAction.icon = [UIImage imageNamed:#"phone"];
phoneAction.selector = #selector(callPhone);
Of course callPhone is implemented in the PhoneNumber class.
In my TableView I then get the actions for that cell
action = [displayedRecord action];
I then try to use that selector in my Button:
[button addTarget:displayedRecord action:[action selector] forControlEvents:UIControlEventTouchUpInside];
But here things go wrong: we never arrive in that method and I get the following error:
[UIDeviceWhiteColor callPhone]: unrecognized selector sent to instance
0x874af90 2013-12-29 23:23:03.629 thinx[27242:907] * Terminating app
due to uncaught exception 'NSInvalidArgumentException', reason:
'-[UIDeviceWhiteColor callPhone]: unrecognized selector sent to
instance 0x874af90'
Sounds like you have a zombie. When you get an action being sent to an object that makes no sense, it usually means that your object is being deallocated before you can send a message to it.
In your case, you're adding "displayedRecord" as the target for your button.
In order for that to work, you need to keep a strong reference to displayedRecord call for the lifetime of your button object. What owns your displayedRecord object?
If you can't debug this from looking at your code you can use the zombies instrument to try to figure it out.
In your unrecognized selector error you sent the message to an object called UIDeviceWhiteColor. Does that class have a method called callPhone? It seems to me that displayedRecord is not pointing to the object you think it is.
I'm developing an App which uses a DataBase managed from MagicalRecord. App displays names which user can add tapping + button. + button opens an UIAlert where user can type a new name and tapping OK name is added to a DataBase.
Trouble is that, everytime user add a new name, table has to be reloaded to display new name.
To reload table I imported CoreDataHelper and I'm using this method: (I've found everything there: https://github.com/kgudger/AssignLine )
#property (strong, nonatomic) NSMutableArray * eventiArray; // Array which populate table
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
-(void)readDataForTable {
_eventiArray = [CoreDataHelper getObjectsForEntity:#"Entity" withSortKey:#"nomeEvento" andSortAscending:YES andContext: _managedObjectContext];
[self.tableView reloadData]; }
// (Entity is the name of NSManagedObject SubClass)
// (nomeEvento is the name of NSString in NSManagedObject SubClass)
Running, it crashes saying *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Entity'' but I can't understand the reason, can somebody help me? Thank you!!!
My recommendation would be to use MagicalRecord to load the data.
-(void)readDataForTable {
self.eventiArray = [YourEntity MR_findAllSortedBy:#"nomeEvento" ascending:YES inContext:[NSManagedObjectContext MR_defaultContext]];
[self.tableView reloadData];
}
If you want to use it in context
[YourEntity MR_findAllSortedBy:#"nomeEvento" ascending:YES inContext:self.managedObjectContext];
or
[YourEntity MR_findAllSortedBy:#"nomeEvento" ascending:YES];