Thread Safety Summary

This appendix describes advanced thread safety for some of the key frameworks in OS X and iOS. Information in this appendix is subject to change.

Cocoa

The guidelines for using Cocoa from multiple threads include the following:

  • Immutable objects are usually thread-safe. Once they are created, you can safely pass these objects to and from the thread. Mutable objects, on the other hand, are generally not thread-safe. To use mutable objects in threaded applications, the application must be properly synchronized. For more information, see Mutable Versus Immutable.
  • Many objects that are considered “thread-unsafe” are simply unsafe when used in multiple threads. Many of these objects can be used in any thread, as long as it is only one thread at a time. Objects specifically limited to the main thread of the application will be called out.
  • The main thread of the application handles events. Although Application Kit can continue to work if other threads are involved in the event path, the operations may be out of order.
  • If you want to use threads to draw patterns into a view, enclose all drawing code inNSViewlockFocusIfCanDrawunlockFocusBetween methods.
  • To use POSIX threads with Cocoa, you must first place Cocoa in multithreaded mode. For more information, see Using POSIX threads in Cocoa Applications.

Foundation framework is thread-safe

There is a misconception that the Foundation framework is thread-safe, whereas the Application Kit framework is not. Unfortunately, this is a rough generalization and somewhat misleading. Every framework has thread-safe areas and non-thread-safe areas. The following sections describe the general thread safety of the Foundation framework.

Thread-safe classes and functions

The following classes and functions are generally considered thread-safe. You can use the same instance from multiple threads without first obtaining the lock.

NSArray                                 NSAssertionHandler
NSAttributedString                      NSBundle
NSCalendar                              NSCalendarDate
NSCharacterSet                          NSConditionLock
NSConnection                            NSData
NSDate                                  NSDateFormatter
NSDecimal functions                     NSDecimalNumber
NSDecimalNumberHandler                  NSDeserializer
NSDictionary                            NSDistantObject
NSDistributedLock                       NSDistributedNotificationCenter
NSException                             NSFileManager
NSFormatter                             NSHost
NSJSONSerialization                     NSLock
NSLog/NSLogv                            NSMethodSignature
NSNotification                          NSNotificationCenter
NSNumber                                NSNumberFormatter
NSObject                                NSOrderedSet
NSPortCoder                             NSPortMessage
NSPortNameServer                        NSProgress
NSProtocolChecker                       NSProxy
NSRecursiveLock                         NSSet
NSString                                NSThread
NSTimer                                 NSTimeZone
NSUserDefaults                          NSValue
NSXMLParser
Object allocation and retain count functions
Zone and memory functions
Copy the code

Thread-unsafe classes

The following classes and functions are generally not thread-safe. In most cases, you can use these classes from any thread as long as you only use one thread at a time. See the course documentation for more details.

NSArchiver                           NSAutoreleasePool
NSCoder                              NSCountedSet
NSEnumerator                         NSFileHandle
NSHashTable functions                NSInvocation
NSMapTable functions                 NSMutableArray
NSMutableAttributedString            NSMutableCharacterSet
NSMutableData                        NSMutableDictionary
NSMutableOrderedSet                  NSMutableSet
NSMutableString                      NSNotificationQueue
NSPipe                               NSPort
NSProcessInfo                        NSRunLoop
NSScanner                            NSSerializer
NSTask                               NSUnarchiver
NSUndoManager
User name and home directory functions
Copy the code

Note that although the NSArchiver, NSCoder, and NSEnumerator objects are themselves thread-safe, they are listed here because it is not safe to change the data objects wrapped by them while using them. For example, it is not safe for an archiver to change the object graph that is being archived. For an enumerator, it is not safe for any thread to change the enumeration collection.

Main thread is class only

You must use the following classes only from the main thread of your application.

NSAppleScript
Copy the code

Mutable versus immutable

Immutable objects are usually thread-safe runs; Once they are created, these objects can be safely passed to and from the thread. Of course, when working with immutable objects, you still need to remember to use reference counting correctly. If you improperly release an object that you do not retain, it may cause an exception later.

