Is there something wrong with this code? I'm trying to set up some buttons by defining at run time the label, the callback selector, and, later, a pointer to the UIButton itself. But with this code, I get EXC_BAD_ACCESS. It gos away if I delete the line with NSSelectorFromString. But since this is just an object being added to the dictionary, I don't understand shy it fails.
NSMutableDictionary *attachButtonDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"Attach To Job",#"keyForLabel",
NSSelectorFromString(#"attachToJob"), #"keyForSelector",
nil];
a selector is not an objc object; a selector is an opaque representation of a method name.
the program will crash when adding it to the dictionary because it cannot be messaged. for example, it cannot be retained when added.
You cannot store the selector into your NSDictionary.
Just store the string and when you build your button call NSSelectorFromString().
Best,
Christian
Edit:
NSMutableDictionary *attachButtonDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"Attach To Job",#"keyForLabel",
#"attachToJob", #"keyForSelector",
nil];
UIButton *fancyButton = [[UIButton alloc] init];
[fancyButton addTarget:self action:NSSelectorFromString([attachButtonDictionary objectForKey:#"keyForSelector"]) forControlEvents:UIControlEventTouchUpInside];
While you can't store a selector in a dictionary, you can wrap it in a NSValue.
SEL fooSel = #selector(foo);
NSValue *selWrapper = [NSValue valueWithPointer:fooSel];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:selWrapper
forKey:#"foo"];
SEL myFooSel = [[dict objectForKey:#"foo"] pointerValue];
[obj performSelector:myFooSel];
Related
I have a method that is supposed to take an NSManagedObject, copy its attributes into a dictionary, then add the dictionary to an NSMutableArray in a static NSMutableDictionary with NSManagedObjectID keys. The problem is that it crashes when I try to add to a static NSMutableDictionary and only works if I make one on the spot.
The problem is definitely related to the static NSMutableDictionary changes because I do not get the exception if I use a non-static dictionary. It's defined like this (above #implementation):
static NSMutableDictionary* changes = nil;
And here is the method:
+ (void)acceptChange: (NSManagedObject *)change{
if (!changes){
NSLog(#"Making new changes dicitonary"); //it prints this when I run
changes = [[NSDictionary alloc] init];
}
NSManagedObjectID* objectID = change.objectID;
NSMutableArray* changeArray = [changes objectForKey: objectID];
bool arrayDidNotExist = NO;
if (!changeArray){
changeArray = [[NSMutableArray alloc] init];
arrayDidNotExist = YES;
}
[changeArray addObject: [(this class's name) copyEventDictionary: change]]; //copies the NSManagedObject's attributes to an NSDictionary, assumedly works
if (arrayDidNotExist) [changes setObject: changeArray forKey: objectID];//throws the exception
//If I do the exact same line as above but do it to an [[NSMutableDictionary alloc] init] instead of the static dictionary changes, it does not throw an exception.
if (arrayDidNotExist) NSLog(#"New array created");
NSLog(#"changeArray count: %d", changeArray.count);
NSLog(#"changes dictionary count: %d", changes.count);
}
The exact exception message is this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0xa788e30'
Use NSMutableDictionary instead of NSDictionary. You are getting exception as because , NSMutableDictionary can be modified dynamically, NSDictionary cannot. .
NSMutableDictionary is subclass of NSDictionary. So all methods of NSDictionary is accessible via NSMutableDictionary object. Moreover NSMutableDictionary also adds complementary methods to modify things dynamically, such as the method setObject:forKey:
EDIT
You have initialized it using NSDictionary instead of `NSMutableDictionary.
if (!changes){
NSLog(#"Making new changes dicitonary"); //it prints this when I run
//changes = [[NSDictionary alloc] init];
^^^^^^^^^^^^^^ ------------------> Change this.
changes = [[NSMutableDictionary alloc] init];
}
[__NSDictionaryI setObject:forKey:] shows that your dictionary is immutable. You are actually initializing your dictionary as immutable. That's why its raising exception on adding an object.
Here change this line:
if (!changes){
....
changes = [[NSDictionary alloc] init];
}
to:
if (!changes){
....
changes = [[NSMutableDictionary alloc] init];
}
You declared your dictionary to be of NSMutableDictionary, so at compile time your dictionary is of NSMutable dictionary, but at run time it is NSDictionary as you allocated it as NSDictionary, to which you can not make changes, hence the exception. Please define the dictionary as :-
changes = [[NSMutableDictionary alloc] init];
If you read the description of your exception, it says the same thing.
Hope this helps.
hope someone can help me with a problem I've been wrestling with...
Using MapBox to develop a map-based app, and I want to attach an NSMutableDictionary to each of the map annotations to store additional data. I had it working but XCode kept throwing me warning about some of my data/object types, so I went through and tidied those up, and now it's broken. The idea is that on ViewDidLoad, the program runs through a set of plist dictionaries to set up each annotation correctly - that's still running okay, because my initial anno markers pop up with their correct settings. However rather than run back to the plist every time, I want to attach a dictionary to each annotation's userinfo property, which I can then use for toggling selection data and other functions. Here's my code:
NSDictionary *ExploreSteps = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ExploreSteps" ofType:#"plist"]];
for (NSString *key in [ExploreSteps allKeys])
{
//Loop through keys for each anno
NSDictionary *thisStep = [ExploreSteps objectForKey:key];
NSNumber *annoIndex = [thisStep objectForKey:#"Index"];
NSNumber *isLive = [thisStep valueForKey:#"isLive"];
NSString *annoTitle = [thisStep objectForKey:#"Title"];
NSString *annoText = [thisStep objectForKey:#"Text"];
NSString *imagefile = [thisStep objectForKey:#"Imagefile"];
double longitude = [[thisStep objectForKey:#"Longitude"] doubleValue];
double latitude = [[thisStep objectForKey:#"Latitude"] doubleValue];
NSString *pagefile = [thisStep objectForKey:#"Pagefile"];
NSString *audiofile = [thisStep objectForKey:#"Audiofile"];
CLLocationCoordinate2D annoCoord = CLLocationCoordinate2DMake(latitude, longitude);
RMAnnotation *annotation = [[RMAnnotation alloc] initWithMapView:mapView coordinate:annoCoord andTitle:annoTitle];
annotation.annotationIcon = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:imagefile ofType:#"png"]];
annotation.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
NSLog(#"Title: %#",[annotation.userInfo objectForKey:#"annoTitle"]);
[mapView addAnnotation:annotation];
}
The NSLog should spit out the annoTitle string, but instead it's giving me a null every time, and the behaviour of the rest of the app also shows that info stored in the dictionary simply isn't "getting through".
Any ideas? Thanks in advance!
ETA: Modified code for initializing the dictionary (not that it seems to make any difference to the problem!):
NSMutableDictionary *myUserInfo = [[NSMutableDictionary alloc] initWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
annotation.userInfo = myUserInfo;
NSLog(#"Title: %#",[annotation.userInfo objectForKey:#"annoTitle"]);
NSLog(#"Length: %u",[[annotation.userInfo allKeys] count]);
(Title now returns "(null)", while Length returns "1", if that's at all helpful...)
Almost certainly one of your objects is nil. You mention that allKeys] count] returns 1 so I can go further and say that your value for isLive is nil. Hence your original line:
[NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
Acts exactly the same as:
[NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", nil, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
And the dictionary takes annoIndex to be the final key-value pair.
I'd suggest that probably you want to take a mutable copy of thisStep and strip out the keys you don't want, then pass it along as the userInfo.
It's the way you are creating the NSMutableDictionary for userInfo. Take a look at this Difference between [[NSMutableDictionary alloc] initWithObjects:...] and [NSMutableDictionary dictionaryWithObjects:...]?
"
+dictionaryWithObjects: returns an autoreleased dictionary
-initWithObjects: you must release yourself
if you want the dictionary to persist as a instance variable, you should create it with an init method or retain an autoreleased version, either way you should be sure to release it in your dealloc method
"
I am trying to use the Category described in this article:
http://iphonedevelopment.blogspot.com/2008/10/shuffling-arrays.html
I have setup the following:
// NSArray+Shuffle.h
#import <Foundation/Foundation.h>
#interface NSArray (Shuffle)
-(NSArray *)shuffledArray;
#end
// NSArray+Shuffle.m
#import "NSArray+Shuffle.h"
#implementation NSArray (Shuffle)
-(NSArray *)shuffledArray
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
NSMutableArray *copy = [self mutableCopy];
while ([copy count] > 0)
{
int index = arc4random() % [copy count];
id objectToMove = [copy objectAtIndex:index];
[array addObject:objectToMove];
[copy removeObjectAtIndex:index];
}
// Using IOS 5 ARC
// [copy release];
return array;
}
#end
Then in my code that I want to use this, I imported the Category:
#import "NSArray+Shuffle.h"
Then, I attempted to use it like this:
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
NSArray *shuffler = [[NSArray alloc] shuffledArray:orderedGallary];
_pageData = [shuffler shuffledArray:orderedGallary];
But, I get the following compiler error message:
ModelController.m: error: Automatic Reference Counting Issue: Receiver type 'NSArray' for instance message does not declare a method with selector 'shuffledArray:'
Any ideas?
shuffledArray is a method that takes no parameters, it is different from shuffledArray:, which is a method that takes one parameter.
It looks like what you meant was:
NSArray* shuffled = [orderedGallery shuffledArray];
Here you are sending this message to your original array, and it returns a new array that is shuffled.
You're trying too hard. You only need to send -shuffledArray to orderedGallery.
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
_pageData = [orderedGallery shuffledArray];
See how you have declared shuffledArray not to take any arguments? Simply sending this message to any instance of NSArray will return your shuffled array.
shuffledArray does not take a parameter but is called directly on the array:
NSArray *myShuffledArray = [orderedGallery shuffledArray]
You have declared (in the .h) and defined (in the .m) a method named shuffledArray.
You are calling a method named shuffledArray: (notice the colon, which indicates an argument).
You want to be calling
NSArray *shuffled = [orderedGallery shuffledArray];
you don't need the argument because you are sending the method to the ordered array.
(There's no object that's actually a "shuffler" - independent of the array - so I wouldn't use that name as the variable name. The array is shuffling a copy of itself and returning the new shuffled array.)
i want to set the name of an object like UIButton from a string.
NSString *buttonName = [[NSString alloc] initWithString:#"someString"];
My goal is:
UIButton *someString = [[UIButton buttonWithType:UIButtonTypeCustom]retain];
how can i solve this?
You can't - variable names are resolved by the compiler well before any Objective-C code is executed. What you can do is maintain a map of strings to objects like buttons etc. using NSMutableDictionary:
NSString *string = #"someString";
[buttonMap setObject: [UIButton buttonWithType:UIButtonTypeCustom] forKey: string];
//...time passes...
[[buttonMap objectForKey: #"someString"] setEnabled: YES];
I'm actually starting to loose the will to live, this piece of code is driving me nuts!
I'm trying to get the content of mathspractice.txt into *myLabel
I'm using an array which is:
-(void)loadText
{
NSArray *wordListArray = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#”mathspractice” ofType:#”txt”]
encoding:NSMacOSRomanStringEncoding error:NULL] componentsSeparatedByString:#”\n”]];
self.theMathsPractice = wordListArray;
[wordListArray release];
}
and then I'm trying to pass it into *myLabel
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,100,960,40)];
myLabel.text = *theMathsPractice;
[myScrollView addSubview:myLabel];
[myLabel release];
}
Can anyone help?
It looks on quick inspection that your theMathsPractice is an NSArray, not an NSString, which is what you'd want to assign to the label's text property. You should at least format that array back into a string of some sort before assigning it to the label.
(Also not sure why you're dereferencing it with the * in the assignment-- I would think that would throw a compiler error, since naked non-reference Objective-C objects are not really allowed.)
I would use the following:
myLable.text = [theMathsPractice componentsJoinedByString:#" "]);