Trouble with calling a method in Objective C (Apple Documentation example) - objective-c

I'm following along with Apple's "Programming with Objective C" document, the link being: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW1
Anyways, I've gotten to the point where it ask for calling the sayHello method.
"Create a new XYZPerson instance using alloc and init, and then call the sayHello method."
#import <Foundation/Foundation.h>
#import "XYZPerson.h"
int main(int argc, const char * argv[]);
XYZPerson *firstPerson = [[XYZPerson alloc] init]; //Initializer element is not a lime-time constant
[firstPerson sayHello]; //No Visible #interface for 'XYZPerson' delcares the selector 'sayHello'
#implementation XYZPerson
- (void)sayHello {
[self saySomething:#"Hello, World"];
}
- (void)saySomething: (NSString *)greeting {
NSLog(#"%#", greeting);
}
#end
I believe I'm having a misunderstanding with how apple is explaining the work or just have no clue.
Wishing apple had these examples done for us to review over.

You need to put the code inside the main function. Right now you have the code just sitting in your file, outside of any function. It should be:
int main(int argc, const char * argv[]) {
XYZPerson *firstPerson = [[XYZPerson alloc] init];
[firstPerson sayHello];
}
Also, according to the docs you should have a separate main.m file that has your main function inside of it.

As you can only access public functions which are declared in .h file with the class object.
Kindly declare that function in .h file and it will solve your problem

Related

Cocoa Console Application - property not found on object of type

So I am quite new on OC programming, I come from Front-end background (i.e. HTML/CSS/JavaScript ...), so I understand basic concepts of programming :)
Basically I created a console application, with a simple FooClass.
FooClass.h
#import <Foundation/Foundation.h>
#interface FooClass : NSObject
#property (strong, nonatomic) NSString *username;
- (NSString *) username;
- (void) setUsername:(NSString *)username;
#end
FooClass.m
#import "FooClass.h"
#implementation FooClass
#synthesize username = _username;
- (instancetype) init
{
self = [super init];
if (self) {
}
return self;
}
- (NSString *) username
{
return _username;
}
- (void) setUsername:(NSString *)username
{
_username = username;
}
#end
And in the main.m file, where the app bootstraps.
#import <Foundation/Foundation.h>
#include "FooClass.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
FooClass *foo = [[FooClass alloc] init];
foo.username = #"a";
}
return 0;
}
XCode tells me that it cannot find property username on object of type FooClass. And I don't really have idea about it. Any one could help?
I am a bit late in posting the answer. Here are few things that you should consider.
Since you have a property username. You are not required to create methods for setters and getters. The compiler will create them for you. Simply remove the two statements.
No need to synthesize in .m as well.
Instead of #include use #import. import takes only one copy even if you try to add the file(s) directly or indirectly from other files as compared to include.

Problems with subclasses inheriting class factory methods (Objective-C)

