Objective-C Categories not being recognized? - objective-c

Well, this is quite a weird issue. (I just hope it has something to do with my not-playing-that-much-with-Cocoa-for-a-while, or else...)
So, the issue is quite straightforward :
I'm using Xcode 4.3.3 (a very simple test project - 10.7 SDK - no ARC)
I'm creating a Category on some Class (e.g. NSProgressIndicator)
I'm including the appropriate header file
When trying to use any of my Category's methods (however, it still shows up in the dropdown of available commands), I'm getting an error :
[NSProgressIndicator start]: unrecognized selector sent to instance
0x7f9f4b91a0a0
The code (as an example - it has happened with other (100-times tested) categories):
#import <Foundation/Foundation.h>
#interface NSProgressIndicator (NSProgressIndicator_Functions)
- (void)start;
- (void)stop;
#end
#import "NSProgressIndicator+Functions.h"
#implementation NSProgressIndicator (NSProgressIndicator_Functions)
- (void)start
{
[self setHidden:NO];
[self startAnimation:nil];
}
- (void)stop
{
[self setHidden:YES];
[self stopAnimation:nil];
}
#end
Any ideas?

To expand my comment into a real answer:
Make sure the category's implementation (.m) file is included in your target's Compile Sources build phase. Importing the header is enough to tell the compiler that there is a category on NSProgressIndicator which adds a -start method. Unless the category's implementation is actually compiled and linked into the finished binary (or the method implementation is added at runtime, etc), NSProgressIndicator won't actually respond to the start message at runtime. Because of Objective-C's dynamic message send behavior, there's no way the compiler can tell at compile time whether or not NSProgressIndicator is actually going to respond to that message, which is why you don't get a warning or an error.

Related

NSTextField Subclass Crashes on setLineBreakMode: method