Mutable objects are generally not thread-safe. To use mutable objects in threaded applications, the application must synchronize access to them using locks. See Atomic Operations for more information. In general, collection classes (e.g., NSMutableArray, NSMutableDictionary) are not thread-safe when it comes to mutations. That is, if one or more threads are changing the same array, there may be a problem. You must lock the location where the read and write occurs to ensure thread-safety.

Even if a method claims to return an immutable object, you should not simply assume that the returned object is immutable. Depending on the method implementation, the object returned may be mutable or immutable. For example, a method that returns type NSString might actually return NSMutableString due to its implementation. If you want to ensure that an object you own is immutable, you should make an immutable copy.

reentrant

Reentrant can occur only if the operation “calls out” to other operations on the same object or different objects. Retaining and releasing objects is a “calling” that is sometimes overlooked.

The following table lists the parts of the Foundation framework that can be explicitly reentrant. All other classes may or may not be reentrant, or they may be reentrant in the future. A complete analysis of reentrant has never been performed, and this list may not be exhaustive.

Distributed Objects       NSConditionLock
NSDistributedLock         NSLock
NSLog/NSLogv              NSNotificationCenter
NSRecursiveLock           NSRunLoop
NSUserDefaults
Copy the code

Class initialization

The objectiveC runtime system sends an Initialize message to each class object before the class receives any other messages. This gives the class a chance to set up its runtime environment before use. In multithreaded applications, the runtime guarantees that only one thread — which happens to send the first message to the class’s thread — executes the Initialize method. If the second thread tries to send a message to the class while the first thread is still in the Initialize method, the second thread blocks until the Initialize method completes execution. Meanwhile, the first thread can continue to call other methods on the class. The Initialize method should not rely on the second thread calling the class’s methods; If so, the two threads are stuck.

Due to an error in OS X version 10.1. X and earlier, a thread can send a message to the class before another thread completes executing the initialize method of the class. The thread can then access values that have not been fully initialized, potentially crashing the application. If you encounter this problem, you need to introduce locks to prevent values from being accessed after initialization, or force the class to initialize itself before it becomes multithreaded.

Automatic release tank

Each thread maintains its own stack of NSAutoreleasePool objects. Cocoa expects that there will always be an automatic release pool on the stack of the current thread. If the pool is unavailable, objects are not released and memory leaks result. NSAutoreleasePool objects are automatically created and destroyed in the main thread of an Application Kit-based Application, but before Cocoa can be used, worker threads (and foundation-only applications) must create their own objects. If your thread is long and likely to generate a large number of auto-release objects, you should periodically destroy and create auto-release pools (like the Application Kit on the main thread); Otherwise, automatically freed objects accumulate, and your memory footprint increases. If your detached threads do not use Cocoa, there is no need to create an automatic release pool.

Run Loops

Each thread has one and only one run loop. However, each run loop has its own set of input patterns for each thread to determine which input sources are being listened on while the loop is running. An input pattern defined in one run loop does not affect an input pattern defined in another run loop, even though they may have the same name. If your Application is based on Application Kit, the run loop for the main thread is run automatically, but the worker threads (and foundation-only applications) must run the run loop themselves. If the detached thread does not enter the run loop, the thread exits once the detached method completes execution.

Although there are some exceptions, the NSRunLoop class is not thread-safe. You should only call instance methods of this class from the thread that owns it.

Application Kit Framework thread safety

The following sections describe the General thread-safety of the Application Kit framework.

Thread-unsafe classes

The following classes and functions are generally not thread-safe. In most cases, you can use these classes from any thread as long as you only use one thread at a time. See the course documentation for more details.

  • NSGraphicsContext. For more information, seeNSGraphicsContext limit.
  • NSImage. For more information, seeNSImage limit.
  • NSResponder.
  • NSWindowAnd all their descendants. For more information, seeNSView Restrictions.

Main thread is class only

You must use the following classes only from the main thread of your application.

  • NSCell and all its descendants.
  • NSView and all of its descendants. See NSView Restrictions for more information.