While I'm more than familiar with C#, I'm totally new at Objective C and iOS development. So I'm learning the language. What I don't understand is why the following code throws a compiler error (and yes, this is from the exercises at Programming with Objective C:
SNDPerson:
#interface SNDPerson : NSObject
#property NSString *first;
#property NSString *last;
+ (SNDPerson *)person;
#end
#implementation SNDPerson
+ (SNDPerson *)person
{
SNDPerson *retVal = [[self alloc] init];
retVal.first = #"Ari";
retVal.last = #"Roth";
return retVal;
}
#end
SNDShoutingPerson:
#import "SNDPerson.h"
#interface SNDShoutingPerson : SNDPerson
#end
#implementation SNDShoutingPerson
// Implementation is irrelevant here; all it does is override a method that prints a string
// in all caps. This works; I've tested it. However, if necessary I can provide more code.
// The goal here was for a concise repro.
#end
Main method:
- int main(int argc, const char * argv[])
{
SNDShoutingPerson *person = [[SNDShoutingPerson alloc] person]; // Error
...
}
The error is "No visible #interface for "SNDShoutingPerson" declares the selector "person".
Shouldn't this work? SNDShoutingPerson inherits from SNDPerson, so I would have assumed it got access to SNDPerson's class factory methods. Did I do something wrong here, or do I have to declare the method on SNDShoutingPerson's interface as well? The exercise text implies that what I did should Just Work.
Omit the +alloc when calling the class method:
SNDShoutingPerson *person = [SNDShoutingPerson person];
Briefly:
+ (id)foo denotes a class method. This takes the form:
[MONObject method];
- (id)foo denotes an instance method. This takes the form:
MONObject * object = ...; // << instance required
[object method];
Also, you can declare + (instancetype)person in this case, rather than + (SNDPerson *)person;.
change the line SNDShoutingPerson *person = [[SNDShoutingPerson alloc] person]; // Error
to
SNDShoutingPerson *person = [[SNDShoutingPerson alloc] init];
Cheers.
If you want to call class method:
SNDPerson person = [SNDPerson person];
person is a class method, but you're trying to call it with the incompletely constructed instance returned by alloc. Kill the alloc and just do [SNDShoutingPerson person].
This has nothing to do with subclasses, by the way. You would get the same error if you had written [[SNDPerson alloc] person].

In Objective-C, how to use 'self' in main() function?

I have the definition:
#interface MyClass: NSObject
{
NSString *str;
id delegate;
}
-(id)initWithStr:(NSString *)str
delegate:(id)delegate;
#end
and when i send message to my object in the main.m like this:
int main(int argc, const char * argv[])
{
#autoreleasepool {
[[MyClass alloc] initWithStr:#"abc-xyz"
delegate:self];
}
}
There is the error "Use of undeclared identifier 'self' ". I m a newb of objective-c and I know that the 'self' indicates the receiver of the message, but i don't know how to use it in the main() function. Can anybody tell me what's wrong about it and how to fix it?
What happens is that self points to the executing object. main function is not in a class, so, YOU CAN`T call self on a function, only on a method of a class. What are you really trying to do? Use the AppDelegate. When you create a new project, Xcode already gives you some files. One of then is called (YOUR_PROJECT)AppDelegate. This is the starting point of your application.
If you have any customizations to do in the initialization of your app, find your AppDelegate.m file, look for - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method, and place your code inside this method.
Never mess up with main.m file.
EDIT:
For Mac OS X here's what you should do:
Create a class to act as a delegate and add that class to the delegate.
MyDelegate * d;
MyClass * c;
int main(int argc, const char * argv[])
{
#autoreleasepool {
d = [[MyDelegate alloc] init];
c = [[MyClass alloc] initWithStr:#"abc-xyz"
delegate:d];
}
}
Then, on MyDelegate, implement the callbacks of the protocol.
If you want to have a callback on main, you will have to use a function pointer. You'll have to enter in the world of plain C. I don't recommend since you want to use object oriented stuff. Don't mix both, or you'll create a monster.
You do not use the main() function like this.
Do this in the AppDelegate, then it will just work.
In Objective-C, how to use 'self' in main() function?
This is impossible.
Because "self" can only be used by instances of objects.
main() is a function. Functions are not objects but code "on its own".

unrecognized selector sent to instance 0x10010c730->Objective C

I am new to programming.I have seen this code.returning a derived class object to the base class.
So that the base class can then point to the derived class methods.
Here a static function in class B is returning its object to the base
class.
base-derivedclass.m
#import <Foundation/Foundation.h>
#import "B.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[B p];
[pool drain];
return 0;
}
A.h
#import <Foundation/Foundation.h>
#interface A : NSObject {
}
#end
A.m
#import "A.h"
#implementation A
#end
B.h
#import <Foundation/Foundation.h>
#import "A.h"
#interface B : A {
}
+(A*)p;
-(void)display;
#end
B.m
#import "B.h"
#implementation B
+(A*)p
{
NSLog(#"returning derived class object to the base class!!");
return [B new];
}
-(void)display
{
NSLog(#"Hello");
}
#end
p is a class method. In Obj-C you denote class method by using + in method declaration and - to denote instance method. You can call the class method by using this:
// [ClassName methodName];
[B p];
Or you can change p to instance method by this:
- (A *)p;
// and call
// [instanceName methodName];
[dep p];
You can check Objective-C: A Primer to get started with these.
There's also some confusion in your memory management, here:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
B* der = [[B alloc]init];
[der p];
[pool drain];
You create and drain autorelease pools in order to put things into them (after creation) and then dispose of them right away (as you drain them). But you haven't put anything into the pool. You've used "alloc" to create "der," which means you "own" it for memory management purposes, as opposed to its being put in an autorelease pool where it will be taken care of automatically.
If all I've done is confuse you more, you should probably check out some introductory book on Objective-C. They all cover this topic at some point. Or you could look at Apple's docs on memory management, but they assume you already know certain things. (And it IS confusing, so be patient...)

Extend iTunesApplication class with Categories

I am just learning how to use ScriptingBridges. I made a method that slowly fades the volume on iTunes, and would like to make it a category so I can do the following:
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:50 speed:1];
I made another category for NSSpeechSynthesizer that works, but I can't get this one to. I keep getting the following build error:
"_OBJC_CLASS_$_iTunesApplication", referenced from:
l_OBJC_$_CATEGORY_iTunesApplication_$_iTunesApplicationAdditions in iTunesApplication.o
objc-class-ref-to-iTunesApplication in iTunesApplication.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Is there something special I can do to make it work since I can't include the symbols?
Thanks,
Ryan Pendleton
UPDATE:
I only found one solution, which is below. It involves MethodSwizzling, so I'm open to better answers, but for now it's all I have.
The solution I found was to use the Objective-C runtime API. I'm sure there's a better way to organize this, but here's how I did it:
Here are my .h and .m files for creating the category. Notice how lowerVolume is not an actual method, but a C function with the arguments id self, and SEL _CMD. You'll also notice a setupCategories function. We'll call that later.
// iTunes+Volume.h
#import <objc/runtime.h>
#import "iTunes.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed);
void setupCategories();
#interface iTunesApplication (Volume)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// iTunes+Volume.m
#import "iTunes+Volume.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed)
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
void setupCategories()
{
id object = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
Class class = [object class];
[object release];
class_addMethod(class, #selector(lowerVolume:speed:), (IMP)lowerVolume, "#:if");
}
Now that I've made the functions, I need to actually add them to the scripting bridge class using the Objective-C runtime API. I'll do this in main.m to make sure that the methods are ready to be used when the run loop starts.
// main.m
#import <Cocoa/Cocoa.h>
#import "iTunes+Volume.h"
int main(int argc, char *argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
setupCategories();
return NSApplicationMain(argc, (const char **) argv);
[pool drain];
}
Now, I can use my method wherever I want as long as I include the header files:
- (void)mute
{
iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:0 speed:1];
[iTunes release];
}
If any of this doesn't make sense, just tell me and I'll try to explain it better.
I think you need to include -framework ScriptingBridge to your gcc arguments. That got it to compile for me!
As noted above, you can't easily do a category on iTunesApplication because it doesn't exist at compile time, and also because the runtime class name is ITunesApplication (capital "I").
The best solution I've found is to do your category on the class that DOES exist, SBApplication. Here's the code I tested that works and does what the original example was trying to do:
// SBApplication+Extensions.h
#import ScriptingBridge;
#interface SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// SBApplication+Extensions.m
#import "iTunes.h"
#import "SBApplication+Extensions.h"
#implementation SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
#end
// Caller, say in AppDelegate
#import "SBApplication+Extensions.h"
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
iTunesApplication *iTunesApp =
[SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunesApp lowerVolume:4 speed:3.3f];
}