There are two view controllers that I access using an NSTabView: ViewController1 and ViewController2. In my AppDelegate, I have a variable that I wish to share between the two:
AppDelegate.h:
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
NSMutableString *myString;
}
#property (assign) NSMutableString *myString;
ViewController.h:
-(void)doStuff
{
AppDelegate *del = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[del.myString setString:#"This is a test"];
}
This is the error I get when doStuff() is called:
2014-06-10 16:29:09.240 MyApp[32297:303] -[ViewController2 myString]:
unrecognized selector sent to instance 0x6100001a7700
2014-06-10 16:29:09.240 MyApp[32297:303] An uncaught exception was raised
2014-06-10 16:29:09.240 MyApp[32297:303] -[ViewController2 myString]:
unrecognized selector sent to instance 0x6100001a7700
2014-06-10 16:29:09.241 MyApp[32297:303] (
0 CoreFoundation 0x00007fff8d52d25c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff93f16e75 objc_exception_throw + 43
2 CoreFoundation 0x00007fff8d53012d -[NSObject(NSObject)
doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff8d48b322 ___forwarding___ + 1010
4 CoreFoundation 0x00007fff8d48aea8 _CF_forwarding_prep_0 + 120
5 Recorder 0x0000000100006b55 -[ViewController1 doStuff:] + 549
Why is there an error being thrown in the other view controller?
Because at some point you have reset the app delegate so that it is a ViewController2. That happened in code you have not shown, so I don't know how and when you did that. But the point is, your message myString is never arriving at the AppDelegate. It is arriving at the ViewController2.
In other words, you are saying
(AppDelegate *)[[NSApplication sharedApplication] delegate]
but in fact that object has somehow been repointed at the ViewController2. Thus, even though you are casting to an AppDelegate, it isn't an AppDelegate at all. The compiler allows you to say myString to this object because you said (falsely) that it is an AppDelegate, but the reality causes the crash at runtime when the myString message arrives at the ViewController2 object.
Look for code where you say setDelegate: or .delegate = ... to work out when you repointed the app delegate.
A few things:
1) The error "unrecognized selector" means you're trying to call some function ("doStuff" or "setMyString") on an object that doesn't contain a public method with that name. It crashes because it doesn't know how to respond. Most often you'll see this when you forget to hook up a storyboard properly, have a typo in a method call, or if you send a message to the wrong object.
2) You're trying to access myString on AppDelegate but it's actually being called on ViewController2. The error is because ViewController2 doesn't have a #property called myString. As the response above stated, doublecheck how you're actually assigning the appDelegate.
3) You're declaring both an instance variable myString AND the #property myString. This is redundant and really outdated (< iOS4) practice. Instead, remove the {NSMutableString *myString} portion in curly braces & only use #property (nonatomic, strong) NSMutableString *myString;
4) Unless you specifically created a setString method in your AppDelegate.m, the default way to access an #property named "myString" would probably be [del setMyString] or del.myString = ....
Calling [del.myString setString] is also a redundant mixture of dot syntax & brackets. It may throw a separate error after you fix the first one. Let the #property handle creating getter/setter methods.
Call [del setMyString:#"whatever value"]; or alternatively del.myString = #"whatever value";
5) In Obj-C this sort of crash is often avoided by first asking whether or not the object will respond:
if ([someObject respondsToSelector:#selector(doStuff:)] {//then do something
}
6) This sort of error is one good reason why Apple's new language Swift has moved away from the pattern above. Swift discourages asking about selectors & instead requires more specific/optional data types so you're less likely to run into this problem.
Related
I have a class WebServices that inherits from NSObject. I am using xcode4.2 and ARC turned on.
When I created the class, there was no other method in the NSObject lie viewDidLoad or init.
The issues is that when I try to call self.something or [self someMethod] Xcode flags my code red and complains with:
implicit conversion of Objective-C pointer type 'Class' to C pointer type 'struct obj_class*' requires a bridge cast
Please help. Why isn't cocoa like java where you call "this" and get the object you are in?
// WebService.h file
#interface WebService : NSObject
#property (weak, nonatomic) NSString * myString;
+(void) setAndPrintMyString:(NSString*) someString;
#end
//WebService.m file
#import "WebService.h"
#implementation WebService
#synthesize myString=_myString;
+(void) printMyString:(NSString*) someString{
[self setMyString:someString]; //XCode does not allow
NSLog(#"myString is set to %#",self.myString); //XCode dose not allow
}
#end
Declaring a method with + means that it is a class method. Within a class method self refers to the class itself, which in your case would be [WebService class]. If you declared and instance method (using -) then inside the method self would refer to the instance, which is what you want.
To set an instance variable - you need an instance
WebService *webService = [[WebService alloc] init];
webService.myString = #"some string";
Now to make your method work you need to declare it with a - instead of + which makes it an instance method
- (void)printMyString:(NSString *)someString
{
[self setMyString:someString];
NSLog(#"myString is set to %#",self.myString);
}
Now
[webService printMyString:#"boom"];
results in the instance variable myString being set to boom and the console logging out `myString is set to boom".
viewDidLoad method doesn't fit with NSObject subclass. It's a method for UI which will be there in UIViewController subclasses.
Now, Coming to point about self.something OR [self someMethod], That works perfectly well with NSObject subclasses. You need to show us the code, in which you are facing problem.
Just for your reference (I think you should start developing for iOS after going through this):
NSObject Class Reference
UIViewController Class Reference
In my app I want to use the the ManagedObjectContext created by the AppDelegate in another class (myClass). For this I first created an Instance Variable in MyClass to store the objectContext:
NSManagedObjectContext *managedObjectContext;
I also defined getter and setter with #property.
To set the instance variable I assigned it the managedObject Context of AppDelegate after initializing it:
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
myClass *myClassInstance = [[myClass alloc]init];
[myClassInstance setManagedObjectContext:[self managedObjectContext]];
}
Now, when I'm running the App I get the following:
-[NSManagedObjectContext copyWithZone:]: unrecognized selector sent to instance 0x100634c90
To be honest I don't really know what to do. Can someone help me?
It seems that you have declared the #property for the managed object context with the "copy" attribute. You should declare it as
#property(strong, nonatomic) NSManagedObjectContext *managedObjectContext;
A managed object context cannot be copied, but it would also make no sense. You want to use the same context in your class, not a (independent) copy.
I have 2 UIViewControllers(A,B). A is set as the delegate for B and then B is presented. The protocol is set up in B's header:
#protocol BDelegate <NSObject>
- (IBAction)finishOrder:(id)sender;
#end
delegate property is declared:
#property (nonatomic, assign) id<BDelegate> delegate;
A sets B.delegate = self;
Then on a button press B calls:
if (self.delegate) {
[self.delegate finishOrder:nil];
}
However, on first run lldb gives me:
-[UITextInteractionAssistant finishOrder:]: unrecognized selector sent to instance
second run:
-[NSInvocation delegate]: unrecognized selector sent to instance
third run:
-[__NSCFDictionary delegate]: unrecognized selector sent to instance
forth run:
-[UITextTapRecognizer finishOrder:]: unrecognized selector sent to instance
So... no code changed, but self changed from A to NSInvocation and __NSCFDictionary and the delegate for B (self of A) changed to UITextInteractionAssistant and UITextTapRecognizer... Never seen anything like it. Any ideas? Thanks!
You are over-releasing somewhere. Try turning on Zombies, and use Instruments to track down the over-release.
I have an NSString declared like this :
.h:
#interface ViewController : UIViewController
{
NSString * aString;
}
#property(nonatomic,copy)NSString *aString;
.m :
#synthesize aString;
......
aString=[[NSString alloc]init];
aString=#"Hello World";
NSLog(#"%#",aString);//The app crashes here
The app crashes with this stack trace:
-[CFString respondsToSelector:]: message sent to deallocated instance
remove the line:
aString=[[NSString alloc]init];
and set values to the property:
self.aString=#"Hello World";
Doing: aString=#"Hello World"; means you are setting value to the instance variable, without using the accessor methods of the property, then you are responsible to the memory management and it's more complicated. Get the value by: self.aString also.
P.S. Always work though properties, almost never use the instance variables, (only in the dealloc method release the ivar, otherwise if you are not good at memory management you will always have problems, but properties do everything for you)
I'm working through the "Key Value Coding" chapter in "Programming for Mac OS X". I've built an interface with a slider and a label, both bound to fido, an int. If I set the property for fido to readonly, moving the slider still causes the label to change it's value. I had assumed that I'd get some sort of error for this. If the property is readonly, how come the slider can still write to the property? I thought that it would have no setters created, and KVC wouldn't work. Thanks.
Here's the code I'm using:
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject
{
int fido;
}
#property (readonly, assign) int fido;
#end
#import "AppController.h"
#implementation AppController
#synthesize fido;
- (id)init
{
[super init];
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
return self;
}
#end
alt text http://idisk.me.com/nevan/Public/Pictures/Skitch/Window-20091001-174352.png
AppController.h:
#interface AppController : NSObject
{
int fido;
}
#property (readonly, assign) int fido;
#end
import "AppController.h"
#implementation AppController
#synthesize fido;
...
#end
At this point, you have declared that AppController has a -fido method and you have synthesized that method. There is no -setFido: method. So, why does the following "work"?
- (id)init
{
if (self=[super init]) {
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
}
return self;
}
(BTW: I fixed your -init to implement the correct pattern)
This works because KVC follows a heuristic to set or get the value. The call to -setValue:forKey: first looks for -setFoo:. If not found, it then looks for the instance variable foo and sets it directly.
Note that if you change the instance variable fido to _fido, the set will work, but the valueForKey will return 0 as it calls the synthesized method (since I'm on 64 bit, the #synthesize synthesizes a fido instance variable. Confusing, I know.).
If you were to change the name of your ivar to bar and then use #synthesize foo=bar;, the code would fail at runtime.
You'll see:
2009-10-01 08:59:58.081 dfkjdfkjfjkfd[24099:903] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppController 0x20000e700> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key fido.'
*** Call stack at first throw:
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
(
0 CoreFoundation 0x00007fff85b055a4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff85c5a0f3 objc_exception_throw + 45
2 CoreFoundation 0x00007fff85b5caf9 -[NSException raise] + 9
3 Foundation 0x00007fff814e14f5 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 434
4 dfkjdfkjfjkfd 0x0000000100000d96 -[AppController init] + 130
Having readonly property means that compiler won't generate you setter for that property. It's still legal to write to it via KVO/KVC.
The compiler directives #property and #synthesize are just shorthand ways to create the methods to get and set the variable in question.
The setter method created is named setFido:, and the getter method is just named fido.
When you specify readonly, I believe that simply tells the compiler not to create the setter method, but only the getter. It doesn't put any sort of barrier in the way of setting the variable by other means.
(Hope I've got all that right. Good luck!)