how to call a mothod defined in another class - objective-c

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.

Related

Bug when unit testing Google Analytics tracker in XCode 6

I'm writing a unit test to check that the string I pass to the GAITracker class is being returned as the kGAIScreenName property for each screen.
However when I try to pass a sharedInstance to the GAI class to initialize the WNGoogleAnalyticsService instance I am getting the error Thread 1: EXC_BAD_ACCESS (code=1, address=0x20) as if it has not been allocated to the memory. No matter where I try to declare the sharedInstance won't initialize in the test class although it works fine in the AppDelegate.m.
WNGoogleAnlayticsServiceTest.m:
-(void)testIfNoScreenNameExists {
NSString *screenName = #"Screen";
Class builder = [GAIDictionaryBuilder class];
GAI *gai = [GAI sharedInstance];
WNGoogleAnalyticsService *s = [[WNGoogleAnalyticsService alloc] initWithGAInstance:gai
gaKey:#"test"
gaDictionaryBuilderClass:builder
debugging:NO];
id<GAITracker> tracker = [s trackerForScreen:screenName];
XCTAssertEqualObjects([tracker get:kGAIScreenName], screenName);
}
AppDelegate.m:
Add Google Analytics as analytics service
WNGoogleAnalyticsService *googleAnalyticsService = [[WNGoogleAnalyticsService alloc] initWithGAInstance:[GAI sharedInstance]
gaKey:[[NSBundle mainBundle] objectForInfoDictionaryKey:#"WNGoogleKey"]
gaDictionaryBuilderClass:[GAIDictionaryBuilder class]
debugging:analyticsDebugging];
I'm at a loss as to how to even go about fixing this bug so any help would be appreciated.
In line with the comment I left underneath my question, I feel I should formally answer this with my solution.
In the case of this unit test, I was trying to re-use a singleton by reusing the [GAI SharedInstance] method in both my WNGoogleAnlayticsServiceTest and in my AppDelegate.m file which, by definition, it cannot do.
So if you want to test the Google Analytics methods, you must use a tool like OCMock to do so as you can't initialise the sharedInstance twice.

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

IOS values passed to a view are lost or forgotten

This is my first app, and actually isn't even fully mine but rather involves re-working an existing app to add functionality.
It involves a JSON feed which I'm successfully reading in and then trying to pass the value of a URL to a view. Here's the code from my app delegate that is successfully fired once the feed is read in:
- (void)JSONFetch:(MYJSONFetch *)fetch gotTheCollection:(id)collection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.testViewController.feedURL = [NSURL URLWithString:[collection objectForKey:#"Listings"]];
[JSONFetch release];
JSONFetch = nil;
}
Then in my testViewController I have this viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
if(self.feedURL)
{
[self startDownload];
}
}
Eventhough, when I debug, the gotTheCollection method passes a value to the feedURL of the view, it then fails on the if(self feedURL) check within the view and thus the view never gets populated.
As I'm so new to this code I've no idea if the sequence is wrong, or maybe it's how I'm passing the variable.
I know the description is relatively vague but even on a basic level I don't know if this functionality works in objective C, it doesn't cause any errors though, just sits there not loading because it can't get the data.
UPDATE: Definition of FeedURL follows, in the H file:
NSURL *feedURL;
then
#property (nonatomic, retain) NSURL *feedURL;
then in the M file:
#synthesize feedURL;
Thanks for the help guys, I finally decided to just restart the entire upgrade as the project had become a mess of reworked code and I couldn't be sure what worked and what didn't. As a result there's no clear answer to this but I imagine Franks was probably the closest so I'll mark that as the answer.
The NSURL is being autoreleased, you will need to retain it yourself
Assign the NSURL to feedURL, like so
self.testViewController.feedURL = [[NSURL URLWithString:[collection objectForKey:#"Listings"]] retain];
This will also mean you will have to release it yourself.

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!

Does Missing method signature causes app abort?

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.