Trying to replace deprecated loadnibnamed:owner - objective-c

I'm trying to replace the deprecated
[NSBundle loadNibNamed:#"Subscriptions" owner:self];
with this instead (only thing I can find that's equivalent)
[[NSBundle mainBundle] loadNibNamed:#"Subscriptions" owner:self topLevelObjects:nil];
but the dialog pops up and disappears right away instead of staying open like it was doing with the deprecated call.
This code is inside a viewcontroller like this.
- (id)init{
self = [super init];
if (self) {
//[NSBundle loadNibNamed:#"Subscriptions" owner:self];
[[NSBundle mainBundle] loadNibNamed:#"Subscriptions" owner:self topLevelObjects:nil];
}
return self;
}
and I'm calling it from the appdelegate like this.
SubscriptionsViewController *subscriptionsViewController = [[SubscriptionsViewController alloc] init];
[subscriptionsViewController.window makeKeyAndOrderFront:self];
Is there anything I'm missing? It seems straight forward to me.

The dialog appearing and then disappearing is a sign of possible object collection - with a strong reference to the dialog it will be collected and lost.
The deprecated call retained ownership of the top-level objects in the nib, the new call does not.
Therefore the properties of the owner object that refer to top-level objects must be strong, or you need to keep the top-level objects array. This is contrary to the old recommendation where such properties were weak.
Properties which reference non-top-level objects in the nib can still be weak.

I just had a similar problem when using loadNibNamed: owner: topLevelObjects: and always got an error like
[__NSArrayM insertObject:atIndex:]: object cannot be nil' terminating with uncaught exception of type NSException abort() called
Because my top level objects where nil.
I finally discovered that the nib file I was loading had its Interface Builder version set to "Xcode 4.6". When I set that to 6.2, everything worked fine again.

Related

Cocoa Outlets acting wierd, won't recognize selector

I'm getting some weird behavior, I Set a Label in Interface Builder, then I connect the label to a file as an Referencing Outlet.
#property (weak) IBOutlet NSTextField *TitleLabel;
When I access that label in the file (cell.TitleLabel.stringValue = title) and run the application, it doesn't recognize it.I get this:
-[NSApplication TitleLabel]: unrecognized selector sent to instance 0x608000101680
The weird thing is that it doesn't always do this, sometimes it works and displays correctly, other times it doesn't.
I've just started messing with IB so I'm probably missing something. Any help?
Is the property really on your NSApplication subclass? or is it on you application delegate class? It's not impossible for it to be on the application object, but it would be a pretty uncommon (and arguably ill-advised) pattern.
In short, I suspect you're probably connecting it to the wrong object.
EDIT: Ah. I see. You're trying to access things via the topLevelObjects array, but in practice, you can't count on the order of topLevelObjects. What you need to rely on the owner's outlets getting populated, but you're passing nil for the owner. topLevelObjects only exists to give the caller "ownership" (in the reference counting sense) of the top level objects in the xib for memory-mangement purposes, it's not really meant to be "used" directly like you're doing here. (In fairness, I can imagine situations where you might need to introspect that array, but this hardly rises to that level.)
The canonical way to do this would be to use an NSViewController subclass as the owner. In Xcode, if you add a subclass of NSViewController to your project, it will give you the option to create a xib file at the same time that will have everything hooked up. Then you just initialize the NSViewController subclass at runtime and the view outlet property of that class will be filled with the root view. You can obviously add more outlets and plug in whatever you like.
This post appears to cover the basics, if your looking for more details. Apple's docs on xib files and how they work are here.
The problem was that the View would sometimes get assigned to NSApplication. I'm not sure if the way that I am initiating the view is the common way of doing it but the problem was within this block of code:
NSArray * views;
[[NSBundle mainBundle] loadNibNamed:#"CollapseClickViewController" owner:nil topLevelObjects:&views];
CollapseClickCell * cell = [[CollapseClickCell alloc] initWithFrame:CGRectMake(0,0,320,50)];
cell = [views objectAtIndex:0];
the problem was that [views objectAtIndex:0] would sometimes return NSApplication. To fix it I just checked the class against itself and returned that object via:
-(CollapseClickCell*)assignCell:(CollapseClickCell*)cell withArray:(NSArray*)array{
for ( int i = 0; i< [array count]; i++) {
if ([[array objectAtIndex:i] class] == [CollapseClickCell class]) {
return [array objectAtIndex:i];
}
}
return nil;
}
I then assign that to the object:
cell = [cell assignCell:cell withArray:views];
It may not be the conventional way of doing it but it works. If there is a better technique or a more common approach please enlighten me! :)

Unrecongized Selector sent to Instance

Ive been through similar questions on here, but can't seem to relate it to my app!
My problem is when i run the program i get at error message
[UIView setAttString:]: unrecognised selector sent to instance 0x7538c60
Ive debugged the code down to 3 lines in the ViewController class - these are:
NSString *path = [[NSBundle mainBundle] pathForResource:#"g1" ofType:#"txt"];
NSAttributedString* text = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
[(CTView*)self.view setAttString: text];
Im pretty sure its with the final line, but as this line has been lifted from an example app I haven't really grasped the proper understanding of what it does, and therefore can't see anything wrong with it! Any obvious or common errors I could try to resolve? I don't know how much of my code is needed for the clever folk out there to help me out - let me know and I can add more snippets!
Thanks in advance!
In human terms, that snippet is loading a string from your app's resource file, then sending it to the object referenced by self.view by means of the setAttrString: message.
The error you're seeing shows up when an object receives a message that it doesn't recognize. I don't know what a CTView is, but you should ensure that:
The CTView class does actually have a method called setAttrString:
The object referenced by self.view is actually an instance of CTView.
You can verify that latter with the following:
BOOL isCorrectClass = [self.view isMemberOfClass:[CTView class]];
...or just look at it in the debugger at runtime.
Looks like CTView response to setAttString, but your self.view is a UIView instead of CTView. And usually the self.view of a viewController is just a UIView.
If you created the view as an outlet in your IB, make sure you set it to be CTView class.
Or share where did you alloc inited the "self.view" in your last line of code.

how to call a method from a class in another class

I'm working to a new app for mac osx where i'm using a drag and drop system to let the user to input some files [this part works well] and i have a tabelView where i would like to display the paths of files inputed.
I have the next method in tabelViewController.m:
-(void)add{
NSLog(#"da");
[list addObject:[[Source alloc] init]];
[tableView reloadData];
}
In the DropView.m i included the tabelViewController.h and i'm trying to call the add method but it does nothing:
#import "TableViewController.h"
.....
- (void)concludeDragOperation:(id<NSDraggingInfo>)sender{
[self setNeedsDisplay:YES];
TableViewController *tvc;
[tvc add];
}
Can someone to figure out why it doesn't do anything ?
Edit1:
Ok after I fallow the answers, my concludeDragOperation method looks like this:
- (void)concludeDragOperation:(id<NSDraggingInfo>)sender{
[self setNeedsDisplay:YES];
TableViewController *tvc = [[TableViewController alloc] init];
[tvc add];
[tvc rD];
}
rD is a method from tableViewController which contain the reloadData method.
But it doesn't want to work it don't reload the table view.
Any ideea ???
tvc needs to point to an actual object. [[tvc alloc] init]
Otherwise you are simply calling add on nil. This doesn't cause your program to crash as you might expect in other languages. Try it out and see what happens.
it seems as if you missed a great chunk regarding how OOP and Objective-C work (seriously, no offense there).
What link is there between DropView.m and tableViewController.h do you have?
By typing TableViewController *tvc; all you are doing is creating a pointer. You are neither creating an object nor pointing to an object, you have just simply created a pointer that can eventually point to an object in memory of type tableViewController.
Solution:
What you will need to do, is to somehow create a link between the two classes. For instance, you could create a custom delegate method for DropView that could communicate with any class who uses that custom DropViewDelegate methods. So, you could create a delegate method that tells objects that follow that delegate protocol that you just concluded a drag operation. A tutorial how to do so can be found at my blog [it's a permalink].
I am happy to post code, or you can read it on my blog. Good Luck.

Passing a reference and copying the object from another controller. Objects keep disappearing

I know this is a relativly easy question, but I just can't figure out how to solve this problem:
One of my views will receive a dragOperation and the performDragOperation method should pass the NSURL to my AppDelegate which puts it in an mutArray...
The problem is, that I pass over a reference and the object disappears the moment performDragOperation is done. So I tried several things:
in performDragOperation:
//Puts the reference itself in the folderPaths Array
AppDelegate *appDelegate = [NSApp delegate];
[[appDelegate folderPaths] addObject:referenceTotheNSURLObject];
/* tried creating a NSString and putting it in too. Same result because local variables will disappear */
So I created a method in AppDelegate and tried different things:
- (void)addFilePath:(NSURL *)filePath {
NSURL *copyOfURL = [filePath copy];
[[self folderPaths] addObject:copyOfURL];
}
This makes the most sense to me and the only reason I can think about why this doesn't work is, because copyOfURL itself is a pointer and it points to an localObject that will disappear the moment the addFilePath method is finished? But I'm not allowed to use
NSURL copyOfURL = [filePath copy];
I also tried recreating an NSString object again. Same results.
- (void)addFilePath:(NSURL *)filePath {
NSString *pathString = [filePath absoluteString];
[[self folderPaths] addObject:pathString];
}
I know it will be some relatively simply solution but I'm stuck and can't figure it out. Thanks for your time!

loadNibNamed:owner:options crashing app after conversion to ARC

I'm having what I'm sure is a stupid issue after converting my project to ARC. This method has started crashing on the first line:
- (MyView*) loadMyView
{
NSArray* elements = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:nil options:nil];
return (MyView*)[elements objectAtIndex:0];
}
This worked fine before the conversion to ARC. The app crashes in the simulator with the following message:
[CFArray release]: message sent to deallocated instance
Can anybody tell me what I'm doing wrong?
Well, after sleeping on it I decided to just delete the Nib and start over to see if that helped and sure enough, it works now. I don't know what the conversion to ARC did to cause this issue but deleting the Nib and recreating it from scratch solved the problem.
Try changing the owner: parameter from nil to self
NSArray* elements = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
I'm guessing because you are setting the owner to nil, your array is being created with a retain count of zero, and is therefore being deallocated right away.
I had the same issue. It usually happens if you have inconsistencies in your nib file. In mine I had some buttons that were still connected through IBOutlets to some objects that I deleted in the header file. Hope this helps.