I'm new to Objective C and having trouble understanding why I am getting this error. I've checked other similar questions, but haven't been able to resolve the issue.
The error is "-[NSConcreteMutableData base64Decoded]: unrecognized selector sent to instance 0x6e15610"
Here is a snippet of the problem code, where the call to base64Decoded is causing the crash.
#import "DDData.h"
- (NSString *)decodeBase64:(NSString *)input
{
NSData* dataDecoded = [[input dataUsingEncoding:NSUTF8StringEncoding] base64Decoded];
return [NSString stringWithUTF8String:[dataDecoded bytes]];
}
And in DDData.h:
#import <Foundation/Foundation.h>
#interface NSData (DDData)
- (NSData *)base64Decoded;
#end
and DDData.m:
#implementation NSData (DDData)
- (NSData *)base64Decoded
{
// Excluding function code, as it never gets to here
}
#end
Just a note that the Project has ARC enabled. Any ideas as to what might be the issue here? Thanks.
EDIT: I have adjusted the above code to help debug the error:
NSData* dataDecoded = [input dataUsingEncoding:NSUTF8StringEncoding];
[dataDecoded base64Decoded];
dataDecoded gets a value from dataUsingEncoding, it is not nil when the call to base64Decoded is made. When I step over to the called to base64Decoded, it crashes.
Insert a break point in your code and step through it and you'll see exactly where it breaks.
You may also want to check that the DDData files are properly included in your project by looking at the target membership of those files, the .m should be ticked.
SimonH pointed out the solution correctly in one of the sub-comments. I was having the same problem with a custom method i defined in a NSData category. The solution better explained:
Make sure the .m file is included in the projects Build Phases->Compile Sources.
Right click on the .m file in the project navigator and click "Show file Inspector". Under File Inspector make sure you check the target you are building for otherwise it wont be included and the calling that method will crash.
You get that kind of message if you try to execute an undefined method on an object. Try it like this:
NSData *dataDecoded = [[input dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
There is no base64Decoded method as far as I know, but there is base64EncodedString. So when you send the base64Decoded message to your NSData object, it isn't recognized because it's simply not there.
Follow below debugging steps to resolve it.
Put breakpoints in your code and check step by step where that is breaking.
Also, check if you have added DDData.m source file in your project target properly.
You should also check the object presence before using it. Check below sample code.
- (NSString *)decodeBase64:(NSString *)input {
if(input) {
NSData *utfData = [input dataUsingEncoding:NSUTF8StringEncoding];
if(utfDFata) {
NSData* dataDecoded = [utfDFata base64Decoded];
return [NSString stringWithUTF8String:[dataDecoded bytes]];
}
}
Related
I'm experiencing an access problem in my Cordova plugin: my NSFileHandle "loses" context between Cordova calls, and I end up with either an EXC_BAD_ACCESS, a SIGABRT or an Unrecognized Selector sent to instance error. Debugging and digging inside Obj-C's documentation has given me no lead on this, so I'd appreciate your help here very much!
Here's my code. First - the interface:
#interface MyPlugin : CDVPlugin
...
- (void) startWriting:(CDVInvokedUrlCommand*)command;
- (void) stopWriting:(CDVInvokedUrlCommand*)command;
#end
And the implementation:
....
static NSFileHandle *logFile;
#implementation MyPlugin
- (void) startWriting:(CDVInvokedUrlCommand*)command{
logFile = [NSFileHandle fileHandleForWritingAtPath:#"path_to_my_file"];
NSData nsData = [#"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
[logFile writeData:nsData];
}
- (void) stopWriting:(CDVInvokedUrlCommand*)command{
NSData nsData = [#"Goodbye World!" dataUsingEncoding:NSUTF8StringEncoding];
[logFile writeData:nsData];
}
I call startWriting and then stopWriting using cordova.exec. The error occurs on the last line of stopWriting. There were a few times that the problem miraculously disappeared, but in most cases I get one of the aforementioned errors.
It appears that my logFile object closes the file seamlessly, but according to iOS documentation, this usually happens when the NSFileHandle object is deallocated, whereas my object is declared as static, and is not supposed to be deallocated as long as my plugin lives (plus, I see in the XCode debugger that it is still allocated).
What, in your opinion, causes my NSFileHandle object to "lose" the actual file?
Imho - logFile is released once function finishes its job. You should change your code to something like
if (logFile==nil) logFile = [NSFileHandle fileHandleForWritingAtPath:#"path_to_my_file"];
or manually retain/release the logFile object.
I'm using the AudioEncode plugin for PhoneGap (Cordova) on iOS. After updating a couple of lines for a newer version of Cordova, it appears to be correctly encoding wav files as m4a. In the Xcode console I see:
AVAssetExportSessionStatusCompleted
doing success callback
When I look at the file system on the phone, the wav file has indeed become a m4a. However, the success callback (where I upload the file to a server) is never run. This is the relevant code in the plugin:
-(void) doSuccessCallback:(NSString*)path {
NSLog(#"doing success callback");
NSString* jsCallback = [NSString stringWithFormat:#"%#(\"%#\");", self.successCallback, path];
[self writeJavascript: jsCallback];
[self.successCallback release];
}
My code in the app goes like this:
function encodeSuccess (path) {
console.log('Audio encoded to M4A! Preparing to upload...')
// file transfer code...
}
console.log('Preparing to encode audio file...')
window.plugins.AudioEncode.encodeAudio(entry.fullPath, encodeSuccess, fail)
I'm assuming the doSuccessCallback function in the plugin needs to be updated, but I don't have experience with Objective C or PhoneGap plugins, so I'm stuck at this point.
Any ideas?
UPDATE
In the Objective C function posted above, I tried logging self.successCallback, and it logged as <null>. Then I went up to the top of the main encodeAudio function, and the argument which is assigned to self.successCallback ([arguments objectAtIndex:1]) also logs as <null>. So, it seems to me that the callbacks are not being passed into the main function successfully.
This is the AudioEncode.h file, maybe someone can spot the problem here:
#interface AudioEncode : CDVPlugin {
NSString* successCallback;
NSString* failCallback;
}
#property (nonatomic, retain) NSString* successCallback;
#property (nonatomic, retain) NSString* failCallback;
- (void)encodeAudio:(NSArray*)arguments withDict:(NSDictionary*)options;
Ok, I figured this out by reading the basic examples in the Cordova Plugin Development Guide closely. The problem was with the ordering of parameters for cordova.exec(), which must have changed recently.
I plan to submit a pull request on GitHub with a working version of the plugin, but for now, here's the basic solution.
Before asking this question, I had updated the imports in AudioEncode.h from #import <PhoneGap/PGPlugin.h> to:
#import <Cordova/CDVPlugin.h>
#import <Cordova/CDVPluginResult.h>
Any reference to PGPlugin should also be updated to CDVPlugin, and PhoneGap should become cordova.
Here's the crux of the problem: in AudioEncode.js, cordova.exec() (where the original plugin calls PhoneGap.exec()) needs to be called like this:
AudioEncode.prototype.encodeAudio = function(audioPath, successCallback, failCallback) {
cordova.exec(successCallback, failCallback, "AudioEncode", "encodeAudio", [audioPath]);
};
If you don't order the parameters like this, the callbacks won't be passed in (although audioPath was...). Look at the docs for more details, but the parameters have to be the two callbacks first, the module name, the module action, and finally an array of extra parameters.
Then, you'll need to read in the parameters in the main encodeAudio function like this:
self.callback = [[arguments objectAtIndex:0] retain];
NSString* audioPath = [arguments objectAtIndex:1];
Note that there is only one callback object now, which contains references to the success and fail callbacks. This means that whenever the plugin sets up variables for successCallback and failCallback, you now only need callback (e.g. #synthesize callback). This is also declared in the AudioEncode.h file with #interface and #property.
Now, when actually firing the callbacks & returning data (in the doSuccessCallback and doFailCallback functions), you need to use CDVPluginResult, like this:
CDVPluginResult* pluginResult = nil;
NSString* javaScript = nil;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:path];
javaScript = [pluginResult toSuccessCallbackString:self.callback];
[self writeJavascript: javaScript];
[self.callback release];
Until I get the updated module up on GitHub, this should help anyone to get the plugin working.
I'm making a small iOS application which requests a JSON file with ASIHTTPRequest and I want it to be parsed by JSONKit. I'm using the following code:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
// yada yada
NSURL *url = [NSURL URLWithString:#"http://localhost/file.json"]; // the file is a valid Json
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setTimeOutSeconds:20]; // Response takes too long on local machine
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
responseString = [request responseString]; // request type: ASIREQUEST, responseString is declared as NSString
NSLog(#"%# \n", [responseString class]); // NSCFString
NSDictionary *deserializedData = [responseString objectFromJSONString];
}
However, I'm seeing the following exception when the application runs:
[7646:207] -[NSCFString objectFromJSONString]: unrecognized selector sent to instance 0xdba0000
[7646:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-`[NSCFString objectFromJSONString]: unrecognized selector sent to instance 0xdba0000'`
What could be causing this exception? Why is it showing that I'm using an NSCFString here, even though I'm specifying an NSString?
NSString is something known as a class cluster. What this basically means is that when you construct an NSString you will get back something that is guaranteed to behave like an NSString as defined in its documentation, but could be implemented by some other class. In this case NSCFString is the type the system decided to return.
Anything that takes an NSString will take anything that is part of that cluster.
From looking at your code, ASI must define objectFromJSONString as part of a category on NSString. This will work perfectly fine with the clustering system, are you loading a static library that includes ASI? If so you need to add -all_load and -ObjC to your linker flags in order to get categories working from external libraries.
Edit:
Based on your comments then your solution is making sure the JSON library is included in your project. I'm assuming the objectFromJSONString was copy/pasted from some sample somewhere. It is part of the JSONKit library. Make sure both JSONKit.m and JSONKit.h are present in your project.
Update:
JSONKit is included (both .h and .m) but still the same error...
you are right, i just copied it because it seemed to do its job :)
You've included JSONKit.h, but you forgot to include JSONKit.m in the project. It compiles fine, but the implementation isn't there at runtime.
I had same problem because I enabled ARC and JSonKit doesn't support it after rectifying it worked.
Also when you include JSonKit.h and .m files please make sure you check target box too.
JSONKit declares a category called JSONKitDeserializing on NSString in order to allow you to call objectFromJSONString. Make sure you've included JSONKit.h in your source file where you are trying to use objectFromJSONString.
Just to begin, I've read Read text file in a folder and other troubleshooting things on here, and I've followed the advice previously given.
However, my program still can't find my .txt file.
I've checked the name, the bundle resources, and the file itself, and everything seems to be in order. My code runs fine, and I get my own error message when it can't find the file. No crashes or anything out of the ordinary.
I've also changed the Preferences/Locations/Build Location to "Place Build Products in locations specified by targets." which was an issue before.
Any additional help?
Thanks in advance!
#import "FileArrayControl.h"
#implementation FileArrayControl
#synthesize partArray, arrayContent;
- (NSArray *) setFileToArray {
NSString *stemFile = [[NSBundle mainBundle] pathForResource:#"stemList.txt" ofType:#"txt" inDirectory:#"stemIDv0"];
if (stemFile != nil) {
// ...stuff...
} else {
NSLog(#"Error, file not found!");
}
NSLog(#"%#",partArray);
return partArray;
}
#end
You're incorrectly using pathForResource:ofType:inDirectory:. This method should not include the extension in the first parameter. So it would be:
NSString *stemFile = [[NSBundle mainBundle] pathForResource:#"stemList" ofType:#"txt" inDirectory:#"stemIDv0"];
I'm developing an iOS application that needs to deploy to iOS 3.1.3. I need to extend some of the functionality of the NSData class and am using the following code inside NSData+Base64 (truncated to show the interesting part):
NSData+Base64.h
[...]
#interface NSData (Base64)
+ (NSData *)dataFromBase64String:(NSString *)aString;
- (NSString *)base64EncodedString;
#end
NSData+Base64.m
#implementation NSData (Base64)
[...]
//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
// receiver.
//
- (NSString *)base64EncodedString
{
size_t outputLength;
char *outputBuffer =
NewBase64Encode([self bytes], [self length], true, &outputLength);
NSString *result =
[[[NSString alloc]
initWithBytes:outputBuffer
length:outputLength
encoding:NSASCIIStringEncoding]
autorelease];
free(outputBuffer);
return result;
}
#end
However, when I try to message this selector:
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedString];
I get the following error:
-[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70
2010-11-09 13:44:41.443 SpringboardApplication[21318:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70'
I read a lot about iOS 3.1.x having problems with categories. I tried adding the flags -all_load and -ObjC (both separately and together) to no avail. I would really appreciate some direction of how to get this selector to work.
Thanks!
It really seems like your category isn't being compiled or linked into the same target that you're using it from. You should make sure that NSData+Base64.m is marked to be compiled by the same target that it's being used from by getting info on the two files and comparing the targets they're assigned to.
A test you can perform is to add a line with an #error error message to NSData+Base64.m, which will cause the build to fail when it gets to that file. Like this:
#error We're now compiling NSData+Base64.m
Then look and see which target fails to compile.
I had the same issue with ARC project which was linking with non-ARC module having category extension.
Fixed the issue by adding "Other Linker Flags: -all_load" in parent ARC project.
Have you #imported the header file for your category? I know it sounds simple, but I forget nearly every time.
There is a great post on The Carbon Emitter about about handling categories in iOS. It details an easy way to handle importing categories to your project.
Make a file containing all of your category imports, in this example it is Extensions.h:
#import "NSDate+Formatting.h"
#import "UIFonts+MyFonts.h"
#import "UIViewController+Tourbot.h"
Add import your file in AppName-Prefix.pch:
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
#import "Extensions.h" // Add import here
#endif
In My case when I got this error I simply added the .m file in the Compiled resources, and it get worked. This can be achieved by selecting target project->Build Phases->Compile Sources. Then you click on the + button from its bottom left. In this case you may add 'NSData+Base64.m' file to the compile sources. Then you clean your project and run. I guess this may help.