UNOFFICIAL
Cocoa-dev Frequently Asked Questions
Extended for the needs of Objective-Basic by Bernd
Noetscher
This article is Copyright (c) 2003,2004 by Alastair Houghton. It is provided as a service to the Cocoa developer community, and may be freely distributed and reproduced as long as this copyright notice is left intact.
Certain topics come up again and again on this list. Whilst they are good questions, a lot of time is wasted on repetitive responses, even more when controversial topics rear their ugly heads.
To contact the maintainer of this FAQ, please post a message to the Cocoa-dev list (cocoa-dev@lists.apple.com), prefixing in the subject with FAQ: That way, there won't be a problem if ownership of this document is handed-over at some point. Note that you need to be subscribed to the list in order to post to it.
If you have a question that you think should be part of this FAQ, please send the question and your answer to the FAQ maintainer. The maintainer reserves the right to amend, include or reject your submission as appropriate, although you will usually receive an explanation as to why your question was or was not accepted.
If you have a service or product that you believe will be useful to the Cocoa community at large, the maintainer will consider mentioning it in the FAQ.
There are no guarantees, and blatant plugs are certainly out... any decisions will be made on a case by case basis. Please note that it will help your case tremendously if you provide unbiased information about competing products and services (otherwise the maintainer will need to do the necessary research before adding any information, which could take some time).
N.B. This document is UNOFFICIAL. It has no connection whatsoever with Apple... they did not write it and are not responsible for its accuracy or content. Any correspondence relating to the FAQ should be directed to the current FAQ maintainer.
Some terms used this document are trademarks or registered trademarks of Apple, IBM, Motorola, Microsoft and others. All such uses are hereby acknowledged.
Subject: 1. Learning Cocoa
* 1.1 I'm new to programming and I'd like to learn Cocoa. What should I do?
Start by learning Objective-Basic and C would be nice. You can ask questions about Objective-Basic on www.objective-basic.com. Listed below are a number of books and other resources you can use to help you here. Please try to avoid asking questions about learning C on the Cocoa-dev list... the Usenet newsgroup comp.lang.c is a much better place to ask, not least because there are a larger number of expert C programmers reading that newsgroup than there are reading the Cocoa list.
It is probably also worth mentioning that people will not take kindly to your posting questions not related to ANSI C (such as those about Mac programming) to comp.lang.c; for Macintosh related topics, consider using the Usenet newsgroup comp.sys.mac.programmer.help or one of Apple's mailing lists instead.
Once you've learnt C, see 1.2.
Books:
The C Programming Language (2nd ed.) -- Brian W. Kernighan, Dennis M. Ritchie (Prentice Hall, April 1988). ISBN 0-13-110362-8
Programming in ANSI C (Revised edition: 1994) - Kochan, Stephen (SAMS Publishing, April 1994). ISBN 0-672-30339-6
On the web:
comp.lang.c FAQ http://www.eskimo.com/~scs/C-faq/faq.html
Learn C/C++ Today http://www.cyberdiem.com/vin/learn.html
Usenet:
comp.lang.c
comp.sys.mac.programmer.*
* 1.2 I know C or BASIC and I'd like to learn Cocoa. What should I do?
First, make certain that your C is up to scratch. If it is, move on to Objective-Basic. Objective-Basic (or Objective-C) is the small superset of C that is typically used for Cocoa development. Objective-Basic is very similar to Objective-c. Questions about Objective-Basic (Objective-C) may be directed to the list, or alternatively consider the Usenet group comp.lang.objective-c.
A few resources useful for learning Objective-Basic (Objective-C) are:
Books:
Programming in Objective-C - Kochan, Stephen (SAMS Publishing, November 2003). ISBN 0-672-32586-1 (Not published yet, but the original C book was supposed to be good.)
Objective-C: Object-Oriented Programming Techniques - Pinson, Wiener.
ISBN 0-201-50828-1
Additionally, most Cocoa books contain at least a chapter on learning Objective-C; see section 1.5 for a list of books specifically about Cocoa.
On the web:
comp.lang.Objective-C FAQ
http://www.faqs.org/faqs/computer-lang/Objective-C/faq/
The Objective-C Programming Language (HTML/PDF of the book above) http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/index.html
Usenet:
comp.lang.objective-c
* 1.5 I'm learning Cocoa... which book should I buy?
This is a very difficult question to answer, as people have widely differing opinions as to which book is best. If you're an experienced programmer, you'll probably like Cocoa Programming (Anguish, Buck and Yacktman).
Otherwise, the following books are available:
Cocoa Programming - Anguish, Buck & Yacktman (SAMS Publishing, September 2002). ISBN 0-672-32230-7
Learning Cocoa with Objective-C (2nd ed.) -- Davidson, Apple Computer (O'Reilly & Associates, October 2002). ISBN 0-596-00301-3
Building Cocoa Applications: A Step by Step Guide - Garfinkel & Mahoney (O'Reilly & Associates, May 2002). ISBN 0-596-00235-1
Cocoa Programming for Mac OS X - Hillegass (Addison-Wesley, December 2001). ISBN 0-201-72683-1
Cocoa Recipes for Mac OS X - Cheeseman (Peachpit Press, November 2002).
ISBN 0-201-87801-1
Cocoa in a Nutshell: A Desktop Quick Reference - Beam & Davidson (O'Reilly & Associates, May 2003). ISBN 0-596-00462-1
Cocoa Programming for Dummies - Tejkowski (For Dummies, March 2003).
ISBN 0-764-52613-8
Learning Cocoa - Mott, Apple Computer (O'Reilly & Associates).
ISBN 0-596-00160-6
Cocoa Design Patterns - Buck (O'Reilly & Associates, September 2003).
ISBN 0-596-00430-3
Instant Cocoa - Feiler (Hungry Minds, Inc., April 2003). ISBN 0-764-53755-5
Mac OS X: Advanced Development Techniques - Zobkiw (SAMS Publishing, April 2003). ISBN 0-672-32526-8
Recent threads on this precise topic can be found in the list archives at
A few specific threads to check-out:
http://cocoa.mamasam.com/COCOADEV/2003/08/2/71524.php http://cocoa.mamasam.com/COCOADEV/2002/08/1/41960.php http://cocoa.mamasam.com/COCOADEV/2003/01/1/53787.php
Additionally, Cocoadev has a useful list of books with reviews, here:
http://www.cocoadev.com/index.pl?CocoaBooks
* 1.6 Where can I get Cocoa training?
There are two companies that offer instructor-led Cocoa programming courses: Apple and The Big Nerd Ranch. Both classes are 5 days long and cover the tools and techniques used by Cocoa programmers. The Big Nerd Ranch courses are taught by Aaron Hillegass, the author of "Cocoa Programming for Mac OS X".
See
or
for more information.
Subject: 2. Objective-C
* 2.2 Is Objective-Basic (Objective-C) portable? Can I write cross-platform applications in it?
Objective-Basic is only available for Mac. But you could use your program written in Objective-Basic on other platforms as well, if you would take the generated Objective-Basic (Objective-C) code and compile it on each platform you need. However, there are a few obstacles if you want to write cross-platform Cocoa applications (see section 3).
If you want to compile Objective-Basic (Objective-C) on other platforms, you can either use the GNU Compiler or the Portable Object Compiler; see
http://users.pandora.be/stes/compiler.html
* 2.4 What does #import do?
Experienced C programmers normally write
#ifndef MYHEADER_H_
#define MYHEADER_H_
...
#endif /* MYHEADER_H_ */
around the contents of header files to prevent their contents from being included more than once in the same source file (these pre-processor statements are usually referred to as header guards). If you "#import" a file rather than "#include"ing it, the pre-processor guarantees that it won't even try to include its contents more than once, so you don't need the header guards.
Some people strongly dislike #import, and will tell you that you should use #include instead.
* 2.5 What are "id", "IMP", "SEL" and "nil"?
"id", "IMP" and "SEL" are all types built-in to the Objective-Basic (Objective-C) language.
A variable of type id holds a pointer to an Objective-Basic (Objective-C) object of any class.
"nil" is a NULL pointer of type id. Messages sent to nil will have no effect, and, if they are declared as returning an object, will return nil.
For portability reasons, you should not rely on the return value of messages sent to the nil object unless the message is declared as returning an object.
A variable of type IMP holds a pointer to a method implementation, which can be used just like any other function pointer except that it requires two additional arguments; for example, given the class
@interface CFAQDemoClass
{
}
-(int)impMethodWithInt:(int)parameter;
@end
you could obtain an IMP using
IMP myImp = [myDemoObject methodFor:@selector(impMethodWithInt:)];
and use it like this:
int ret = ((int (*) (id, SEL, int)) myImp)(self,
@selector(impMethodWithInt:), 103);
There is normally no need to use an IMP in your programs (it is only present to permit certain specific optimisations).
A variable of type SEL holds a selector value, which you can create using the @selector() built-in function.
By the way, if you are asking a question this basic, you should probably read The Objective-C Programming Language at
http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/index.html
* 2.6 When should I use an IMP?
You should only use an IMP if you need to optimise a particular section of your program. Before optimising, you should profile; otherwise you do not know what you need to optimise, or indeed whether there is any need to optimise at all.
IMPs are good for the case where you are calling the same method over and over in a tight loop. However, they have disadvantages; for example, if you load a bundle into your application, you may find that your IMP is pointing at the wrong method!
* 2.7 How fast are Objective-Basic (Objective-C) messages?
The name "message" might make you think that they are slow; however, they are actually quite fast. Here are some figures from a 1GHz PowerPC G4 (courtesy of Marcel Weiher):
Operation | Time (ns)
------------------------------+------------
Increment (memory) variable | 2
Call through an IMP | 12
Local function call | 18
Cross-module function call | 37
Objective-Basic (Objective-C) message | 54
atoi("1") | 182
Local function call refers to a call to a function in the same executable or dynamic object module. Cross-module function call is a call from one executable or dynamic object module into another.
On current Apple versions of GCC, an Objective-Basic (Objective-C) message results in a call to objc_msgSend(), which is itself a cross-module function call. That means that the actual method dispatch only takes 17ns (on average), which is pretty quick.
* 2.8 When should I use a subclass?
Subclassing is only appropriate in Objective-Basic (Objective-C) when you are creating a new variant of a particular class. Before subclassing anything, answer the following questions:
A. Is the class I'm creating a logically distinct sub-class of the class I'm subclassing? If not, then you are probably about to do something wrong.
(For example, Boy and Girl would be appropriate sub-classes of Child; however, Adult should not be a subclass of Child, nor should Child be a subclass of Adult).
B. Does the class I'm about to subclass provide delegate methods that would allow me to obtain the behaviour I'm looking for? If so, consider using delegation instead of inheritance.
C. Am I subclassing just to add a few methods, leaving the rest of the
class's behaviour intact? If so, consider using a category instead.
The second and third questions are especially important if you have a background in C++ or Java; programmers that have used those languages are often over-enthusiastic about subclassing when they start using Objective-C.
* 2.9 I ignored the compiler warnings and my program doesn't work.
Read the compiler warnings. Ideally, you shouldn't be getting any compiler warnings when you build your program.
* 2.10 Why is Objective-Basic (Objective C) better/worse than <insert language here>?
Questions of this nature are hard to answer, and even harder to answer correctly because to do so requires someone with an expert knowledge of both the languages in question. It is also, to a certain extent, subjective, in that some people prefer certain styles of programming languages that are disliked by other people.
Please try to avoid asking the list this question (in any variant); we have collectively answered it several times before, and the discussion tends to deviate quite quickly from the topic we are supposed to be discussing (Cocoa).
It is generally the opinion of users of Objective C that they can achieve a greater amount in a shorter period than is possible other C-family OO languages, a fact that is in no small part attributed to the more dynamic nature of the type system; similar conclusions have generally been drawn by users of other languages with dynamic type systems (Forth, Lisp, Scheme and Smalltalk probably being the most popular).
(1) Abstract classes are not a language feature in Objective C, however they
do not need to be; they are simply classes that you do not instantiate.
(2) Objective C only provides access control for member variables; it is not
as widely used as the similar features in Java and C++, as ObjC programmers are generally more trusting of users of their classes. It is usual in Objective C to use naming conventions (specifically, a unique prefix) to label things as private, protected, etcetera.
(3) Both ObjC and Java support introspection, hence it is possible to examine
a class to determine whether it supports a specified interface. In C++, additional layers of software are necessary (e.g. COM).
(4) Requires the use of JNI, and means that Java binary is no longer platform
independent, as it requires a suitable compiled version of the C library code. This is relatively complicated.
(5) Requires extern "C" { } wrappers. Some libraries may need extra work,
for example because they use variable or function names that are C++ keywords; this can be complicated to fix.
(6) This has just been added by Apple.
(7) Transparent CORBA is by way of a third-party framework, ADORB, available
at
http://homepage.mac.com/v_ananiev/adorb/home.html
Both Distributed Objects and ADORB are highly dynamic; they do not even require an IDL compiler!
You can find many more language comparison charts on the Internet... for example,
http://www.approximity.com/ruby/Comparison_rb_st_m_java.html
or
http://www.smallscript.com/Language%20Comparison%20Chart.asp
Subject: 3. Cocoa
3.1 Memory Allocation
* 3.1.1 Cocoa memory management is confusing. All those pointers and retain/releases make my head spin.
This appears to be a common problem. Fortunately it is easily resolved; follow the simple guidelines below and you will be fine:
i) If you obtain an object by asking for a property of another object,
you need to retain it if you expect to make use of it outside of the lifetime of that other object. For example, if my object has a colour property, and I wish to remember what colour it was after the object has been destroyed, I should retain it.
ii) If you obtain an object from a container (like NSArray), you need to
retain it if you expect to make use of it outside of the lifetime of the container.
Iii) If you obtain an object by any other means, you need to retain it if
you wish to use it outside of the lifetime of the current autorelease pool. In practice, this could be as short as the function or method you are writing... for example, someone could write
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; myMethod();
[pool release];
around a call to your method.
iv) If you explicitly create an object using any method with the words
"copy", "alloc" or "new" in its name, you need to release it.
v) If you explicitly retain an object, you need to release it.
vi) The object ownership chain must not contain loops. If it does, then
clumps of objects can leak; for example, consider the following:
D ----> A ----> B ----> C
^ |
| |
\_______________/
where A, B and C are three objects and the arrows indicate a "retains" relationship (so D retained A, A retained B, B retained C, and C retained A). Notice that the reference count on A is 2, so when A is released (by whatever code owns D), A will not be de-allocated, and the reference counts on all objects in the loop remain at 1.
There are also a number of very comprehensive articles available:
Very simple rules for memory management in Cocoa - mmalcolm crawford http://www.stepwise.com/Articles/Technical/2001-03-11.01.html
Hold Me, Use Me, Free Me - Don Yacktman
http://www.stepwise.com/Articles/Technical/HoldMe.html
Memory Management 101 - Erik Barzeski
http://cocoadevcentral.com/articles/000055.php
Memory Management in Objective-Basic (Objective-C) - Michael Beam http://www.macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html
Introduction to Memory Management - Apple Computer http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
* 3.1.2 I created my object like this, but it crashes whenever I try to use it.
You probably wrote
MyClass *myObject;
[myObject doSomething];
and expected it to work. You haven't actually created an object; instead, you've declared a pointer to one, and as you haven't initialised it, there is a very good chance that your program will crash.
Allocate an object using [[MyClass alloc] init], and set the myObject pointer equal to the pointer it returns; for example
MyClass *myObject = [[MyClass alloc] init];
[myObject doSomething];
[myObject release];
Notice that we've added a release... looking at the memory management rules in 3.1.1, you'll see that rule (iv) says that since we wrote "alloc" in our code, we need to release or autorelease the object in order to avoid a memory leak.
* 3.1.3 Can I declare an object on the stack?
No. Objective-Basic (Objective-C) doesn't let you declare static object instances; you can only declare pointers to objects.
* 3.1.4 I've been told I should always autorelease objects that I return from my class's methods. Surely that's inefficient?
Yes. There are differing opinions within the developer community on this subject; some people feel that the advantages of always autoreleasing outweigh the disadvantages, and vice-versa. Apple currently say that you should, but as people have noticed, Apple's classes do not always follow that rule. It is best not to make assumptions about the lifetimes of objects returned by methods of other objects; that way, you cannot be caught-out.
* 3.1.5 Should I use -allocWithZone:[self zone] rather than just -alloc?
Probably not. Zones are an optimisation, and one that only works in certain cases; worse than that, when memory zone techniques do not work, they tend to make things worse rather than better.
Unless you have a specific reason for using zones, avoid them.
* 3.1.6 When I raise an exception, my application leaks objects.
Quite probably. Objective-Basic (Objective-C) doesn't (currently) have support for destruction of objects whose only references are pointers held in the local variables of functions whose stack frames are unwound by exception handling.
Either don't use exceptions, or autorelease any object that might need to be destroyed if an exception occurs.
(Apparently, NSAutoreleasePool objects do get destroyed during exception handling, so local declarations of those are fine.)
* 3.1.7 My program leaks objects/memory. What can I do?
First, remember that autoreleased objects are only released when their containing autorelease pool is released. Also remember that some objects are created once, but on-demand (i.e. Not at start-up). Apple provide several tools to help you track down memory and object leaks, including:
ObjectAlloc
MallocDebug
leaks
heap
malloc_history
The first two are in /Developer/Applications, whilst the latter three are command line tools. In addition, you can set the environment variable MallocHelp to YES and run any program to see a list of other environment variables that you can set to enable debugging features in the Mac OS X implementation of malloc().
For example, under tcsh (the default shell), you might enter
setenv MallocHelp=YES
true
while if you are using a Bourne shell (e.g. Bash),
MallocHelp=YES /usr/bin/true
will work.
In addition to the Apple-supplied tools, Omni Group have a more sophisticated object tracking tool, OmniObjectMeter, that may be worth a look.
* 3.1.8 My program is crashing, and I think it might be because of a problem with autoreleased objects. What can I do?
There are a variety of variables you can use to control the behaviour of the autorelease mechanism; this will allow you to detect the code that is (for example) incorrectly releasing an autoreleased object. Take a look in
/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h
for more information.
See also section 4.1.4, which deals with crashes.
3.2 Views & Drawing
* 3.2.1 My view is upside-down!
Yes. Now that you're over the shock, let's discuss why. Either your view is returning YES from its -isFlipped method (in which case the positive Y axis runs downwards, the conventional direction for computer displays), or your view is returning NO, in which case the positive Y axis runs upwards.
It is also possible that you have used NSAffineTransform to invert the axis, but given that you're surprised by the view not being the way up you thought it was, that probably isn't the problem.
* 3.2.2 When I draw a line, it comes-out all thick and fuzzy. What's wrong?
You're drawing at integer co-ordinate values. The default co-ordinate system used in Mac OS X has the integer values falling on the pixel *grid*, and not on the pixels themselves. That is, the bottom left hand pixel in a non-flipped view is a rectangle from (0.0, 0.0) to (1.0, 1.0), whose centre is at (0.5, 0.5). If you tell Mac OS X to draw a line from e.g. (5.0, 5.0) to (5.0, 105.0), it will do the best it can by putting half of the colour you wanted into each pixel either side of the line. To get a single-pixel line, offset your co-ordinates by 0.5 in each direction.
* 3.2.3 Drawing is too slow. How can I speed it up?
Either
A. Optimise your drawing, by drawing only what you need and by minimising the complexity of any NSBezierPaths you are using (fewer lines make NSBezierPath render much faster, because it has to compute all of the self-intersections in order to get the antialiasing correct).
B. Cache rendered data in an NSImage. This is probably worth doing for complex areas of a drawing that you don't expect to change very often.
Or
C. Draw using OpenGL instead; this will take advantage of any hardware
acceleration provided by your graphics card, but the result will not be of as high a quality.
It may help (depending on your application) if you increase the flatness value, using Core Graphics' CGContextSetFlatness(), which will increase image quality at the expense of performance. Similarly, if you don't need antialiasing, you can turn it off with NSGraphicsContext's -setShouldAntialias:. Also, if you are using scaled images extensively, consider using NSGraphicsContext's -setImageInterpolation: method to reduce the interpolation quality.
The reason that Quartz draws more slowly than other contemporary graphics systems is that it uses a more advanced imaging model and is capable of very high quality rendering. Quartz is a workstation-class graphics system, rather than an evolution of one of the consumer-oriented systems (QuickDraw, Windows GDI, GEM VDI et-cetera).
There is an excellent section in Cocoa Programming that covers this topic (see Ch. 13 pp. 496 to 519).
* 3.2.4 I put one view on top of another in Interface Builder, and when I run my program they don't look right.
Cocoa doesn't support overlapping views. If a view belongs "on top of" another, then it should probably be a sub-view.
This particular problem is likely to crop-up more often if you are coming from a Windows background, because dialog boxes on Windows do not have any hierarchical structure (there isn't even an owner/child relationship between controls within group boxes).
(BTW, there is an NSWindow method, -useOptimizedDrawing:, the documentation for which seems to imply support for overlapping views. Nevertheless, you probably shouldn't use it---it may not work in all cases, and is likely to make your program brittle in the face of any changes made to the frameworks. Additionally, you will almost certainly find that your program runs slower if you do fiddle with this setting.)
* 3.2.5 How do I draw <x>?
To draw rectangles quickly, look at NSRectFill(), NSRectFillList(), NSRectFillUsingOperation() et al.
For general drawing, use NSBezierPath; in addition to the instance methods, there are a few convenience methods for stroking or filling a single line or rectangle (see e.g. +strokeLineFromPoint:toPoint:, +strokeRect:). To draw more complicated shapes, use the -curveToPoint:controlPoint1:controlPoint2, -lineToPoint:, -moveToPoint:, -appendBezierPath: and -appendBezierPathWith* methods.
To draw images, use the methods on NSImage or on the various NS*ImageRep classes. If you just want to copy pixels from one place to another in your view, you can use NSCopyBits(), rather than having to use NSBitmapImageRep.
If you want to draw text, there are a couple of convenience methods on NSString and NSAttributedString that will work for small amounts of text. For larger amounts of text, either use an NSTextView (you can make it a subview of the view you're rendering in, set its properties as appropriate and -display it... BTW there's nothing to stop you reusing the same NSTextView to render several pieces of text in your -drawRect: routine, you can just move it, change its properties and -display it again). Alternatively, you can use NSLayoutManager, NSTextStorage and NSTextContainer, without an NSTextView (hint:take a look at NSLayoutManager's -drawGlyphsForGlyphRange:atPoint: method).
An alternative (non-Cocoa) way to render text is to use ATSUI (Apple Type Services for Unicode Imaging). You might need to be a little careful when passing coordinates to ATSUI, because it expects fixed-point coordinates, whereas Cocoa uses floating-point values; this could cause trouble if your coordinates get very large for any reason, and it also means that you need to remember to convert them, using FixedToFloat() and FloatToFixed(). However, the trouble of converting coordinates is certainly worthwhile---ATSUI provides access to a large number of advanced typographic features. If you want to render text in Mac OS X, it's certainly worth a look.
If you can't find what you want in the Cocoa framework, it is also possible to use Core Graphics functions directly from your Cocoa application; to draw into a Cocoa NSView or NSImage with CoreGraphics, you perform the following steps:
1. Lock focus on the view or image.
2. Obtain a CGContextRef for the Core Graphics context corresponding to
your view; you can do this using
(CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]
3. Use Core Graphics functions as desired.
4. Unlock focus from your view (or image).
You can also create your own Core Graphics context and render directly into that, although doing so is outside of the scope of this FAQ.
If you are interested in porting your program to GNUStep, or to other platforms' implementations of the OpenStep or Cocoa frameworks, then remember that Core Graphics and ATSUI are not part of either OpenStep or Cocoa, so any functionality derived this way will need to be rewritten.
* 3.2.6 I looked at NSBezierPath, and I can't see how to draw an elliptic arc.
NSBezierPath has some instance methods that can draw circular arcs, and one that can draw an axis-aligned oval, but none that will draw an elliptic arc.
However, NSBezierPath's contents may be transformed, independently of the view's co-ordinate transform, with the -transformUsingAffineTransform: method, which will allow you to achieve your goal.
* 3.2.7 How do I draw a gradient fill?
If you're using Jaguar (Mac OS X 10.2) or later, you should use Core Graphics CGShading functions (provided they can achieve the effect you want, of course). One major advantage with using them is that if you are rendering PDF output, they will turn into a PDF gradient fill, rather than a million rectangles, which makes for significant savings in file size.
If you need to support pre-10.2 versions of Mac OS X, then your best bet is probably NSRectFillListWithColors() or
NSRectFillListWithColorsUsingOperation(). You can use NSDivideRect() to compute the rectangles you need.
* 3.2.8 I applied a transform to my view, but now my lines come-out all distorted.
Yes. Line widths, patterns and other attributes are geometric rather than cosmetic under Quartz. That is to say, the view transform applies to them just as it applies to the geometry you have specified... so if you stretch the X axis by a factor of two (for example), vertical lines will be twice as thick as horizontal ones.
If you don't want a transform to apply to the attributes, then apply it to your geometry rather than to your view.
* 3.2.9 My image is upside-down when I render it into my view.
Either your view is flipped and your image isn't, or your image is and your view isn't. In any case, use NSImage's -setFlipped: to solve the problem.
* 3.2.10 I want my window to be completely transparent, outside of the area that I'm drawing, but when I set the window alpha, I still get Apple's pinstripes showing behind my view.
You need to subclass NSWindow and override initWithContentRect to do the following:
if ((self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered])) {
[self setBackgroundColor: [NSColor clearColor]];
[self setOpaque:NO];
[self setHasShadow:YES];
}
The first line removes the window gadgets, which you won't want if you're doing a funny-shaped window. The subsequent lines remove the pinstripes, make the window transparent and enable the shadow (a side-effect being that the shadow region is re-computed).
* 3.2.11 I moved my view, but the changes aren't visible on the screen.
Views are not windows. If you move one, add it to or remove it from the hierarchy, they don't redraw automatically; you need to tell Cocoa to redraw whichever views are affected using -setNeedsDisplay:.
* 3.2.12 My drawing code runs, but I don't see the results immediately.
Most likely you have forgotten to do a -setNeedsDisplay:. If you have called -setNeedsDisplay: or display: and the drawing still doesn't appear when you want, consider calling NSGraphicsContext's -flushGraphics, which will cause NSGraphicsContext to flush all of its buffered operations to the screen.
* 3.2.13 How can I draw into an NSImage?
Use NSImage's -lockFocus and -unlockFocus methods, just as you would with an NSView.
* 3.2.14 How do I draw arrows on the ends of an NSBezierPath?
Unlike some other operating systems, Mac OS X doesn't provide built-in support for drawing arrowheads on the ends of paths. However, all is not lost; there are at least two approaches that will work:
(a) Find the tangent to the end of the path, and use it to rotate another path to the correct angle. This works well for straight lines, but is sub-optimal for curves.
Or
(b) (Harder) Chop the end off the path, rotate it clockwise and anticlockwise about the end, then join the paths together. This works well provided curvature of the path isn't too high near the endpoints.
You may have seen arrows drawn like this in diagrams produced with MetaPost.
The former approach is pretty easy to implement (hint: you need to use atan2(), and remember that the tangent to the endpoint of a Bezier curve is simply the line passing through the endpoint and the adjacent control point). An example of the latter approach may be found here:
http://www.alastairs-place.net/archives/000037.html
3.3 Distributed Objects
* 3.3.1 I wrote a program that uses Distributed Objects, but it doesn't seem to work if I run it as a Startup Item. What's going on?
Distributed Objects uses Mach ports to implement local inter-process communication. When you run your program as a Startup Item, the port it creates to use DO is in the root port name-space; however, when a user logs-in and runs the other part of your application, the application creates ports in a per-user name-space. If you are trying to communicate from the user half of the application to the Startup Item, you will be fine; if you are trying to use DO in the other direction, however, it will not work because the Startup Item process cannot find the port your application is vending.
3.4 Run-Loops & Event Handling
* 3.4.1 When a user clicks one part of my user-interface, my program may take several seconds to complete the required processing; this makes the beach-ball appear. What can I do to stop it?
There are a few different ways to approach this problem, depending on the type of processing you are doing. The simplest is usually just to move the long-running computation to another thread using NSThread's +detachNewThreadSelector:toTarget:withObject: method, then in order to provide status information to the GUI, have the other thread use either Distributed Objects, or NSObject's -performSelectorOnMainThread:withObject:waitUntilDone: method.
Another possibility is to use an NSTimer to run small amounts of processing periodically (this might be OK, for example, for processes that the user isn't waiting for... e.g. Background spell-checking).
One method that is sometimes suggested is to call NSRunLoop's -runUntilDate: method periodically during processing. This isn't ideal because some of the AppKit's processing isn't driven by the run-loop, whilst you are processing your application will behave slightly differently than normal. An alternative if you want to poll for events during a method is to use NSApplication or NSWindow's -nextEventMatchingMask:untilDate:inMode:dequeue: method, together with -sendEvent:, in a loop like so:
NSEvent *event;
while ((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate
dateWithTimeIntervalSinceNow:0.01] inMode:NSEventTrackingRunLoopMode dequeue:YES])) {
[NSApp sendEvent:event];
}
You may need to use a different run loop mode, depending on where you invoke this loop. Be aware that doing this is fraught with danger; it is quite possible (unless you take steps to prevent it) that the method you are performing your computation in will be re-entered during the processing of an event, which could cause anything from confusing behaviour through to outright crashes.
* 3.4.2 Is there a way to make the run-loop generate events when the status of a file-descriptor changes, like select()?
There are a couple of ways of doing this. You can use NSFileHandle's -initWithFileDescriptor: method to obtain an NSFileHandle corresponding to your descriptor, at which point there are a variety of methods on NSFileHandle you can use to perform operations in the background (including waiting for input). Unfortunately, you can't wait for an output notification (so you won't know if you are going to block when you write to the descriptor).
Core Foundation also provides a couple of mechanisms, in particular, CFReadStream, CFWriteStream and CFSockets. Strangely, the only CF*Stream function that will create a stream given file descriptors is CFStreamCreatePairWithSocket(), an unfortunate side-effect of which is that CF*Stream then tries to use send() and recv() rather than read() and write(), neither of which work with fds other than sockets. A workaround is to use CF*StreamCreateWithFile, passing-in an NSURL containing "file:/dev/fd/<number>". Obviously this is less than ideal, because it uses another two file descriptors.
3.5 Controls & Cells
* 3.5.1 I cannot find a standard Cocoa control that does what I want. Are there any third-party controls available?
Yes. The following is an undoubtedly incomplete list of third-party controls of which the author is currently aware:
Stick Software's Circular Slider
http://www.sticksoftware.com/software/CircularSlider.html
Omni Group's OmniAppKit (which contains several additional controls, including some very cool sticky palettes)
http://www.omnigroup.com/developer/sourcecode/
CHAppKit (two colour-related controls)
http://www.concepthouse.com/products/CHAppKit/
AMShapedButton and AMToolTipTableView
http://www.harmless.de/cocoa.html
Calendar and URLTextView controls
http://blackholemedia.com/code/
iTableView (NSTableView with stripes)
http://members.iinet.net.au/~tonyarnold/archives/000015.html
MiscKit (various additional controls)
http://www.misckit.com/index.html
MOKit (various)
http://mokit.sourceforge.net/index.html
UKDistributedView and UKPrefsPanel
http://www.zathras.de/programming/cocoa_stuff.php
WBSearchTextField, WBTimeControl and WBCalendarControl http://s.sudre.free.fr/Software/DevPotPourri.html
Jiiva's Application Builder Collection ($$$, several very nice controls) http://www.jiiva.com/abc/
DisclosableView (including Interface Builder palette)
Note that some of these controls may become obsolete as Apple adds equivalents to the AppKit framework itself.
* 3.5.2 How do I display text *and* an icon in an NSTableView, NSOutlineView or NSMatrix?
Create a subclass of NSCell that renders both text and an icon. The right way to implement it is such that the -setObjectValue: method on your new cell class accepts an object that contains both the text and the image; you can then implement -tableView:objectValue:forTableColumn:row: in the way Apple/NeXT intended you to.
Some people seem to use -tableView:objectValue:forTableColumn:row: as an opportunity to fool with the cell that the table view is about to render; this probably isn't such a good idea, because nowhere in the method does it indicate that the tableview is going to render that column/row immediately after the call. If you want to alter the cell, use the delegate method -tableView:willDisplayCell:forTableColumn:row:, which is guaranteed to be called immediately before display.
* 3.5.3 How do I constrain what people can type into my text fields?
If the NSFormatters available on Interface Builder's palettes don't already do what you need, then you need to create a custom NSFormatter. Editing isn't allowed to finish until -getObjectValue:forString:errorDescription: returns YES. For a more polished approach, implement NSFormatter's -isPartialStringValue: methods. If you have a copy of Cocoa Programming, take a look at Ch. 11 pp. 393--402 for a fairly complete example.
* 3.5.4 How do I put an NSProgressIndicator (or some other NSView) into a table view?
The basic idea is to add the view as a subview of the NSTableView in the delegate method -tableView:willDisplayCell:forTableColumn:row:.
Joar Wingfors has posted some excellent sample code at
http://www.stepwise.com/Articles/Technical/2003-12-20.01.html
that shows exactly how to do this, and includes not only an example of a progress indicator embedded in an NSTableView, but also some code that implements a rule editor (like the one in Mail.app) using an NSTableView to manage the rows.
3.6 Integrating with the System
* 3.6.1 How do I obtain the icon the Finder would use for a given type of file?
Use NSWorkspace's -iconForFileType: method, like so:
NSImage *tiffIcon = [[NSWorkspace sharedWorkspace] iconForFileType:@"tiff"];
Note that this method is quite slow, so although it may be fine for one-off use, heavy users may want to build an icon cache.
* 3.6.2 How do I launch the user's default <web-browser/whatever>?
Again, look at NSWorkspace. There are a variety of methods you can use to achieve whatever you are actually after doing, including:
-getInfoForFile:application:type:
-openFile:
-openFile:withApplication:
-openURL:
Alternatively, you can use Launch Services (see Technical Note TN2017).
* 3.6.3 How do I perform a privileged operation? (e.g. as "root" or an administrator?)
Cocoa applications shouldn't really be performing privileged operations directly, because there are too many ways that the flexibility of the Objective-Basic (Objective-C) language and the Cocoa frameworks can be exploited to subvert an application that gains superuser privileges.
Either have your program execute a setuid tool (preferably written in C by someone that understands how to write secure C programs), or, if password authentication is appropriate, use Authorization Services.
See
http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/index.html
and
http://developer.apple.com/documentation/Security/Reference/authorization_ref/index.html
* 3.6.4 How do I get that password box to appear?
The password box is displayed automatically by the security server in response to requests made by applications through Authorization Services.
See 3.6.3 for more information.
3.7 Miscellaneous
* 3.7.1 What is NSNull and why am I getting one?
NSNull is used when placing null values into collection objects, as the Cocoa collection objects do not allow you to store "nil" as a value. It is quite a common requirement to check that an object pointer is not nil; to do this and take account of the possibility of an NSNull, the test should look like this:
if (!object || object == [NSNull null])
Note that there is only ever a single instance of NSNull in the system.
* 3.7.2 How do I do file operations? Should I use NSFileWrapper?
No. NSFileWrapper brings the entire file or directory into memory, which is not normally what you want to do. Instead, use NSFileManager, NSFileHandle or the C runtime or BSD functions. Remember that if you are using the C or BSD functions, you should convert file and folder names contained in NSStrings using the -fileSystemRepresentation method and not the -cString or -UTF8String methods.
* 3.7.3 How do I do networking? Should I use NSSocketPort or NSPort?
No. The NSPort classes are for Distributed Objects. Either use the BSD sockets functions (which are documented in "man 2 socket" as well as in any good book on TCP/IP), or use Core Foundation's CFSocket and/or CFNetwork services (see
http://developer.apple.com/documentation/CoreFoundation/Networking-date.html
for more information). If you choose to use the BSD functions, you may find it convenient to combine them with either NSFileHandle or CFStream... see question 3.4.2.
It is also worth saying that there are several third-party frameworks available that provide socket-based communication; see
http://cocoadev.com/index.pl?SocketClasses
for a list.
* 3.7.4 I created an object in Interface Builder, and its outlets don't seem to be connected when my program is running. I've double-checked, and they're definitely connected in IB. What's going on?
You have probably defined a method called "set<name of outlet>:", but that doesn't actually set the outlet member variable. The reason this causes trouble is that the objects in a nib file are archived, and the un-archiving process looks for accessor methods when trying to wire-up the outlets.
See the documentation for NSNibConnector's -establishConnection function, as well as the NSKeyValueCoding protocol.
* 3.7.5 How do I use a class that might not be available at run-time? (for example because it is only available on newer releases of Mac OS X)?
To use a class that may or may not be there, you can use the features of the Objective-Basic (Objective-C) run-ti