Object name from String in Objective-C - objective-c

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

Related

NSMutableDictionary won't save data

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
"

How to convert NSUrl to NSString?

After AVAssetExportSession has complete export video.
I have plan to garb Video Path to upload via Youtube.
but [GDataUtilities MIMETypeForFileAtPath:path defaultMIMEType:#"video/mp4"];
it only accept NSString.
Is it possible to convert NSUrl in to NSString for video file path.
i have try to use NSString *path = [ExportoutputURL absoluteString];
but it crash.
Here is the Code
- (void)exportDidFinish:(AVAssetExportSession*)session {
ExportoutputURL = session.outputURL;
_exporting = NO;
NSIndexPath *exportCellIndexPath = [NSIndexPath indexPathForRow:2 inSection:kProjectSection];
ExportCell *cell = (ExportCell*)[self.tableView cellForRowAtIndexPath:exportCellIndexPath];
cell.progressView.progress = 1.0;
[cell setProgressViewHidden:YES animated:YES];
[self updateCell:cell forRowAtIndexPath:exportCellIndexPath];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:ExportoutputURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:ExportoutputURL
completionBlock:^(NSURL *assetURL, NSError *error){
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"writeVideoToAssestsLibrary failed: %#", error);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription]
message:[error localizedRecoverySuggestion]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
else {
_showSavedVideoToAssestsLibrary = YES;
ExportCell *cell = (ExportCell*)[self.tableView cellForRowAtIndexPath:exportCellIndexPath];
[cell setDetailTextLabelHidden:NO animated:YES];
[self updateCell:cell forRowAtIndexPath:exportCellIndexPath];
NSArray *modes = [[[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, UITrackingRunLoopMode, nil] autorelease];
[self performSelector:#selector(hideCameraRollText) withObject:nil afterDelay:5.0 inModes:modes];
}
});
}];
}
[library release];
}
- (void)uploadVideoFile {
NSString *devKey = DEVELOPER_KEY;
GDataServiceGoogleYouTube *service = [self youTubeService];
[service setYouTubeDeveloperKey:devKey];
NSURL *url = [GDataServiceGoogleYouTube youTubeUploadURLForUserID:kGDataServiceDefaultUser];
// load the file data
NSString *path = [ExportoutputURL absoluteString];//[[NSBundle mainBundle] pathForResource:#"video_2451" ofType:#"mp4"];//[mFilePathField stringValue];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSString *filename = [path lastPathComponent];
// gather all the metadata needed for the mediaGroup
NSString *titleStr = #"Upload Test";//[mTitleField stringValue];
GDataMediaTitle *title = [GDataMediaTitle textConstructWithString:titleStr];
NSString *categoryStr = #"Entertainment";//[[mCategoryPopup selectedItem] representedObject];
GDataMediaCategory *category = [GDataMediaCategory mediaCategoryWithString:categoryStr];
[category setScheme:kGDataSchemeYouTubeCategory];
NSString *descStr = #"GData Description";//[mDescriptionField stringValue];
GDataMediaDescription *desc = [GDataMediaDescription textConstructWithString:descStr];
NSString *keywordsStr = #"RAGOpoR Demo";//[mKeywordsField stringValue];
GDataMediaKeywords *keywords = [GDataMediaKeywords keywordsWithString:keywordsStr];
BOOL isPrivate = NO;//([mPrivateCheckbox state] == NSOnState);
GDataYouTubeMediaGroup *mediaGroup = [GDataYouTubeMediaGroup mediaGroup];
[mediaGroup setMediaTitle:title];
[mediaGroup setMediaDescription:desc];
[mediaGroup addMediaCategory:category];
[mediaGroup setMediaKeywords:keywords];
[mediaGroup setIsPrivate:isPrivate];
NSString *mimeType = [GDataUtilities MIMETypeForFileAtPath:path
defaultMIMEType:#"video/mp4"];
// create the upload entry with the mediaGroup and the file
GDataEntryYouTubeUpload *entry;
entry = [GDataEntryYouTubeUpload uploadEntryWithMediaGroup:mediaGroup
fileHandle:fileHandle
MIMEType:mimeType
slug:filename];
SEL progressSel = #selector(ticket:hasDeliveredByteCount:ofTotalByteCount:);
[service setServiceUploadProgressSelector:progressSel];
GDataServiceTicket *ticket;
ticket = [service fetchEntryByInsertingEntry:entry
forFeedURL:url
delegate:self
didFinishSelector:#selector(uploadTicket:finishedWithEntry:error:)];
[self setUploadTicket:ticket];
GTMHTTPUploadFetcher *uploadFetcher = (GTMHTTPUploadFetcher *)[ticket objectFetcher];
}
Error EXC_BAD_ACCESS at
NSString *path = [ExportoutputURL absoluteString];
Is it possible to convert NSUrl in to NSString for video file path.
Yes. Send it an absoluteString message.
i have try to use NSString *path = [ExportoutputURL absoluteString]; but it crash.
If you want a path, send the URL a path message. A string representing a URL is generally not a valid path; if you want a path, ask it for one.
As for the crash, it does not mean absoluteString is wrong. Sending absoluteString to an NSURL object is the correct way to get an NSString object that represents the URL. The problem is somewhere else.
Error EXC_BAD_ACCESS at
NSString *path = [ExportoutputURL absoluteString];
This probably means that ExportoutputURL points to something that is not nil but is also not a valid object. It might have pointed to an NSURL object at some point, but it doesn't now.
My guess would be that the problem is this:
ExportoutputURL = session.outputURL;
You assign the URL to the ExportoutputURL instance variable, but you don't retain the object or make your own copy. Therefore, you don't own this object, which means you are not keeping it alive. It may die at any time, most probably after this method (exportDidFinish:) returns.
The crash is because you call uploadVideoFile later, after the URL object has already died. You still have a pointer to it, but that object no longer exists, so sending a message to it—any message—causes a crash.
There are three simple solutions:
Retain the URL object when you assign it to your instance variable.
Make your own copy of the URL object and assign that to the instance variable.
Declare ExportoutputURL as a property, with either the strong keyword or the copy keyword, and assign the object to the property, not the instance variable. That will call the property's setter, which, if you synthesize it or implement it correctly, will retain or copy the URL for you.
Either way, you will own the object, and that will keep it alive until you release it. Accordingly, you will need to release it when you are done with it (in dealloc, if not earlier), so that you don't leak it.
This all assumes that you are not using ARC. If you are using Xcode 4.2 or later, and can require iOS 4 or later, you should migrate your project to ARC, as it makes so many things much simpler. You would not need to retain or copy this object if you were using ARC, which means that migrating to ARC now is a fourth solution (but certainly a larger-scale one).
Use either absolutePath or path as mentioned by Miek and Nepster. Expanding on their answers, the difference between lies in the prefix.
NSString* string1 = [url absoluteString]; // #"file:///Users/jackbrown/Music/song name.mp3"
NSString* string2 = [url path]; // #"/Users/jackbrown/Music/song name.mp3"`
NSString *path = [[NSString alloc] initWithString:[url path]]; ?
Use this. I think it will help you.
In Objective c
NSString *testString = testUrl.absoluteString;
In Swift
var testString : String = testUrl.absoluteString
Simply you can do it like this.
NSString *myString = [myURL absoluteString];

NSSelectorFromString

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

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.

parsing txt from array to UILabel

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:#" "]);