I have never used Can draw concurrently option mentioned in iterface builder. See image below:
Can someone explain me its use and purpose?
You can use it to inform AppKit that your NSView subclass' -drawRect: instance method may be called from a secondary thread.
It's 10.6+. When/if AppKit renders views asynchronously, -drawRect: is performed using concurrent blocks, and your -drawRect: will be called from a secondary thread. This means your subclass' implementation must be threadsafe (for compatibility, it is disabled by default).
See also -[NSView setCanDrawConcurrently:] for details, because there are few things required to actually enable this behaviour. As well, it shouldn't imply that AppKit is threadsafe or that its NSView subclasses are designed to support this functionality - It's designed for your heavy drawing.
The option is for view that support threaded drawing for example NSProgressIndicator. If can Draw Concurrently is checked then drawRect can be invoked from a background thread for that view. Refer NSView class reference.
None of the AppKit's views support this (except for NSButton and NSProgressIndicator, but they have separate APIs for this for historical reasons).
Related
I have a MacOS appkit app with a LOT of different NSWindows (hundreds), and they are each created from storyboards.
Many of these NSWindows have container views with complex embedded view/view controller hierarchies.
During initialization, it's necessary to know the model object associated with any given NSWindow, so its subviews and controls can be properly initialized. Since any NSController can know its NSView, and any NSView can know its NSWindow, it would be nice for that information to stored with the NSWindow.
It would be great to set a "representedObject" for the NSWindow, but unlike NSViewController, it doesn't really have one.
Is the only real solution to create a simple custom class (derived from a small base class) for each and every NSWindow storyboard object, so NSViews & NSViewControllers down the view hierarchy can get to my model data (pointer)?
A CLARIFICATION: very few of my NSWindow objects in our hundreds of storyboards have custom classes or code derived from NSWindow. So while a Category is definitely helpful for adding an API to classes to ACCESS the model data associated with the NSWindow, it's not helpful in creating a property or instance variable and initializing it in all those NSWindow storyboards.
ULTIMATELY I PRESENT A SIMPLE BUT DISGUSTINGLY BAD SOLUTION NO ONE SHOULD COPY:
Our app does not use NSDocument, which would provide a facility for associating NSWindow objects with a document/model architecture. So our goal has been to allow each and every NSController and NSView to get access to the appropriate singular document model object required to initialize the view's controls.
I've been warned by Apple engineering gurus that I cannot depend on the order in which views and subviews are created and initialized. That makes passing data down into complex storyboard embedded subviews tricky and error-prone.
But -- with all UI on the main thread, it is not possible for a single application on MacOS to create, initialize, and display one storyboard AND have another storyboard initialization & display interrupt that process (at least not our user-invoked application storyboards). So the simple solution is...
...to have a temporarily set application-level global with the desired document model pointer. That, and a stack-based lock count to insure that the above assumptions are never violated. Terrible design. Efficient solution.
No one needs to remind me WHY this is not good. But if there's a better solution it has escaped my testing. I found that even viewDidLoad and viewWillAppear can't be trusted to have a solid pointer back to its NSWindow...
Without knowing your application structure; you will need a mechanism to assign the model pointer to each individual window. This will necessitate adding some code somewhere. An NSWindow subclass does seem appropriate.
In the AppKit MVC pattern, model data usually fits between the view and the view controller. Attempting to associate the model with the window is fighting against this pattern to some extent.
That being said; the Objective C runtime does allow you to add custom properties to existing classes using categories. This is achieved using Associative References. The relevant functions are:
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
This article has a good rundown of the benefits and downsides of that approach.
I experimented and was able to instantiate UIKit class (UIImageView in this case) in a background thread without issue. I have seen app crashes by doing things with UIKit on a background thread. Behind the scene, what causes exception when dealing with UIKit on a non-main thread? For example, if I stay completely away from dealing with the view hierarchy, is it safe?
It is not safe. There are some documented tasks that are safe to call on a background task, e.g. drawing a string into an image, accesing UIFont, pushing/popping/using current graphics context. However, mostly the behavior is undefined or documented as unsafe. You never know what shared resources the classes are using in the background (or they will in future releases). They could use shared memory pools, for example.
From my experience, in most cases you won't see a problem if the UIView is not added to any UIWindow but still it is not safe.
For example, I think that loading things from nibs (using UINib) must be done only from the main thread... and any view can load subviews from nibs.
In my code, I subclassed NSView and in its drawRect method, I am spawning three threads to perform the drawing.
-(void)drawRect:(NSRect)dirtyRect
{
[[self window] setAllowsConcurrentViewDrawing:YES];
[self setCanDrawConcurrently:YES];
[NSThread detachNewThreadSelector:#selector(DrawText) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:#selector(DrawRectangle) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:#selector(DrawGradient) toTarget:self withObject:nil];
//Wherease these functions DrawText, DrawRectangle and DrawGradient performs their task as suggested by name.
//In DrawText, DrawRectangle, and DrawGradient lockFocus and unlockFocus is being
// used for multithreaded drawing.
}
When I run the same program from Xcode, it is running fine. Output is shown below.
But when I run it from the outside, there is problem and output is shown below.
First, I would like to know is it right way to draw from a secondary thread? Or what is another way to draw from a secondary thread?
What is the reason behind this problem?
Ken Aspeslagh is somewhat incorrect about drawing from secondary threads (he is correct it is generally a bad idea). From what I can see of your code you don't have a good use case for drawing on a secondary thread. Can you explain why you want to do this?
you yourself have already discovered setCanDrawConcurrently: which explicitly talks of invoking drawRect: from a background thread. Note that the views window must have allowsConcurrentViewDrawing set to YES for this to work (it is the default).
Apples own Cocoa Drawing Guide has a section on drawing from secondary threads. I have highlighted some portions I think are relevant to you.
The Application Kit maintains a unique graphics context for each window and thread combination. Because each thread has its own graphics context object for a given window, it is possible to use secondary threads to draw to that window. There are some caveats, however.
During the normal update cycle for windows, all drawing requests are sent to your application’s main thread for processing. The normal update cycle happens when a user event triggers a change in your user interface. In this situation, you would call the setNeedsDisplay: or setNeedsDisplayInRect: method (or the display family of methods) from your application’s main thread to invalidate the portions of your view that require redrawing. You should not call these methods from any secondary threads.
If you want to update a window or view from a secondary thread, you must manually lock focus on the window or view and initiate drawing yourself. Locking focus configures the drawing environment for that window's graphics context. Once locked, you can configure the drawing environment, issue your drawing commands as usual, and then flush the contents of the graphics context to the window buffer.
In order to draw regularly on a secondary thread, you must notify the thread yourself. The simplest way to send regular notifications is using an NSTimer or NSAnimation object. For more information on how to animate content, see “Advanced Drawing Techniques.”
The Cocoa Threading Programming Guide also says this:
If you want to use a thread to draw to a view, bracket all drawing code between the lockFocusIfCanDraw and unlockFocus methods of NSView
Just an aside, GCD block invocation is probably a much nicer method for performing small sets of operations in the background than NSThread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// you can put each of these calls in their own queue if you want
[self DrawText];
[self DrawRectangle];
[self DrawGradient];
});
However, that likely has nothing to do with your problem; I mention it only because I think it will serve you better to use GCD queues.
You should only be drawing to the screen from the main thread.
Edit: It is apparently really complicated so you're better off drawing to the screen from the main thread. ;)
If you need to render something that takes too much time to avoid blocking the main thread, consider using a thread to do the drawing to an offscreen context, and then copy that context to the screen on the main thread.
I read about NSGraphicsContext Restriction at Thread guide.
Here, I found the following line:
If you do any drawing from a secondary thread, you must flush your drawing calls manually. Cocoa does not automatically update views with content drawn from secondary threads, so you need to call the flushGraphics method of NSGraphicsContext when you finish your drawing. If your application draws content from the main thread only, you do not need to flush your drawing calls.
After calling flushGraphics, it works fine.
This question is for both cocoa and cocoa touch. But I'll write an example just for cocoa.
As I understood, I can setNeedsLayout to YES multiple times in a cycle and -layout will be called just once. But are there any other benefits of laying out subviews in -layout method?
Explanation / example: At the moment I'm laying out my subviews in custom viewController (that has default NSView) every time I call custom redraw method. And I call redraw method only when user changes some properties so I really want to relayout subviews.
There are plenty of external circumstances not under your direct control that might cause the system to want to lay out your views. For example, device rotation or incoming calls on iOS, or window resizing on OS X. If you have your layout logic in the standard places, then your code accommodates these without any additional effort, and in the places your internal state changes, you can request such a layout explicitly.
To turn your question around: is there a significant benefit to not doing your layout in the standard way? Do you believe that this will be a performance issue? Have you measured it to see whether it is actually a performance issue?
While working on a small project I found myself needing to do some custom drawing via drawRect: in one of my UIView subclasses. I noticed when I overrode drawRect: that the default background color of the UIView subclass had changed from transparent to black (by default background color I mean the color the view draws itself when its backgroundColor property is nil.) Even with an empty drawRect: or a drawRect: that simply calls [super drawRect:] I noticed this behavior.
This isn't really a problem, as simply setting a backgroundColor to a non-nil value works regardless of whether drawRect: is overridden. However, it did make me start thinking about how UIView knows whether drawRect: is overridden by a subclass. I know Objective-C offers facilities to determine if a class or even its superclass responds to a certain selector. But how could a superclass possibly know if its subclass has overridden one of its methods? And, if this type of introspection is indeed impossible, what could be going on in my example?
That is pretty bizarre (but sounds like it is intended). Simply adding:
- (void) drawRect:...
{
[super drawRect:...];
}
Triggers the behavior? Atypical. In any case, it is trivial to use the Objective-C runtime API to introspect class implementation details quite thoroughly. See the Objective-C runtime reference.
UIView's documentation for -drawRect: goes into considerable detail about subclassing. It quite specifically states that you don't need to call super when directly subclassing UIView, indicating that the class is likely optimized to doing the minimal amount of extra work (like drawing a background that is obliterated entirely in your implementation).