Do UIKit objects need to dealloc on the main thread? - uikit

With respect to UIView, Apple has some comments on threading recommendations:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW147
Manipulations to your application’s user interface must occur on the
main thread. Thus, you should always call the methods of the UIView
class from code running in the main thread of your application. The
only time this may not be strictly necessary is when creating the view
object itself but all other manipulations should occur on the main
thread.
Is it generally (un)safe to dealloc a UIView object in a background thread? I'm looking for some supporting documentation in either case.

Yes, UIKit objects need to dealloc on the main thread. It is not safe to dealloc a UIView object in a background thread. See here for more details.

iOS Developer Library docs link to NSView Restrictions section so I guess they apply also to UIView.
NSView Restrictions
The NSView class is generally not thread-safe. You should create, destroy, resize, move, and perform other operations on NSView objects only from the main thread of an application. Drawing from secondary threads is thread-safe as long as you bracket drawing calls with calls to lockFocusIfCanDraw and unlockFocus.
Source:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123427
I hope this helps.

Related

Why is applicationDidFinishLaunching called after main view's draw method in AppKit [duplicate]

This question already has answers here:
OS X storyboard calls viewDidLoad before applicationDidFinishLaunching
(5 answers)
Closed 5 years ago.
I am used to performing app initialization in applicationDidFinishLaunching in iOS, but in appKit it is getting called after the main view's draw method! It is too early to draw anything since the app hasn't been initialized. Here is the order:
viewDidLoad (main view controller)
draw(_ dirtyRect:) (view of main controller)
applicationDidFinishLaunching (too late!)
The API reference says the following about applicationDidFinishLaunching
This method is called after the application’s main run loop has been
started but before it has processed any events.
Thus a draw event should be posted after applicationDidFinishLaunching.
I am not used to this behavior -- what is going on?
You're just making a false assumption. The initial view controller's viewDidLoad does fire before applicationDidFinishLaunching is called. That's because the runtime loads the storyboard's initial window controller, and causes its view to be loaded, before calling applicationDidFinishLaunching on the app delegate.
If you need an event before the main view's draw, implement applicationWillFinishLaunching. But it would be even better, since you now know the actual order of events, to do your application initialization in the main view controller's viewDidLoad, as it is even earlier. After all, if what you draw initially depends on an initialization you perform earlier, viewDidLoad seems to fit perfectly.
The earliest event you can get in a conventional modern storyboard-based launch is the app delegate's awakeFromNib. But be careful because in macOS it can be called more often than you might expect. (Actually, the class method initialize is even earlier, but be warned that you can't implement that in Swift.)

How to manage windows in cocoa

