#import "PsychologistViewController.h"
#import "HappinessViewController.h"
#interface PsychologistViewController()
#property (nonatomic) int diagnosis;
#end
#implementation PsychologistViewController
#synthesize diagnosis = _diagnosis;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowDiagnosis"]) {
[segue.destinationViewController setHappiness:self.diagnosis];
}
else if ([segue.identifier isEqualToString:#"Celebrity"]) {
[segue.destinationViewController setHappiness:100];
}
else if ([segue.identifier isEqualToString:#"Serious"]) {
[segue.destinationViewController setHappiness:20];
}
else if ([segue.identifier isEqualToString:#"TV Kook"]) {
[segue.destinationViewController setHappiness:50];
}
}
****- (void)setAndShowDiagnosis:(int)diagnosis****
{
self.diagnosis = diagnosis;
[self performSegueWithIdentifier:#"ShowDiagnosis" sender:self];
}
-(IBAction)flying
{
[self setAndShowDiagnosis:85];
}
-(IBAction)apple
{
[self setAndShowDiagnosis:100];
}
-(IBAction)dragons
{
[self setAndShowDiagnosis:20];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
My question pertains to the - (void)setAndShowDiagnosis:(int)diagnosis method. This method is undeclared anywhere as either public in any .h file and obviously it's not there privately either. My question is why the reason for this would be? It just shows its setter implementation but the actual method declaration appears nowhere. Any help to clarify this is appreciated. Oh and this is from an online lecture and everything compiles just fine and runs.
Methods do not need to be declared, publicly, privately, or otherwise. Declaring a method in a .h file gives other users of the class knowledge of those methods. By not declaring it, you are hiding that method from the rest of the program that is using the class.
This method is undeclared anywhere as either public in any .h file and
obviously it's not there privately either.
I think you answered your own question, its coming up undeclared, because it isn't being declared. Unless I am reading this wrong?
You don't have to declare methods if you want them to private. There is no such thing as private methods in objective-c.
The difference between declaring a method in the header file, and a class extension at the top of implmentation file, is that if you don't declare it in the header, and you use the method from another class then the compiler will warn you that the method may not exist. But as long as you've implemented the method the application will not crash and the method will be called.
You could get away with not declaring any methods anywhere, but you will get lots of compiler warnings and it is harder to read later on, and harder for other people to understand your code. And there will be greater chance of you causing a crash because you miss spelt a method name,or some other trivial mistake.
Related
I'm writing a SIMBL plugin for Spotlight, and I'm trying to create a subclass of an internal Spotlight type. While I can get headers directly from the executable using class-dump, I don't have a static library to link against, so compiling a subclass of one of those internal classes fails (even though the classes will be available at runtime). I've been following Mike Ash's instructions on subclassing classes at runtime, but it's pretty inconvenient. Is there any way to create a subclass of an Objective-C class without the superclass being available at link time?
This is entirely possible, and not actually very difficult to do. Here's a simple example with NSValue:
#interface MySubclass : NSObject
-(void) someMethod;
#end
#implementation MySubclass
+(void) load {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
class_setSuperclass(self, NSClassFromString(#"NSValue"));
#pragma clang diagnostic pop
}
-(void) someMethod {
NSLog(#"%#", [self superclass]);
}
-(const char *) objCType {
return #encode(int);
}
-(void) getValue:(void *)value {
if (value) {
*((int *) value) = 10;
}
}
#end
int main() {
MySubclass *theSubclass = [MySubclass new];
[theSubclass someMethod];
NSLog(#"%i", [theSubclass isKindOfClass:[NSValue class]]);
}
class_setSuperclass, while deprecated, still has an implementation as of OS X 10.10, and can be done after the class has been registered. I have not fully explored the effects of changing a class's superclass post the creation of an instance of that class, however, so take caution if you are to do this at any point past +load or +initialize.
This may make it difficult to call methods on super, but this could be gotten around by simply declaring a category on NSObject (or whichever superclass you choose to inherit from in your implementation).
my first question on stackoverflow so please be gentle. I've tried searching for answers but I really need help with this.
The issue is with learning about delegates from Neal Goldstein's Objective-C for Dummies
He has the following in Transaction.h
#import <Cocoa/Cocoa.h>
#class Budget;
#interface Transaction : NSObject {
Budget *budget;
double amount;
NSString *name;
id delegate;
}
//some init method
#end
#protocol TransactionDelegate
#required
- (void) spend: (Transaction *) aTransaction;
//additional optional method
#end
--
//and then in Transaction.m he has this
#import "Transaction.h"
#import "Budget.h"
#implementation Transaction
#synthesize budget, delegate , amount;
- (void) spend {
if ([delegate respondsToSelector:#selector(spend:)])
[delegate spend:self];
}
- (id) initWithAmount: (double) theAmount forBudget: (Budget*) aBudget {
if (self = [super init]) {
budget = aBudget;
[budget retain];
amount = theAmount;
}
return self;
}
- (void) dealloc {
[budget release];
[super dealloc];
}
#end
I have problem understanding the spend method in the Transaction.m file
Can the id type instance variable call ANY method in the class that contains it?
I understand that respondsToSelector is a NSObject method that tells the compiler if a method has been implemented. But how can delegate which is of id-type call that method? the compiler does not even know what object it is yet...
Please help!
P.S. if anybody has any recommendations on good Objective-C books, I'd highly appreciate it. I wanna get into iPhone development but I figured I need to get a good grasp on the basics of Objective-C first.
Thanks!
Yes, you can send any message to the delegate variable, because its type is id.
You wrote this:
[delegate spend:self];
The compiler turns that into a call to the objc_msgSend function, like this:
objc_msgSend(delegate, #selector(spend:), self);
At runtime, the objc_msgSend function searches the method table of delegate's class (and its superclasses) for a method associated with the selector spend:.
Incidentally, we usually declare the delegate variable like this:
id<TransactionDelegate> delegate;
This informs the compiler that delegate will be an object that conforms to the TransactionDelegate protocol. This declaration will help Xcode give you better autocompletion when you're trying to send a message to delegate. If you declare your delegate setter method or property this way, the compiler will also check at compile time that you're setting it to an object that conforms to the protocol.
Good question. Gets at one of the key differences between Obj-C and other compiled languages.
You may send any message to any Objective-C object you like. The message send will try to be resolved by the library called the runtime library and this happens at runtime. At compile time, if you have an object type other than the generic id some IDEs may flag it as a likely user mistake.
At runtime, the runtime library will look for a matching method and then would look whether the class has a fallback handler that wants it and as a last resort would throw an exception. The user code could very well catch this exception and treat it as a normal situation.
So, I was reading this related SO, as it ended up being something I wanted to do as well.
I added a property to my destinationViewController's header file:
#property (nonatomic, strong) NSString *incomingSegue;
And I've got it synthesized in the destinationViewController's implementation file:
#synthesize incomingSegue = _incomingSegue;
I've added the following lines to my prepareForSegue method, for the sourceViewController (depending on which is triggering the segue):
[segue.destinationViewController setIncomingSegue:#"edit"];
[segue.destinationViewController setIncomingSegue:#"add"];
And, finally, I've got a process to check for which value is set in my destinationViewController's implementation file:
if (_incomingSegue == #"add")
{
//snipped code here
}
else if (_incomingSegue == #"edit")
{
//snipped code here
}
So, apparently I am missing something. When I try to perform the segue I get an error that shows up about 1000 times in SO, which makes it rather difficult to figure out which detail I've overlooked. This thing triggers (according to breakpoints) in my prepareForSegue method on my sourceViewController:
unrecognized selector sent to instance
Can I not use a literal string (#"string") in place of a (NSString *), or is it something else throwing the error?
Update (solved):
More detailed description of my prepareForSegue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"editSegue"])
{
//snipped
[segue.destinationViewController setIncomingSegue:#"edit"];
}
else if ([[segue identifier] isEqualToString:#"addSegue"])
{
//snipped
DestinationViewController *dtv = (DestinationViewController *)[[segue destinationViewController]topViewController];
[dvc setIncomingSegue:#"add"]
}
}
Turns out, I had to use my declared DestinationViewController class object to set the value. Instead of just referencing the segue.destinationViewController as I did for the editSegue. I do not have a DestinationViewController class object declared for editSegue, so that one was/is working as intended.
You should add a condition around the line that sets the incoming segue:
if ([segue.identifier isEqualToString:#"SegueToControllerThatSupportsIncomingSegue"]) {
[segue.destinationViewController setIncomingSegue:#"edit"];
}
The idea is to call setIncomingSegue: only on the destination view controller that supports your added method.
You should also change the code in the destination view controller to check string equality with isEqualToString:
if ([_incomingSegue isEqualToString:#"add"])
{
//snipped code here
}
else if ([_incomingSegue isEqualToString:#"edit"])
{
//snipped code here
}
When you call
#synthesize incomingSegue = _incomingSegue;
You really only need to call
#synthesize incomingSegue;
I have an Objective-C class that has a method that is meant to be overridden, which is uses in a different method. Something like this:
#interface BaseClass
- (id)overrideMe;
- (void)doAwesomeThings;
#end
#implementation BaseClass
- (id)overrideMe {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)doAwesomeThings {
id stuff = [self overrideMe];
/* do stuff */
}
#end
#interface SubClass : BaseClass
#end
#implementation SubClass
- (id)overrideMe {
/* Actually do things */
return <something>;
}
#end
However, when I create a SubClass and try to use it, it still calls overrideMe on the BaseClass and crashes due to doesNotRecognizeSelector:. (I'm not doing a [super overrideMe] or anything stupid like that).
Is there a way to get BaseClass to call the overridden overrideMe?
What you are describing here should work so your problem is likely elsewhere but we don't have enough information to help diagnose it.
From your description, I'd say either the instance you're messaging is not the class you think it is or you made some typo in your code when declaring the method names.
Run your application under gdb, add a symbolic breakpoint on objc_exception_throw, reproduce your problem. Once your process has stopped on the "doesNotRecognizeSelector" exception, print object description and it's class.
Or log it before calling -overrideMe:
NSLog(#"object: %# class: %#", obj, [obj class])
Write a category for BaseClass to override the method.
#interface BaseClass (MyCategory)
- (id) overrideMe;
#end
#implementation BaseClass (MyCategory)
- (id) overrideMe
{
/* Actually do things */
return <something>;
}
#end
Now all instances of BaseClass will respond to selector overrideMe with the new implementation.
I know it is a common problem, I googled a lot and seems get no luck to solve my problem.
I have a #interface TestViewController:UIViewController
and in its implementation file I have a method defined:
-(void)method1 {
do something;
[self method1];//here I need to call the method itself if a statement is true and this line is where the warning TestViewController may not respond to'method1' I got
}
-(void)method2{
[self method1] //But there is no problem with this line
}
Can anyone help me?
Thanks in advance!
Your method declarations are missing in the header.
Just add
-(void)method1;
-(void)method2;
to your TestViewController.h file
Update:
The reason why you don't get a warning about the second call ([self method1] within method2) is, that the compiler already knows about method1 at that point. (because the implementation occurs before method2)
Objective-C just as C uses a single pass compiler to gather all known symbols. The result is that you can only reference methods and variables that has been declared above the current scope.
You can solve this particular problem you give an example of in three ways:
Add method1 to the public interface in the header file, just as #weichsel suggested.
If you want method1to be private then you can add it to your class by declaring an unnamed category at the top of you implementation file. Like this:
#import "Foo.h"
#interface Foo ()
-(void)method1;
#end
#implementation Foo
// ... lots of code as usual ...
#end
The third option could be regarded as hack by some, but it really a feature of the Objective-C language. Just as all methods get an implicit variable named self that is the instance the method was called on, so do all method alsa get the implicit variable named _cmd, that is of type SEL, it is the selector that was used to call this method. This can be used to quickly call the same method again:
-(void)method1 {
if (someContition) {
[self performSelector:_cmd withObject:nil];
} else {
// Do other stuff...
}
}
This is most useful if you want to make sure that a particular method is always performed on the main thread:
-(void)method {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
return;
}
// Do stuff only safe on main thread
}