Does Missing method signature causes app abort? - objective-c

Here's the error message:
warning: 'ReaderAppDelegate' may not respond to '-checkForDatabase'
(Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments.
I put a breakpoint at the 'checkForDatabase' method in the code below, and it never got there... the app died. I'm assuming he warning above has something to do with the abort. How do I fix this? Do I have to declare 'checkForDatabase' in my .h file?
//--------------- application finished launching ----------------|
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// create the d/b or get the connection value
[self checkForDatabase];
}
//-------------- check for database or create it ----------------|
- (void)checkForDatabase {
NSFileManager *filemanager = [NSFileManager defaultManager];
NSString *databasePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingString:#"/ppcipher.s3db"];
if(![filemanager fileExistsAtPath:databasePath]) { //Database doesn't exist yet, so we create it...
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/ppcipher.s3db"];
// remainder of code goes here...
}
}

As you mention, you need to declare the function within your interface or the dynamic (i.e.: runtime) binding won't know that the method exists.

It'd be a good idea to declare the -checkForDatabase method in your class's interface, but I don't think that's the cause of your crash. Look at the console for a message that will probably indicate the cause of the crash.

You either need to declare checkForDatabase in your header file or in a private category in the implementation file. You could also define the method implementation above the implementation of applicationDidFinishLaunching.

Related

'unrecognized selector sent to instance'?

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

Why isn't [NSBundle mainBundle] working here?

