Interface Builder loading wrong class - objective-c

I created a custom NSTextView subclass called JWTextView. It's part of a separate Xcode project. That project has a target that creates a static library containing this class. I now added this subproject to my main project under MainProject/Libraries/JWKit (in Finder) and added it in Xcode as well. I made my main project's target depend on the subproject's library target and I'm linking the library in my main target. I also added MainProject/Libraries/** to the Library and Header search paths.
Now I'm trying to use this view in my main project. I added an NSTextView to one of my xib's and changed its class to JWTextView. I also have an IBOutlet JWTextView in my code and call some methods on it upon user interaction. JWTextView.h is imported. Everything compiles file.
But once I call a method on this text view that I implemented in my JWTextView subclass, the app crashes with an unrecognized selector error:
-[NSTextView myCustomMetod:]: unrecognized selector sent to instance 0x101901a80
I checked, and it's actually an NSTextView instance, even tough the ivar is a JWTextView and I set the class in IB.
If I just add the classes to my main project it works fine, but not as a subproject and a static library.

The compiler optimized it out, because I wasn't referencing the class anywhere by name (except the ivar, which doesn't seem to help).
Adding [JWTextView class]; to applicationDidFinishLaunching: fixed the problem.

Related

Change of AppDelegate class on OSX

I have a cocoa project on OSX. For that, I had to change the class of the Appdelegate.
I did this in two steps: First I implemented the new appdelegate class (I didn't implement the NSApplicationDelegate protocol yet) and checked if it worked and compiled. In the new class I already implemented the applicationDidFinishLaunching method. Everything was still fine!
After that I changed the protocol implementation. I removed the prorocol from the old Appdelegate and inserted it in the new one. Even after this step everything seemed to be fine! The application compiled and worked as it should. But as I was somewhat sceptical, I inserted log statements in the new and the old class.
After inserting this, I had to see, that the application still used the old appdelegate!
Obviously, there is something where the name of the old appdelegate is still known! But where is that? Or do I have to do something else to reach my goal? Does anyone know what I have to do?
There are two things that have to happen: 1) an instance of your new class has to be instantiated. 2) That instance needs to be assigned to the delegate property of the application object (instance of NSApplication or a subclass).
In a typical Mac app, both of those things are done in the MainMenu NIB. That NIB contains a freeze-dried instance of the appropriate class. If you were to build the NIB from scratch, you would drag an "Object" (blue cube) from the Object library to the NIB document. By default, that would represent an instance of NSObject. You would then select it and bring up the Identity inspector where you would change its class to your new app delegate class. Since you're not building the NIB from scratch, there's already an Object in the NIB. You can select it and change its class.
The other step, assigning it to the delegate property of the application object, is done by connecting the delegate outlet of the placeholder for the application object in the NIB to the object. Again, since you're not building the NIB from scratch, that's already done.
So, in summary, you just need to change the class of the app delegate object in the MainMenu NIB.
Update: Here's a screen shot of what you have to change:

deploy to device results in frankenstein binary

I'm unsure if this is relevant to my question, but I'll start at the beginning: In my project I have a view controller which is instantiated dynamically by class name, like this:
NSString* className;
...className is set to a valid class name, e.g. "someViewController"
Class c = NSClassFromString( className );
UIViewController* vc = [[c alloc] init];
In the current build of the project, someViewController is simply a specialized UIViewController. However, in an old build of the project (which was previously deployed to my device), the view controller of type someViewController was a specialized UITableViewController.
I spent a bunch of time this morning trying to understand why when I ran the build on the device (deploy from XCode), it would crash with an odd call stack and a console message:
[someViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0xeb62a0
With much experimentation I found that if I swapped out the someViewController with any other view controller I couldn't repro the problem. And if I renamed someViewController to someViewController2 I couldn't repro the problem. The problem was with the symbol name someViewController. Then it dawned on me that someViewController USED to be a UITableViewController and would have had the tableView:numberOfRowsInSection: implemented.
In any event; deleting the app off of the device and having XCode deploy a clean copy fixed the issue.
What I want to know is, why? How? I guess I've known in the back of my mind for a while that XCode does funky stuff when deploying - I've had issues before where old resources stick around in the deployed bundle after they've been removed from the project. But I wouldn't have expected there to be issues with Objective-C types.
Simply renaming a .h and .m file and then introducing a new .h and .m with the same name will cause problems. Not to xcode, but to you. Best is to copy files to a directory like /tmp, delete them from the project, later add them to the project as new with a different name.

How do I initialize a window in Objective-C?

For a Mac graphics application (not iPhone), I need something like a main method in Java, the first method that gets a program going. I've been looking at things like NSViewController and NSWindow object. I've looked around but can't find an answer to this seemingly easy question anywhere. (I am very new at this by the way)
Thanks
If you create a Cocoa project from one of the Xcode "Cocoa Application" templates, you'll get a main.m file that includes the usual startup code.
Try working through one of the tutorial projects that you'll find in the documentation.
If you are using the project templates, the startup code is in the ApplicationDelegate file. The main.m, for a Cocoa application, sets up the run loop, runs NSApplication (as you can see in the Info.plist file under the 'Principle class' key.
This then loads the nib file that is specified in the Info.plist file (under the 'Main nib file base name' key). The default is MainMenu.xib. Now have a look at this nib file.
This is already set up by the template to have a 'Files's Owner' of NSApplication (the class that loaded the nib) But There is also a blue block which represents the application delegate. This is already filled out with one delegate method one outlet.
The delegate method is applicationDidFinishLaunching: This method is called by the application. This is only one of the possible delegate methods that it can handle, but it is sent after the run loop is started but before the application receives any events. It is the common place to put your initialisation code. It is in here that you should start to set up your window, which you can get to using the pre-supplied window outlet.
This is just a quick summary. A handy referenece is on Cocoa With Love and Apple's Introduction to Application Architecture document.

Linking problems with AppKit

I've created a class using XCode3.2.1 and I want to make it inherit from NSViewController (or any other AppKit entity) .
#import < Cocoa/Cocoa.h>
#interface myCustomView : NSViewController {}
#end
I've linked in the Cocoa libraries, but I get the error that it can't find the class header file
Undefined symbols: "_OBJC_CLASS_$_NSViewController", referenced from:
_OBJC_CLASS_$_myCustomView in myCustomView.o
I have other classes in my project that are inherit Cocoa classes without a problem. I don't have any errors if I make it inherit from classes that are part of Framework or CoreData (eg NSObject, NSArray, NSEntityDescription).
Any suggestions?
Check if your subclassed NSViewController implementation file is in the "Compile sources" build phase of your active target.
Somehow the some of the Frameworks become disconnected in XCode.
Under if you control-click Frameworks and select GetInfo, it the
box had a dash through it (meaning it was partially selected).
Clicking it again activated it for all classes.

Application Delegate - Cocoa

I want to incorporate an applicationDidFinishLaunching: into my cocoa delegate. How would I do this?? On the iphone SDK the applicationDidFinishLaunching is already in the application delegate, but when making my mac application I noticed that there were none.
Best Regards,
Kevin
As of Xcode 3.2, the Mac application template also comes with an application delegate, already connected, that has such a method.
To set this up in a project created before Xcode 3.2, create a new class for your delegate to be an instance of. I usually name mine “AppDelegate”. You'll do this by right-clicking on the Classes group and choosing “Add File”, then picking the Cocoa NSObject Subclass file template.
Open the header you just created (AppDelegate.h). Give it any instance variables you want. Then hit Go to Counterpart. That takes you to the implementation file (AppDelegate.m). Add your applicationDidFinishLaunching: instance method here. Unlike on the iPhone, this is a notification-handler method, so it takes an NSNotification instance and not an NSApplication instance.
Now to hook it up. In the Resources group, open MainMenu.nib. Drag an Object from the Library window into the top-level nib window (the one with icons in it, such as File's Owner and First Responder). Select the object you just created and open the Identity inspector. Set the object's class to AppDelegate, matching the name you used in Xcode. Right-click on the File's Owner, and drag from its delegate outlet to your new object.
In Xcode, add an NSLog statement to your applicationDidFinishLaunching: method. Hit Save All, then Build and Go. Switch back to Xcode and open the Debugger Console. If you did everything right and I didn't forget anything, you should see the log message there.
- (id)init
{
if (self = super init]) {
[NSApp setDelegate:self];
}
return self;
}
You can also do this in Interface Builder; from "File's Owner" in MainMenu.xib, just drag the "delegate" outlet to your object. You may want to consider using -awakeFromNib instead though.
Were you missing the application delegate files altogether? It seems as though there's a bug in the Xcode installation scripts (at least for 3.2.1 on Snow Leopard) that installs the latest project templates in the wrong folder. The older template for a "Cocoa Application" project doesn't contain the delegate files.
I've explained what I've discovered (and how I "fixed" it) in a blog post called Fixing the Xcode Project Templates.
Cheers,
Graham