I know the question is a bit generic but I guess my issue is generic as well.
I'm developing a small application in my free time and I decided to do it with Cocoa. It's nice, many things works almost automagically, but sometimes it's quite hard to understand how the framework works.
Lately I'm facing a new problem. I want to manage all the windows of the application from a single class, a front controller basically. I have a main menu and an "Import data" function. When I click it I want to show another window containing a table and call a method for updating the data. The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class? And more important, which should be the right way to do it? Should I extend the NSWindow class so that I can receive an Instance of NSWindow that can control the window containing the table (and then call the method)?
I may find several ways to overcome this issue, but I'd like to know which one is the best practice to use with cocoa.
PS: I know that there are tons of documentations files, but I need 2 lives to do everything I'd like to, so I thought I may use some help asking here :)
The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class?
These two sentences don't make sense, but I think I understand what you're getting at.
Instead of subclassing NSWindow, put your import window's controlling logic – including your NSTableViewDataSource methods – into a controller class. If the controller corresponds to a window, you can subclass NSWindowController, though you don't have to.
You can implement -importData: as an IBAction in your application delegate, then connect the menu item's selector to importData: on First Responder. That method should instantiate the import window controller and load the window from a nib.
In your import window controller's -awakeFromNib or -windowDidLoad method, call the method which updates the data.
Added:
Here's the pattern I'd suggest using in your app delegate:
#property (retain) ImportWindowController *importWC;
- (IBAction) showImportWindow:(id) sender {
if (!self.importWC)
self.importWC =
[[ImportWindowController alloc] initWithWindowNibName:#"ImportWindow"];
[self.importWC refreshData];
[self.importWC.window makeKeyAndOrderFront:sender];
}

NSWindowController clarification of understanding

I have used NSWindowController in projects several times, and feel like I have a (very)rough grasp of the concepts behind this important class. What I would like to do with this post is to clarify/correct my own understandings, and hopefully help other learners get that first step into understanding. It's the at-a-glance concepts, overview, and best practices that I find is most useful, and often lacking in the documentation. Here is my take on NSWindowController (questions are interspersed in bold):
An NSWindowController (NSWC) subclass exists (conceptually) just beneath every window nib, acting as the glue between the user interface elements and the model objects that they control/represent. Basically, every window in your application should have its own NSWC subclass.
The File's Owner of the nib should always be the NSWC subclass. Is this the case even for the MainMenu.xib application?
The NSWC window property should always be linked to the NSWindow in InterfaceBuilder.
You should override the 'init' method, using [super initWithWindowNibName:], so that when you refer to [mycontroller window] it will load the nib. Should this also be the case for the NSWC for the MainMenu.xib window, even though this is opened at startup?
The NSWC shouldn't do too much heavy lifting - it should simply pass messages to instances of objects, and present those objects in the UI.
It can modify the UI using binding, or acting as a delegate for tables etc., or by actively changing the UI elements when it observes a change, or a combo of any of the above (which one you use seems to be a matter of taste, with pros and cons on all sides).
An NSWC can create instances of other NSWCs when necessary (for example, when opening a one-off sub-window).
Use the [mycontroller showWindow:nil] to display the associated window at the front. If you want the window to appear as a sheet, use something like:
NSWindowController* mycontroller = [[MyController alloc] init];
[NSApp beginSheet: [mycontroller window]
modalForWindow: [self window]
modalDelegate: self
didEndSelector: #selector(didEndMySheet:returnCode:contextInfo:)
contextInfo: nil];
The didEndSelector: should be a method of the NSWC of the parent window, and can access and release 'mycontroller' with [sheet windowController].
- To close the window call the performClose: method of NSWC's window.
Some Questions:
Should the NSWC of the MainMenu window also be the application delegate, or should this be a different class?
In the same vein, should the main NSWC handle files (drag/drop and opening), or should it be passed on to the app delegate, or is that just a matter of taste?
Please correct me if any of this is bad practice, or is just plain wrong. I am looking to clarify my understanding of NSWindowController, so any additions (in the form of best practices, experiences, gotchas) would be highly appreciated.
Thanks,
Laurie
What are window controllers actually for?
Window controllers are tools to load a window from a NIB file and for managing the memory of the resources allocated in the NIB. Before there where NSWindowControllers one basically had to write the same code for every window or invent an own window controller class.
Of course they are also controllers in the Model/View/Controller sense, so they are the right place to connect the views from the window to the model objects. To do this they often need to act as the delegate or data source for a view object. So you got this part perfectly right.
Also window controllers are a tool for code reuse. It makes it easy to drop the window controller class and it’s XIB/NIB into another project and use it there.
So yes, every window from a NIB should be owned by a window controller, with one exception. Actually, this is just a guideline for good code, nothing enforces it.
WindowControllers and MainMenu.xib
MainMenu.xib is a different thing, there you can’t use a window controller. This NIB gets loaded by NSApplication so this has to be it’s "Files owner". There is no way to get a window controller between the NSApplication and the NIB. It also isn’t necessary to use a window controller for memory management there, since the application object lives for the entire runtime of the program, so it doesn’t have to clean up it’s resources from the NIB when it gets deallocated.
If you really need a window controller for your main window you cannot put this in the MainMenu.xib.
I hope this helps. There probably is a lot more to say about window controllers too
Is this the case even for the MainMenu.xib application?
No, the MainMenu nib is owned by NSApplication (that's who loads it).
Should this also be the case for the NSWC for the MainMenu.xib window, even though this is opened at startup?
No, NSApplication loads the main nib based on your applications file's "NSMainNibFile" property. (It just happens to be pre-set to "MainMenu" in the template Xcode projects.) If you want to change its name then change it there (and rename your nib file). (BTW: This property can also be changed in your target's "Summary" view in Xcode 4.)
Should the NSWC of the MainMenu window also be the application delegate, or should this be a different class?
The owner of the NSMainNibFile nib is the instance of NSApplication that loads it and by association any delegate of that instance. Neither of these are NSWC sub-classes.
In the same vein, should the main NSWC handle files (drag/drop and opening), or should it be passed on to the app delegate, or is that just a matter of taste?
There is no "main NSWC" (The app/app-delegate is the controller for the NSMainNibFile).
All drag-n-drop operations are handled by NSWindow or NSView sub-classes. I usually use a special NSWindow or NSView sub-class that just passes all drag-n-drop methods thru to the delegate. For example:
- (unsigned int) draggingEntered:sender
{
return [[self delegate] draggingEntered:sender];
}
This way I can keep all my window/view code together in their respective controller (as determined by their nib owner). And because the window/view specific code is in the controller (not the NSWindow/NSView subclass) different types of NSWindows/NSViews can all use the same drag-n-drop sub-classes.

Unpredictable EXC_BAD_ACCESS and objc_msgSend_vtable5

I was testing my application and suddenly it had EXC_BAD_ACCESS. Now this has become a semi-regular thing, happening on some builds and not others. It also spits out lots of errors in the debugger such as objc_msgSend_vtable5 What could cause such a weird issue like this?
Looks like it was my fault. To fix it all I needed to do was call retain.
I've recently had just this same error. The short version: it occurred because I had an object that had a delegate that had been released. Make sure you have control over the life cycle of any delegates you assign, be it via Interface Builder or otherwise.
The long version: the error occurs when the Objective-C runtime tries to send a message to an object that's been ultimately released and deallocated. This is different to sending a message to a nil object (a perfectly valid Objective-C operation). In these cases, the runtime knows that the object is nil and responds accordingly. Rather, in this case, Objective-C thinks it has a real, valid object to communicate with and sends a message to an address that contains no object.
In my case, I had an object of type NSTextView which was connected up to a delegate by Interface Builder. When a view or view controller is constructed with a NIB/XIB file, Cocoa hooks up all the child views as instructed, including hooking up any delegates that you might have.
My error was that I had created a subclass of NSView, made it an NSTextViewDelegate and then told Interface Builder to hook up a child NSTextView to that parent view. However, during the various operations I was conducting on the owning NSViewController, I had told Cocoa to remove the NSView from the view hierarchy and reinsert it later.
AppKit specifically tells you not to retain any IBOutlets you might have in your classes, but rather to keep track of these by simple assignment. This is because AppKit handles the memory management of these for you. Little did I know that by asking the view to leave the view hierarchy for a while, I had trigged AppKit to clean up that NSView. Since only AppKit had any retains on the view object itself, [pfft], off it went into the ether. Deallocated.
Since I had no direct control over the NSTextView's delegate, the NSTextView now pointed to a delegate that had been released, even though it had a valid parent view. The next time it tried to send a message to its delegate, the Objective-C runtime fell over.
So, make sure that the delegate is one that you own, or at least know will never get released at an inopportune moment.

AppDelegate or AppController

While reading cocoa tutorials i've noticed that some of the tutorials use AppDelegate and some AppController for defining IBActions that open various windows that use subclasses of NSWindowController.
Is there some sort of rule of thumb for this?
I create one class that is solely my app delegate, and instantiates my main controller (in applicationWillFinishLaunching:) and releases it (in applicationWillTerminate:). Then I have two classes with clearly-defined responsibilities: My main controller is the owner of the main window and the model, and the app delegate is nothing but the app delegate (and, as part of these duties, the owner of the main controller).
It's just a class name. AppDelegate implies that the class's main duty is as NSApplication's delegate, whereas AppController seems to imply a broader range of responsibility.

Resources