I've never loaded a bundle, so I'm not sure why this is not working. I don't think it matters, but the .xib in question here is in the same Resources folder as all my other .xibs.
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"S3AsyncView" owner:self];
Returns this error:
Instance method -loadNibNamed:owner not found. Return type defaults to id
I find this error strange, because the return type of [NSBundle mainBundle] is of course NSBundle.
There is no such method in NSBundle, hence the error.
I guess you are looking for:
loadNibNamed:owner:options:
Documentation link
You can pass nil to the options, as it expect a NSDictionary
So in your case:
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"S3AsyncView" owner:self options:nil];
EDIT
If it still doesn't work, verify you have included <UIKit/UIKit.h>.
EDIT 2
Ok, now I see. You tagged your question with iOS, but now you say it's a Cocoa app.
The loadNibNamed:owner:options: is a UIKit addition, so available only on iPhone.
On Mac OS X, you'll use the + (BOOL)loadNibNamed:(NSString *)aNibName owner:(id)owner class method.
So:
NSArray *array = [ NSBundle loadNibNamed: #"whatever" owner: self ];
Three things:
Make sure that you're spelling the method name right. The error message you give shows the method name as: -loadNibNamed:owner:options, which isn't right. There should be a colon after the "options". Perhaps you missed that in pasting the name into your message, but the lesson here is to check carefully that you're using exactly the right method name, with no spelling errors, omitted parts, missing colons, etc.
Make sure that you're linking against UIKit. NSBundle is part of the Foundation framework, but the -loadNibNamed:owner:options: method comes from a UIKit Additions category on NSBundle that's part of UIKit. If you don't link against UIKit, then, NSBundle won't have that method.
I see that you've removed ios from your list of tags. If you're writing for Cocoa and trying to load a nib, see the NSNib class for some convenient methods for loading nibs.
I have come across the very same problem while fixing an issue in a low-level Cocoa/Objective-C++ framework. Strictly speaking, build issue came from this function:
bool osxNibLoadMenuNibFile()
{
const auto cvAppKitVersion = floor( NSAppKitVersionNumber );
if( cvAppKitVersion >= NSAppKitVersionNumber10_8 )
{
NSBundle * mainBundle = [NSBundle mainBundle];
NSDictionary * bundleInfoDict = [mainBundle infoDictionary];
if( bundleInfoDict != nil )
{
NSString * mainNibFleNameStr = [bundleInfoDict valueForKey:#"NSMainNibFile"];
if( mainNibFleNameStr != nil )
{
if( [mainBundle loadNibNamed:mainNibFleNameStr owner:[NSApplication sharedApplication] topLevelObjects:nil] )
{
return true;
}
}
}
}
return false;
}
Clang gave me:
warning: instance method '-loadNibNamed:owner:topLevelObjects:' not found (return type defaults to 'id') [-Wobjc-method-access]
The issue was not a build configuration, as all standard frameworks were there already. The issue was more trivial: the definition of that single method is present in a separate header. So please be sure to add:
#import <AppKit/NSNibLoading.h>
which contains:
#interface NSBundle(NSNibLoading)
- (BOOL)loadNibNamed:(NSNibName)nibName owner:(nullable id)owner topLevelObjects:(NSArray * _Nullable * _Nullable)topLevelObjects API_AVAILABLE(macos(10.8));
#end
Interestingly enough, CLion gaves me "unused import directive" even though I definitely use it. Hope this helps someone!

Objective-C: need to call a method in another class from FinishedLaunching

I got this far... here is my code:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// create the d/b or get the connection value
SQLiteDB *dbInstance = [[SQLiteDB alloc] init];
}
Now, the question is: this bit of code is supposed to check to see if a database exists, and if not, create it. My problem is I am having a problem figuring out exactly how to write the first line of the called method and where to place it in SQLiteDB.m. Is this an instance method (-) or a class method (+)?
I'm sorry for being so lame on this, but once I see it, I'll have the hang of it... the rest of the code is written in C#, and I can handle the conversion to Obj_C.
The following is a method to copy an existing database from your Bundle to the Documents directory, but can easily be adapted for a new database. Just use the fileExistsAtPath: method logic below and replace the actions to take with your custom database creation code.
Put this in your AppDelegate.m file:
- (void)prepareDatabase
{
//add Database Versioning check to see if the resources database is newer
// generally as simple as naming your database with a version on the end
NSFileManager *filemanager = [NSFileManager defaultManager];
NSString *databasePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:#"/YOURDATABASE.s3db"];
if(![filemanager fileExistsAtPath:databasePath]) {
//Database doesn't exist yet, so we copy it from our resources
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/YOURDATABASE.s3db"];
if([filemanager copyItemAtPath:defaultDBPath toPath:databasePath error:nil]) {
NSLog(#"Database Copied from resources");
} else {
NSLog(#"Database copy FAILED from %# to %#",defaultDBPath,databasePath);
}
}
}
Then in your applicationDidFinishLaunching: method call this:
[self prepareDatabase];
I'm assuming that by "see if the database exists" you mean "see if the database file exists on disk". For that, you use method fileExistsAtPath: of class NSFileManager. It's an instance method, but you can use [NSFileManager defaultIntance].
Calculate the path to the file first (it's up to you how). Check if the file exists. If yes, open the file, if not, create a new database with that filename.

Create folder/directory in Objective-C/cocoa

I have this code for creating a folder/directory in Objective-C/cocoa.
if(![fileManager fileExistsAtPath:directory isDirectory:&isDir])
if(![fileManager createDirectoryAtPath:directory attributes:nil])
NSLog(#"Error: Create folder failed %#", directory);
It works fine, but I got creatDirectoryAtPath:attributes is deprecated warning message.
What's the newest way of making a directory builder in Cocoa/Objective-c?
SOLVED
BOOL isDir;
NSFileManager *fileManager= [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:directory isDirectory:&isDir])
if(![fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:NULL])
NSLog(#"Error: Create folder failed %#", directory);
Found in the documentation:
-[NSFileManager createDirectoryAtPath:withIntermediateDirectories:attributes:error:]
Your solution is correct, though Apple includes an important note within NSFileManager.h:
/* The following methods are of limited utility. Attempting to predicate behavior
based on the current state of the filesystem or a particular file on the
filesystem is encouraging odd behavior in the face of filesystem race conditions.
It's far better to attempt an operation (like loading a file or creating a
directory) and handle the error gracefully than it is to try to figure out ahead
of time whether the operation will succeed. */
- (BOOL)fileExistsAtPath:(NSString *)path;
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;
- (BOOL)isReadableFileAtPath:(NSString *)path;
- (BOOL)isWritableFileAtPath:(NSString *)path;
- (BOOL)isExecutableFileAtPath:(NSString *)path;
- (BOOL)isDeletableFileAtPath:(NSString *)path;
Essentially, if multiple threads/processes are modifying the file system simultaneously the state could change in between calling fileExistsAtPath:isDirectory: and calling createDirectoryAtPath:withIntermediateDirectories:, so it is superfluous and possibly dangerous to call fileExistsAtPath:isDirectory: in this context.
For your needs and within the limited scope of your question it likely would not be a problem, but the following solution is both simpler and offers less of a chance of future issues arising:
NSFileManager *fileManager= [NSFileManager defaultManager];
NSError *error = nil;
if(![fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error]) {
// An error has occurred, do something to handle it
NSLog(#"Failed to create directory \"%#\". Error: %#", directory, error);
}
Also note from Apple's documentation:
Return Value
YES if the directory was created, YES if createIntermediates is set
and the directory already exists), or NO if an error occurred.
So, setting createIntermediates to YES, which you already do, is a de facto check of whether the directory already exists.
Thought I'd add to this and mention some more from the documentation about using the +defaultManager method:
In iOS and Mac OS X v 10.5 and later you should consider using [[NSFileManager alloc] init] rather than the singleton method defaultManager. Instances of NSFileManager are considered thread-safe when created using [[NSFileManager alloc] init].
You may prefer to work with the NSFileManager method:
createDirectoryAtURL:withIntermediateDirectories:attributes:error:
It works with URL's instead of path strings.

how to call a mothod defined in another class

Hi
I have a class named as root:
In root.h :-
#import "UIKit/UIKit.h"
#import "AVFoundation/AVFoundation.h"
#import "AudioToolbox/AudioToolbox.h"
#interface root : UIView {
}
+(void)somefunction:(BOOL) sf;
#end
in root.m the definition of somefunction is as follows
-(void)somefunction:(BOOL) sf {
//AVAudioPlayer *myExampleSound; //this variable can be named differently
if ( issoundon==TRUE) {
NSString *path =[[NSBundle mainBundle] pathForResource:"bg" ofType:#"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID(
(CFURLRef) [NSURL fileURLWithPath:path], &soundID);
AudioServicesPlaySystemSound(soundID);
}
else{
NSString *path =[[NSBundle mainBundle] pathForResource:nil ofType:#"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID(
(CFURLRef) [NSURL fileURLWithPath:path], &soundID);
AudioServicesPlaySystemSound(soundID);
}
now i have imported root.h in another class and i am calling the "somefunction" as follows
bool abc=true;
[root somefunction:true];
but at this point my app terminates(crashes).
basically i am trying to set background music to my app (as the game starts) and in the middle of the game i allow user to switch of the sound.(it is crashing even i am calling the function in delegate of the view.)
please tell me what is happening wrong coz my code is compiling properly(with a few warning though).
Sure, your code compiles successfully. But you know what? There is a typo in your code which is causing your program to crash. Never assume your code has no typos just because it compiles successfully.
In your root.m file you have this:
-(void)somefunction:(BOOL) sf {
It should be a +, not a -, like in your header file:
+(void)somefunction:(BOOL) sf {
There might be a discrepancy between the C type bool and the Objective-C type BOOL too, but I'm not too sure about that:
bool abc=true; // Shouldn't this be BOOL abc = YES, and
[root somefunction:true]; // shouldn't you be passing abc here?
Your code is incomplete, and without context it's really hard to say what the bug might be. However:
You haven't defined issoundon anywhere
where have you constructed your root object? in interface builder?
why is it a subclass of UIView?
is AudioServicesPlaySystemSound synchronous?
But mainly, I think your problem is here:
why would you expect to be able to play a file called (nil).wav?
I suggest you provide a crash dump and error log to assist people in answering this question.
See this SO question: playing background audio on iphone
"but at this point my app terminates(crashes)"
We need to know more about that to help you with this.
In the Xcode console, it should be putting out messages that would narrow in on what the problem is. A stack trace would be helpful too.
My total guess is that your local instance of root is undefined and so you're calling an unrecognized selector on that object. But without more help from the console, that's a complete guess.