I have a subclass of NSTextField and I setting the LineBreakMode.
It works fine under my mac with Yosemite
and Crashes for one of my users on Mavericks
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[XTextField setLineBreakMode:]: unrecognized selector sent to instance 0x7fc784548ad0'
How could I work this round ?
Header File of the subclass
#import <Cocoa/Cocoa.h>
#interface XTextField : NSTextField
- (void)setText:(NSString *)text
#end
Implementation
#import "XTextField.h"
#implementation XTextField
- (void)setText:(NSString *)text
{
if (text)
{
[self setStringValue:text];
}
else
{
[self setStringValue:#""];
}
}
- (instancetype)initWithFrame:(NSrect)frame
{
if(self = [super initWithFrame:frame])
{
[self setEditable:NO];
[self setSelectable:NO];
[self setDrawsBackGround:NO];
[self setBezeled:NO];
}
return self;
}
#end
Calling code:
XTextField* myLabel = [[XTextField alloc]initWithFrame:myFrame];
[myLabel settext:#"text text text"];
[myLabel setLineBreakMode:NSLineBreakByTruncatingTail];
There are lots of questions that need to be answered here. The problem, as indicated by the error message, is that the setLineBreakMode: selector was sent to an object that doesn't recognize that selector. What could be happening that would cause that? Figuring things like this out requires critical thinking and detective work. Here are some ideas.
-setLineBreakMode: does not seem to actually be implemented by NSTextField; it is a method of NSCell, and of NSMutableParagraphStyle, as far as I can tell. If you have an NSTextField (or a subclass of it) you'd normally call [[myTextField cell] setLineBreakMode:...], but your code snippet doesn't indicate that you are doing that. So unless you implemented this method in your subclass – which you don't state that you did – this is probably the reason for the crash. Perhaps you don't see the crash on your Mac because this code path doesn't get hit, for whatever reason? Or perhaps Apple privately implemented this method in Yosemite but not in Mavericks? Who knows. Do you get a warning from the compiler on that line, saying that the object does not respond to that selector? Do not ignore compiler warnings.
The code you posted looks like you are calling setLineBreakMode: on the class object, not on an instance of the class; normally classes start with a capital letter, whereas instances start with a lowercase letter. Obeying coding conventions like this makes things much less confusing for everybody. And if your subclass is really named NSTextFieldSubClass, then I agree with #MichaelDautermann that you should never, ever name classes with an NS prefix; that is both confusing and asking for trouble, since for all you know Apple has a private subclass with exactly that name. Class names beginning with NS are reserved by Apple.
It could be that the object you think is in the variable that you have apparently named NSTextFieldSubClass is not an instance of your subclass at all, or has been freed (and perhaps replaced by a new object at the same address), or some such problem, and that that is why it doesn't respond to the selector. You could investigate this by turning on NSZombieEnabled, examining it in the debugger, adding an NSLog of it (perhaps with an added -description method in your subclass), or many other techniques.
That's all pretty vague, but then the question is vague. You need to post the code for your subclass, the code where you instantiate the subclass, and the code surrounding the line that crashes, at a minimum, to get more specific help. We can't read your mind, and the root of the bug may well be in one of those places.

What is the correct way of accessing a Swift Delegate from Objective-C?

Environment: Xcode 6.1.1 & Xcode 6.2 Beta
Greetings:
I need to publish a NSString within a Swift doc from a neighboring Objective-C doc within the same project. For example, display "Hello World" generated in Objective-C upon a Swift page. I've made a proof-of-concept demo; based on feedback.
I'm thinking of using an ObjC --> Swift delegate via a protocol pattern as shown below:
Note: the Swift file is the delegate.
Here I'm calling the delegate method in Swift, from Objective-C:
#pragma mark - Action methods
- (IBAction)sendDelegateAction:(UIButton *)sender {
[_delegate radiusString:#"Hello World"];
}
I've instantiated the Objective-C file to link the delegate to the instance (I hope I got it right):
let geo32Controller = MyObjCTableViewController()
geo32Controller.delegate = self
So far, the compiler complained that the Swift protocol couldn't be found.
Here's the protocol (declared in Swift):
#objc protocol DiscoveryContributeProtocol {
// optional
func radiusString(radiusString:String)
}
And here's the delegate reference to that protocol in the Objective-C header file:
#interface MyObjCTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id<DiscoveryContributeProtocol> delegate;
#end
However, the compiler can't find the protocol:
BTW: when I put the bridge reference in the ObjC's header file, I get a compiler error:
Two Questions:
Do I have the correct pattern (did I instantiate the ObjC correctly) ?
How do I make the Objective-C portion see the Swift protocol for the delegate link?
You have the right idea, but have a few bugs that are preventing this from working.
You've declared Geo32Boundaries as conforming to the DiscoveryContributeProtocol, but it doesn't need to and doesn't actually implement it, it only has a property that conforms to that protocol. That's the source of the "Method 'radiusString:' not implemented" error:
#interface Geo32Boundaries: UIViewController // <-- that's all you need
You're setting the delegate incorrectly -- the code you have there looks like it's trying to set a class instance of Geo32Boundaries to self, but you're also trying to call it like a function. You'll need to set the delegate on a the instance of the Geo32Boundaries view controller that is being presented to the user. I don't know where that code lives, so I can't give a great example, but it'll be something like:
geo32Controller.delegate = self
Lastly, though not a bug, your protocol should really be called DiscoveryContributeDelegate -- we usually don't use "protocol" in the protocol name.

Xcode - implementing a method, also be implemented by its primary class

I'm using Xcode 4.5.
In my latest Xcode project, I have this warning pop up when i build/compile my program:
Category is implementing a method which will also be implemented by its primary class
This is my code that is causing the error:
#implementation NSApplication (SDLApplication)
- (void)terminate:(id)sender {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event); }
#end
I have made it so the compiler ignores (or skips over) the warning generate using pragma code.
So my code now looks like this:
#implementation NSApplication (SDLApplication)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (void)terminate:(id)sender{
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);}
#pragma clang diagnostic pop
#end
Obviously it does the job and once built/compiled, no warning is generated.
My question is, is this a safe/ok way to suppress or ignore the warning in this way?
I read on some threads that using subclasses were advantageous, but there were many people fore and against using them in this way... I'd appreciate your thoughts :)
"I read on some threads that using subclasses were advantageous..."
Using subclasses may be advantageous but that's not what you are doing here.
What you have done is implemented an (SDLApplication) Objective-C Category on NSApplication. In that category, you are overriding NSApplication's terminate: method. In general, you should use categories only to add additional methods to an existing class, not to override existing methods, as that can have unpredictable results.
What you really should do is subclass NSApplication so that you can override terminate: properly:
#interface SDLApplication : NSApplication {
}
#end
#implementation
- (void)terminate:(id)sender {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
[super terminate:sender]; // this line is the key to assuring proper behavior
}
#end
The last step in your overridden terminate: method should be a call to super, so that the terminate method works as it should. (Using the keyword super in a category is not allowed; only the word self, which is why there's a need for subclassing).
Make sure you also change the value for the NSPrincipalClass key in your Info.plist file to be SDLApplication instead of NSApplication.
No.
Overriding (or re-implementing) methods, especially critical system methods like -terminate: in a category results in undefined behavior. Obviously, quitting an application should not be a game of chance. Subclass NSApplication and override -terminate: (with a call to super at the end), then specify it in the info.plist as the Principal Class. Pragmas aren't warning fixers, they're merely suppressors.

Why can't I access variables when I subclass a customised UIViewController?

I am new to iOS dev and apologies if the answer is obvious...but it isn't to me.
I have an APP with a Navigation controller at its root.
I have many very similar looking areas of the app to be created.
These are each to be UItableviewcontroller which has had a fair bit of customising done to allow buttons and other controls beside the tableview which has been reduced in size to allow for controls beside and below it.
The buttons, text, background etc etc and the data that gets loaded must all be individual to the particular are of the APP.
I created a UItableviewcontroller subclass by simply adding a new file subclass in Xcode.
I created my interface in the xib...created all the methods to drive what I need to in it.
Looks great...all seems fine. If I use it alone...works well.
Problem: I can't figure out how to subclass my custom sub-classed UITVController!
None of its properties are available from inside the new sub-class.
I clearly don't understand how things work here.
I have tried adding a new file > UIViewcontroller sub-class and changing the superclass to my custom superclass...to no avail. No properties accessible.
I have dug and dug and become more confused than anything else.
Is someone kind enough to help me get it right. Frustration is building.
Thanks
Keispe
EDIT:
Whoa found the problem. I have had Xcode open for many many days with several projects open.
It had totally weirded out!
In fact jrturton and eugene...I did know what I was doing (I thought I was going crazy...done this before in my app and suddenly no worky) Xcode had totally lost it's brains!
Anyone seen Xcode do that before??? using 4.1
Bloody hell that wasted a heap of valuable time including yours.
Thanks fellas
When you subclass anything, you can access your parent's class properties by addressing self via dot syntax
#interface BaseClass : NSObject {
#public
NSString *baseclassString;
}
#property (nonatomic, copy) NSString *name;
#end
.h
#interface HigherClass : BaseClass
#end
.m
#implementation HigherClass
- (id)init {
self = [super init];
self.name = #"Hola";
self->baseclassString = #"Hola";
return self;
}
- (void)viewDidLoad {
NSLog(#"name: %#", self.name);
}
#end
This all isn't 100% memory clean but you've gotta get a hang of what is happening here and adjust it properly to your application.

'TileMap' may not respond to '+mapNamed:'

Here's an odd one. I have a class named TileMap with the following interface:
#interface TileMap : NSObject
{
int *data;
int tilesWide;
int tilesHigh;
NSString *imageName;
}
+ (id)mapNamed:(NSString *)filename;
- (id)initWithFile:(NSString *)filename;
#end
The implementation looks like this:
#implementation TileMap
+ (id)mapNamed:(NSString *)filename
{
return [[self alloc] initWithFile:filename];
}
- (id)initWithFile:(NSString *)filename
{
if (self = [super init])
{
// ...
}
return self;
}
#end
But when I add a call to [TileMap mapNamed:#"map.plist"]; to my application Xcode warns:
'TileMap' may not respond to '+mapNamed:'
The application compiles fine and calls to NSLog within TileMap-initWithFile: are logged. I noticed that Xcode's syntax coloring was off for this class and method so I tried renaming both the class and the method separately. The only combination that eliminated the warning and syntax coloring issues was to rename both the class and the method.
Am I colliding with some undocumented framework class or method? Find in Documentation doesn't reveal anything. Find in Project only reveals the call, interface definition and the implementation. I'm stumped (not that it takes much). Is there a way around this without munging my existing naming conventions?
Did you #import the TileMap.h header? Did you save your TileMap.h header?
Turns out my project directory ended up with two TileMap.h and TileMap.m files—visible from the Finder but not in Xcode. One, a complete interface and implementation, in my root project directory. The other just a bare NSObject subclass in my framework subdirectory. Not sure how that happened. Deleting the latter resolved the problem. Thanks for the help just the same Dave.
Shaun,
besides the problem you asked about, you also have a memory leak in +mapNamed:. The following line returns a non-autoreleased object with a retain count of +1, which basically gives ownership to the caller:
return [[self alloc] initWithFile:filename];
According to the Memory Management Programming Guide for Cocoa, you should return autoreleased objects from convenience methods, such as this:
return [[[self alloc] initWithFile:filename] autorelease];
If you have Snow Leopard and Xcode 3.2, you might want to try running the static analyzer to find mistakes such as this one by pressing Cmd+Shift+A.