Windows limits

You can create a window on a worker thread. Application Kit ensures that data structures associated with the window are freed on the main thread to avoid race conditions. Window objects can leak in applications that process a large number of Windows at once.

You can create modal Windows on worker threads. The application toolkit blocks calls to worker threads while the main thread runs a modal loop.

Event handling restrictions

The main thread of the application handles events. The main thread is the thread that is blocked in the RUN method of the NSApplication and is usually called in the main function of the application. If other threads are involved in the event path, The Application Kit will continue to work, but the actions may occur out of order. For example, if two different threads are responding to key events, keys may be received out of order. By having the main thread handle events, you get a more consistent user experience. Once received, the event can be dispatched to a worker thread for further processing if desired.

You can call the postEvent:atStart: method of NSApplication from the worker thread to publish events to the main thread’s event queue. However, the order of user input events is not guaranteed. The main thread of the application is still responsible for handling events in the event queue.

Drawing limits

Application Kit is generally thread-safe when drawing using graph functions and classes, including the NSBezierPath and NSString classes. The following sections describe the details of using specific classes. For more information on drawings and threads, see Cocoa Drawing Guide.

NSView limit

NSView classes are generally not thread-safe. You should create, destroy, resize, move, and perform other operations on NSView objects only from the main thread of your application. Drawing from a worker thread is thread-safe as long as you draw the drawing call by calling lockFocusIfCanDraw and unlockFocus. If an application’s helper thread wants to override part of the view on the main thread, it cannot do so using the display, setNeedsDisplay:, setNeedsDisplayInRect:, or setViewsNeedDisplay: methods. On the contrary, it should be the main thread to send messages or use performSelectorOnMainThread: withObject: waitUntilDone: method calls these methods. The graphical state (GStates) of the view system is per thread. Using graph state was once a way to achieve better drawing performance on single-threaded applications, but it is no longer appropriate. Incorrect use of graph state can actually cause the drawing code to be less efficient than drawing in the main thread.

NSGraphicsContext limit

The NSGraphicsContext class represents the drawing context provided by the underlying graphics system. Each NSGraphicsContext instance has its own independent graph state: frame, clipping, current font, and so on. An instance of this class is automatically created on the main thread of each NSWindow instance. If any drawing is performed from a worker thread, a new NSGraphicsContext instance is created specifically for that thread. If any drawing is performed from a worker thread, the drawing call must be refreshed manually. Cocoa does not automatically update the view with content drawn from a worker thread, so the flushGraphics method of NSGraphicsContext is called when the drawing is complete. If your application only extracts content from the main thread, there is no need to refresh the drawing call.

NSImage limit

A thread can create an NSImage object, draw it into the image buffer, and pass it to the main thread for drawing. The underlying image is cached and shared between all threads. For more information on how images and caching work, see the Cocoa Drawing Guide.

The Cocoa framework of Data

Core data frameworks generally support threading, although there are some caveats that apply. For information on these warnings, see “Core Data Concurrency” in the Core Data Programming Guide.

Core Foundation

Core Foundation is thread-safe enough that if you program carefully, you shouldn’t have any problems with competing threads. It is thread-safe in common situations, such as when querying, reserving, releasing, and passing immutable objects. Even central shared objects that may be queried from multiple threads are reliably thread-safe.

Like Cocoa, Core Foundation is not thread-safe with respect to mutations in objects or their content. For example, as you might expect, modifying mutable data or mutable array objects is not thread-safe, but neither is modifying objects within an immutable array. One reason is performance, which is critical in these situations. Moreover, it is often not possible to achieve absolute security at this level. For example, you cannot rule out uncertain behavior resulting from retaining objects retrieved from a collection. The collection itself may be released before calling the reserved contained object.

In cases where Core Foundation objects are accessed from multiple threads and changes are made, your code should prevent simultaneous access by using locks at the access point. For example, code that enumerates Core Foundation array objects should use the corresponding lock calls around the enumeration block to prevent others from altering the array.