If I have an autoreleased object and I need to provide it to a different thread, what is the best way to do so?
Let's say I have an object that is autoreleased in thread 0. I tell thread 1 about this object and it retains it because it needs it. Later then it's done, it releases it. No problem. When thread 0 runs again and empties its autorelease pool, it sees the retain count is 1 and because it's an autoreleased object it deallocs. Everything is fine, therefore threads don't matter. Right?
By the way this was originally an interview question. The interviewer insisted that an autoreleased object cannot be given to another thread. He seemed almost angry about it. More and more in tech interviews, I encounter ppl who believe they know everything.
You should not pass autoreleased object directly to other thread.
in this code
id _sharedVariable; // ivar
NSConditionLock *_lock;
- (void)thread1
{
id objectNeedToPass = [[NSObject new] autorelease];
[_lock lock];
_sharedVariable = objectNeedToPass;
[_lock unlockWithCondition:1];
}
- (void)thread2
{
while (true)
{
[_lock lockWithCondition:1];
id objectReceived = [_sharedVariable retain];
[_lock unlockWithCondition:0]
process(objectReceived );
[objectReceived release];
}
}
thread2 may see _sharedVariable hold a released object (and crash)
because it may do this
thread 1 create and autorelease object
thread 1 assign it to the shared variable
thread 1 release the object
object deallocated
thread 2 read the object
thread 2 retain the object - crash
to solve the problem, you should pass a retained object
id _sharedVariable; // ivar
NSConditionLock *_lock;
- (void)thread1
{
id objectNeedToPass = [[NSObject new] autorelease];
[_lock lock];
_sharedVariable = [objectNeedToPass retain];
[_lock unlockWithCondition:1];
}
- (void)thread2
{
while (true)
{
[_lock lockWithCondition:1];
id objectReceived = _sharedVariable;
[_lock unlockWithCondition:0]
process(objectReceived );
[objectReceived release];
}
}
however, this may cause memory leak if second thread failed to release the object and make code hard to maintain (retain/release are hard to balance)
There is nothing to worry about at all as long as you are following the normal Cocoa memory management rules. Every single way of "providing it to a different thread" will work fine as long as you are following the rules.
Pretty much any time you "provide something to a different thread", it is asynchronous (unless you are using locks to do synchronous cross-thread execution or something). Which means that the other thread may (and will likely) use it after the current function on this thread has gone out of scope. Any time you store an object that needs to outlive the current execution, it needs to be retained. If you are storing it in an instance variable or global variable directly, then you are responsible for retaining it, according to the memory management rules. If you are storing it in some kind of container object, then that object is responsible for retaining it. So pretty much if you follow the rules, there is nothing to worry about.
Let's consider a common way that people execute things on another thread, with -performSelector:onThread:withObject:waitUntilDone:. If waitUntilDone is false, this function stores the receiver, selector, and argument in some kind of object to wait until the other thread is ready to execute it. Therefore, this function must be responsible for retaining the receiver and object when it places it into this structure, and releasing it when the structure is destroyed. And indeed it does -- if you read the pre-ARC documentation for the method, it says "This method retains the receiver and the arg parameter until after the selector is performed."
So basically the memory management rules are sufficient -- if you store the object in an instance variable, you need to retain it. If you pass it to some other function, then it's their job to take care of it.
Don't. Pass an owning reference to the other thread. The other thread will take ownership of the object and release it when done with it.
With autoreleased objects, you can't tell when the sending threads autorelease pool will be drained, and can't be sure if it will be drained before the receiving thread gets it.
Related
I have some event from C++ written library which works in background thread:
virtual void OnData(const char* data)
{
NSLog(#"Here 'data' string is present %s", data);
#autoreleasepool {
NSString* sData= [NSString stringWithCString:data encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Here _sometimes_ 'data'(%s) is nil (\0). But sData is always present %#", data, sData);
[callback OnData:sData];
});
};
}
And sometimes I have NULL(I suspect its garbage actually) in dispatch_async block in argument variable. But local NSString variable is always here. Why?
P.S. Do I actually must use #autoreleasepool in this situation?
You have no assurances about the lifespan of the buffer that const char *data was pointing to by the time the async block is performed. The data could be dangling pointer by that point (and should be assumed to be so). It's very dangerous to use C-style pointers in any asynchronous references or outside the context they were originally created.
You should either use memory managed objects (e.g. NSData, NSString, etc.) or, if you insist on using C-style pointers and need to reference this pointer in the asynchronous block, copy the data to your own buffer, use that buffer, and then free it when you're done using that buffer in your asynchronous routine. In this case, you have your sData, so just don't refer to data after that point, and you'll be fine.
P.S. You later ask whether you must use #autoreleasepool in this situation.
In short, in most cases, no additional autorelease pool is needed. Notably, when using Grand Central Dispatch (e.g. dispatch_async), it has its own autorelease pools, so you don't have to create one. And, when your main thread yield back to its run loop, again, it's pool is drained. In short, you only need manually created autorelease pools when instantiating your own NSThread objects.
Having said that, sometimes you will introduce autorelease pools if doing significant memory intensive operations prior to yielding back to the run loop. In that case, you'll add autorelease pools in order to reduce the peak memory usage of the app. But this would not appear to be one of those cases.
If you had something like this:
void CallOnData()
{
char *test = malloc(5 * sizeof(char));
strcpy(test, "test");
OnData(test);
free(test);
}
You should expect data to be "NULL" in the block.
And autorelease is not needed, assuming you're using ARC, which you should be.
My code grabs onto the current runloop and runs it continually (to allow UI interaction) while a much longer procedure occurs asynchronously.
This longer procedure may end up creating a lot of Objective-C objects and I believe even binding some of them into Core Foundation structures for usage with Core Foundation based API's. We use an autorelease pool around the loop body to clean up any objects from each execution of the loop body.
I'm running into a crash at the end of the autorelease pool, which means that some ARC object probably was probably bridged to a CF API that released the object. When the autorelease pool ends, it most likely tries to free that object.
However since it occurs at the end of the autorelease pool, I have no method of finding the culprit code. Are there known methods of debugging such a scenario in Objective-C on iOS?
for(NSString *runLoopMode in (__bridge NSArray *)allCurrentRunLoopModes)
{
#autoreleasepool {
CFRunLoopRunInMode((__bridge CFStringRef)runLoopMode,RUNLOOP_INTERVAL_SECONDS,false);
} // <- Crash occurs here, when autorelease pool releases objects.
}
For the most part with ARC (Automatic Reference Counting), we don't need to think about memory management at all with Objective-C objects. It is not permitted to create NSAutoreleasePools anymore, however there is a new syntax:
#autoreleasepool {
…
}
My question is, why would I ever need this when I'm not supposed to be manually releasing/autoreleasing ?
EDIT: To sum up what I got out of all the anwers and comments succinctly:
New Syntax:
#autoreleasepool { … } is new syntax for
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
More importantly:
ARC uses autorelease as well as release.
It needs an auto release pool in place to do so.
ARC doesn't create the auto release pool for you. However:
The main thread of every Cocoa app already has an autorelease pool in it.
There are two occasions when you might want to make use of #autoreleasepool:
When you are in a secondary thread and there is no auto release pool, you must make your own to prevent leaks, such as myRunLoop(…) { #autoreleasepool { … } return success; }.
When you wish to create a more local pool, as #mattjgalloway has shown in his answer.
ARC doesn't get rid of retains, releases and autoreleases, it just adds in the required ones for you. So there are still calls to retain, there are still calls to release, there are still calls to autorelease and there are still auto release pools.
One of the other changes they made with the new Clang 3.0 compiler and ARC is that they replaced NSAutoReleasePool with the #autoreleasepool compiler directive. NSAutoReleasePool was always a bit of a special "object" anyway and they made it so that the syntax of using one is not confused with an object so that it's generally a bit more simple.
So basically, you need #autoreleasepool because there are still auto release pools to worry about. You just don't need to worry about adding in autorelease calls.
An example of using an auto release pool:
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
#autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(#"number = %p", number);
}
}
}
}
A hugely contrived example, sure, but if you didn't have the #autoreleasepool inside the outer for-loop then you'd be releasing 100000000 objects later on rather than 10000 each time round the outer for-loop.
Update:
Also see this answer - https://stackoverflow.com/a/7950636/1068248 - for why #autoreleasepool is nothing to do with ARC.
Update:
I took a look into the internals of what's going on here and wrote it up on my blog. If you take a look there then you will see exactly what ARC is doing and how the new style #autoreleasepool and how it introduces a scope is used by the compiler to infer information about what retains, releases & autoreleases are required.
#autoreleasepool doesn't autorelease anything. It creates an autorelease pool, so that when the end of block is reached, any objects that were autoreleased by ARC while the block was active will be sent release messages. Apple's Advanced Memory Management Programming Guide explains it thus:
At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.
People often misunderstand ARC for some kind of garbage collection or the like. The truth is that, after some time people at Apple (thanks to llvm and clang projects) realized that Objective-C's memory administration (all the retains and releases, etc.) can be fully automatized at compile time. This is, just by reading the code, even before it is run! :)
In order to do so there is only one condition: We MUST follow the rules, otherwise the compiler would not be able to automate the process at compile time. So, to ensure that we never break the rules, we are not allowed to explicitly write release, retain, etc. Those calls are Automatically injected into our code by the compiler. Hence internally we still have autoreleases, retain, release, etc. It is just we don't need to write them anymore.
The A of ARC is automatic at compile time, which is much better than at run time like garbage collection.
We still have #autoreleasepool{...} because having it does not break any of the rules, we are free create/drain our pool anytime we need it :).
Autorelease pools are required for returning newly created objects from a method. E.g. consider this piece of code:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:#"Hello %#!", self.username];
}
The string created in the method will have a retain count of one. Now who shall balance that retain count with a release?
The method itself? Not possible, it has to return the created object, so it must not release it prior to returning.
The caller of the method? The caller does not expect to retrieve an object that needs releasing, the method name does not imply that a new object is created, it only says that an object is returned and this returned object may be a new one requiring a release but it may as well be an existing one that doesn't. What the method does return may even depend on some internal state, so the the caller cannot know if it has to release that object and it shouldn't have to care.
If the caller had to always release all returned object by convention, then every object not newly created would always have to be retained prior to returning it from a method and it would have to be released by the caller once it goes out of scope, unless it is returned again. This would be highly inefficient in many cases as one can completely avoid altering retain counts in many cases if the caller will not always release the returned object.
That's why there are autorelease pools, so the first method will in fact become
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:#"Hello %#!", self.username];
return [res autorelease];
}
Calling autorelease on an object adds it to the autorelease pool, but what does that really mean, adding an object to the autorelease pool? Well, it means telling your system "I want you to to release that object for me but at some later time, not now; it has a retain count that needs to be balanced by a release otherwise memory will leak but I cannot do that myself right now, as I need the object to stay alive beyond my current scope and my caller won't do it for me either, it has no knowledge that this needs to be done. So add it to your pool and once you clean up that pool, also clean up my object for me."
With ARC the compiler decides for you when to retain an object, when to release an object and when to add it to an autorelease pool but it still requires the presence of autorelease pools to be able to return newly created objects from methods without leaking memory. Apple has just made some nifty optimizations to the generated code which will sometimes eliminate autorelease pools during runtime. These optimizations require that both, the caller and the callee are using ARC (remember mixing ARC and non-ARC is legal and also officially supported) and if that is actually the case can only be known at runtime.
Consider this ARC Code:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
The code that the system generates, can either behave like the following code (that is the safe version that allows you to freely mix ARC and non-ARC code):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(Note the retain/release in the caller is just a defensive safety retain, it's not strictly required, the code would be perfectly correct without it)
Or it can behave like this code, in case that both are detected to use ARC at runtime:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
As you can see, Apple eliminates the atuorelease, thus also the delayed object release when the pool is destroyed, as well as the safety retain. To learn more about how that is possible and what's really going on behind the scenes, check out this blog post.
Now to the actual question: Why would one use #autoreleasepool?
For most developers, there's only one reason left today for using this construct in their code and that is to keep the memory footprint small where applicable. E.g. consider this loop:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Assume that every call to tempObjectForData may create a new TempObject that is returned autorelease. The for-loop will create one million of these temp objects which are all collected in the current autoreleasepool and only once that pool is destroyed, all the temp objects are destroyed as well. Until that happens, you have one million of these temp objects in memory.
If you write the code like this instead:
for (int i = 0; i < 1000000; i++) #autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Then a new pool is created every time the for-loop runs and is destroyed at the end of each loop iteration. That way at most one temp object is hanging around in memory at any time despite the loop running one million times.
In the past you often had to also manage autoreleasepools yourself when managing threads (e.g. using NSThread) as only the main thread automatically has an autorelease pool for a Cocoa/UIKit app. Yet this is pretty much legacy today as today you probably wouldn't use threads to begin with. You'd use GCD DispatchQueue's or NSOperationQueue's and these two both do manage a top level autorelease pool for you, created before running a block/task and destroyed once done with it.
It's because you still need to provide the compiler with hints about when it is safe for autoreleased objects to go out of scope.
Quoted from https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
Autorelease Pool Blocks and Threads
Each thread in a Cocoa application maintains its own stack of
autorelease pool blocks. If you are writing a Foundation-only program
or if you detach a thread, you need to create your own autorelease
pool block.
If your application or thread is long-lived and potentially generates
a lot of autoreleased objects, you should use autorelease pool blocks
(like AppKit and UIKit do on the main thread); otherwise, autoreleased
objects accumulate and your memory footprint grows. If your detached
thread does not make Cocoa calls, you do not need to use an
autorelease pool block.
Note: If you create secondary threads using the POSIX thread APIs
instead of NSThread, you cannot use Cocoa unless Cocoa is in
multithreading mode. Cocoa enters multithreading mode only after
detaching its first NSThread object. To use Cocoa on secondary POSIX
threads, your application must first detach at least one NSThread
object, which can immediately exit. You can test whether Cocoa is in
multithreading mode with the NSThread class method isMultiThreaded.
...
In Automatic Reference Counting, or ARC, the system uses the same
reference counting system as MRR, but it insertsthe appropriate memory
management method callsfor you at compile-time. You are strongly
encouraged to use ARC for new projects. If you use ARC, there is
typically no need to understand the underlying implementation
described in this document, although it may in some situations be
helpful. For more about ARC, see Transitioning to ARC Release Notes.
TL;DR
Why is #autoreleasepool still needed with ARC?
#autoreleasepool is used by Objective-C and Swift to work with autorelese inside
When you work with pure Swift and allocate Swift objects - ARC handles it
But if you decide call/use Foundation/Legacy Objective-C code(NSData, Data) which uses autorelese inside then #autoreleasepool in a rescue
//Swift
let imageData = try! Data(contentsOf: url)
//Data init uses Objective-C code with [NSData dataWithContentsOfURL] which uses `autorelese`
Long answer
MRC, ARC, GC
Manual Reference Counting(MRC) or Manual Retain-Release(MRR) as a developer you are responsible for counting references on objects manually
Automatic Reference Counting(ARC) was introduced in iOS v5.0 and OS X Mountain Lion with xCode v4.2
Garbage Collection(GC) was available for Mac OS and was deprecated in OS X Mountain Lion. Must Move to ARC
Reference count in MRC and ARC
//MRC
NSLog(#"Retain Count: %d", [variable retainCount]);
//ARC
NSLog(#"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));
Every object in heap has an integer value which indicates how many references are pointed out on it. When it equals to 0 object is deallocated by system
Allocating object
Working with Reference count
Deallocating object. deinit is called when retainCount == 0
MRC
A *a1 = [[A alloc] init]; //this A object retainCount = 1
A *a2 = a1;
[a2 retain]; //this A object retainCount = 2
// a1, a2 -> object in heap with retainCount
Correct way to release an object:
release If only this - dangling pointer. Because it still can point on the object in heap and it is possible to send a message
= nil If only this - memory leak. deinit will not be called
A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped
Working with Reference count(Object owner rules):
(0 -> 1) alloc, new, copy, mutableCopy
(+1) retain You are able to own an object as many times as you need(you can call retain several times)
(-1) release If you an owner you must release it. If you release more than retainCount it will be 0
(-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool
(-1) #autoreleasepool Forces process an autorelease pool at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing
autorelease is used in method calls when you allocate a new object there and return it
- (B *)foo {
B *b1 = [[B alloc] init]; //retainCount = 1
//fix - correct way - add it to fix wrong way
//[b1 autorelease];
//wrong way(without fix)
return b;
}
- (void)testFoo {
B *b2 = [a foo];
[b2 retain]; //retainCount = 2
//some logic
[b2 release]; //retainCount = 1
//Memory Leak
}
#autoreleasepool example
- (void)testFoo {
for(i=0; i<100; i++) {
B *b2 = [a foo];
//process b2
}
}
ARC
One of biggest advantage of ARC is that it automatically insert retain, release, autorelease under the hood in Compile Time and as developer you should not take care of it anymore
Enable/Disable ARC
//enable
-fobjc-arc
//disable
-fno-objc-arc
Variants from more to less priority
//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter)
//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)
//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)
Check if ARC is enabled/disabled
Preprocessor __has_feature function is used
__has_feature(objc_arc)
Compile time
// error if ARC is Off. Force to enable ARC
#if ! __has_feature(objc_arc)
#error Please enable ARC for this file
#endif
//or
// error if ARC is On. Force to disable ARC
#if __has_feature(objc_arc)
#error Please disable ARC for this file
#endif
Runtime
#if __has_feature(objc_arc)
// ARC is On
NSLog(#"ARC on");
#else
// ARC is Off
NSLog(#"ARC off");
#endif
Reverse engineering(for Objective-C)
//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments" | grep "_objc_release"
//result
0x00000001000080e0 748 _objc_release
//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong
Tool to Migrate Objective-C MRC to ARC
ARC generates errors where you should manually remove retain, release, autorelease and others issues
Edit -> Convert -> To Objective-C ARC...
New Xcode with MRC
If you enable MRC you get next errors(warnings)(but the build will be successful)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'
There seems to be a lot of confusion on this topic (and at least 80 people who probably are now confused about this and think they need to sprinkle #autoreleasepool around their code).
If a project (including its dependencies) exclusively uses ARC, then #autoreleasepool never needs to be used and will do nothing useful. ARC will handle releasing objects at the correct time. For example:
#interface Testing: NSObject
+ (void) test;
#end
#implementation Testing
- (void) dealloc { NSLog(#"dealloc"); }
+ (void) test
{
while(true) NSLog(#"p = %p", [Testing new]);
}
#end
displays:
p = 0x17696f80
dealloc
p = 0x17570a90
dealloc
Each Testing object is deallocated as soon as the value goes out of scope, without waiting for an autorelease pool to be exited. (The same thing happens with the NSNumber example; this just lets us observe the dealloc.) ARC does not use autorelease.
The reason #autoreleasepool is still allowed is for mixed ARC and non-ARC projects, which haven't yet completely transitioned to ARC.
If you call into non-ARC code, it may return an autoreleased object. In that case, the above loop would leak, since the current autorelease pool will never be exited. That's where you'd want to put an #autoreleasepool around the code block.
But if you've completely made the ARC transition, then forget about autoreleasepool.
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(self.downloadQueue, ^{
NSData *data = [self downloadFromRemoteService:service];
dispatch_async(self.storeQueue, ^{
int img = [self.imageStore addImage:data];
dispatch_saync(self.renderQueue, ^{
[self renderThumbnail:img];
dispatch_async(dispatch_get_main_queue(), ^{
[[self thumbnailViewForId:img] setNeedsDisplay:YES];
});
});
});
});
}
this is the code from Apple WWDC2012 《Asynchronous Design Patterns with Blocks, GCD, and》,'self' as strong reference in blocks, Is this code all right? or how to avoid leaks in this situation?
No, self will not leak. self will however retained until after the last block has been executed. When the last block finished, the block gets deallocated which in turn releases self. At that time, and only IFF there are no other strong references to self, it will be deallocated.
Edit:
I could not resist to mention this (because the sample is from Apple himself -- take it with a grain if salt ;) )
So, at the very top there is the method downloadFromRemoteService. It's glaringly obvious that this is a network request. Network requests are inherently _asynchronous_.
One attribute of an asynchronous operation is that this operation cannot be made "synchronous" in a truly manner anymore. Once asynchronous - always asynchronous.
What's also obvious from the code sample, that the network request is oddly enough synchronous, Ohps!
What happens when wrapping a asynchronous task into a synchronous wrapper? Well, its at least "suboptimal": the calling thread will be blocked immediately until the result is available, then just return the result. That's a quite big amount of waste for resources (threads are limited and are costly to create and require a quite amount of RAM).
So, this code has a "code smell". It's a "bad programming practice". We should make this better. ;)
Objects automaticaly retained when mentioned in block. They got released when block deallocated. So this code is all right. Problems occur when your's self-object takes ownership of such blocks with self inside.
So you just need to release block when you don't need it any more.
There is a retain cycle in this code, as self retains self.downloadQueue (and the other queues), which retains all the blocks dispatched to it, including the block here, which in turn retains self when it is copied (which happens when it is dispatched to the queue).
However, it is a temporary retain cycle, because once the block is executed on the queue, the queue will (hopefully) release it, breaking the cycle.
With reference to this answer, I am wondering is this correct?
#synchronized does not make any code "thread-safe"
As I tried to find any documentation or link to support this statement, for no success.
Any comments and/or answers will be appreciated on this.
For better thread safety we can go for other tools, this is known to me.
#synchronized does make code thread safe if it is used properly.
For example:
Lets say I have a class that accesses a non thread safe database. I don't want to read and write to the database at the same time as this will likely result in a crash.
So lets say I have two methods. storeData: and readData on a singleton class called LocalStore.
- (void)storeData:(NSData *)data
{
[self writeDataToDisk:data];
}
- (NSData *)readData
{
return [self readDataFromDisk];
}
Now If I were to dispatch each of these methods onto their own thread like so:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LocalStore sharedStore] storeData:data];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LocalStore sharedStore] readData];
});
Chances are we would get a crash. However if we change our storeData and readData methods to use #synchronized
- (void)storeData:(NSData *)data
{
#synchronized(self) {
[self writeDataToDisk:data];
}
}
- (NSData *)readData
{
#synchronized(self) {
return [self readDataFromDisk];
}
}
Now this code would be thread safe. It is important to note that if I remove one of the #synchronized statements however the code would no longer be thread safe. Or if I were to synchronize different objects instead of self.
#synchronized creates a mutex lock on the object you are syncrhonizing. So in other words if any code wants to access code in a #synchronized(self) { } block it will have to get in line behind all previous code running within in that same block.
If we were to create different localStore objects, the #synchronized(self) would only lock down each object individually. Does that make sense?
Think of it like this. You have a whole bunch of people waiting in separate lines, each line is numbered 1-10. You can choose what line you want each person to wait in (by synchronizing on a per line basis), or if you don't use #synchronized you can jump straight to the front and skip all the lines. A person in line 1 doesn't have to wait for a person in line 2 to finish, but the person in line 1 does have to wait for everyone in front of them in their line to finish.
I think the essence of the question is:
is the proper use of synchronize able to solve any thread-safe
problem?
Technically yes, but in practice it's advisable to learn and use other tools.
I'll answer without assuming previous knowledge.
Correct code is code that conforms to its specification. A good specification defines
invariants constraining the state,
preconditions and postconditions describing the effects of the operations.
Thread-safe code is code that remains correct when executed by multiple threads. Thus,
No sequence of operations can violate the specification.1
Invariants and conditions will hold during multithread execution without requiring additional synchronization by the client2.
The high level takeaway point is: thread-safe requires that the specification holds true during multithread execution. To actually code this, we have to do just one thing: regulate the access to mutable shared state3. And there are three ways to do it:
Prevent the access.
Make the state immutable.
Synchronize the access.
The first two are simple. The third one requires preventing the following thread-safety problems:
liveness
deadlock: two threads block permanently waiting for each other to release a needed resource.
livelock: a thread is busy working but it's unable to make any progress.
starvation: a thread is perpetually denied access to resources it needs in order to make progress.
safe publication: both the reference and the state of the published object must be made visible to other threads at the same time.
race conditions A race condition is a defect where the output is dependent on the timing of uncontrollable events. In other words, a race condition happens when getting the right answer relies on lucky timing. Any compound operation can suffer a race condition, example: “check-then-act”, “put-if-absent”. An example problem would be if (counter) counter--;, and one of several solutions would be #synchronize(self){ if (counter) counter--;}.
To solve these problems we use tools like #synchronize, volatile, memory barriers, atomic operations, specific locks, queues, and synchronizers (semaphores, barriers).
And going back to the question:
is the proper use of #synchronize able to solve any thread-safe
problem?
Technically yes, because any tool mentioned above can be emulated with #synchronize. But it would result in poor performance and increase the chance of liveness related problems. Instead, you need to use the appropriate tool for each situation. Example:
counter++; // wrong, compound operation (fetch,++,set)
#synchronize(self){ counter++; } // correct but slow, thread contention
OSAtomicIncrement32(&count); // correct and fast, lockless atomic hw op
In the case of the linked question you could indeed use #synchronize, or a GCD read-write lock, or create a collection with lock stripping, or whatever the situation calls for. The right answer depend on the usage pattern. Any way you do it, you should document in your class what thread-safe guarantees are you offering.
1 That is, see the object on an invalid state or violate the pre/post conditions.
2 For example, if thread A iterates a collection X, and thread B removes an element, execution crashes. This is non thread-safe because the client will have to synchronize on the intrinsic lock of X (synchronize(X)) to have exclusive access. However, if the iterator returns a copy of the collection, the collection becomes thread-safe.
3 Immutable shared state, or mutable non shared objects are always thread-safe.
Generally, #synchronized guarantees thread safety, but only when used correctly. It is also safe to acquire the lock recursively, albeit with limitations I detail in my answer here.
There are several common ways to use #synchronized wrong. These are the most common:
Using #synchronized to ensure atomic object creation.
- (NSObject *)foo {
#synchronized(_foo) {
if (!_foo) {
_foo = [[NSObject alloc] init];
}
return _foo;
}
}
Because _foo will be nil when the lock is first acquired, no locking will occur and multiple threads can potentially create their own _foo before the first completes.
Using #synchronized to lock on a new object each time.
- (void)foo {
#synchronized([[NSObject alloc] init]) {
[self bar];
}
}
I've seen this code quite a bit, as well as the C# equivalent lock(new object()) {..}. Since it attempts to lock on a new object each time, it will always be allowed into the critical section of code. This is not some kind of code magic. It does absolutely nothing to ensure thread safety.
Lastly, locking on self.
- (void)foo {
#synchronized(self) {
[self bar];
}
}
While not by itself a problem, if your code uses any external code or is itself a library, it can be an issue. While internally the object is known as self, it externally has a variable name. If the external code calls #synchronized(_yourObject) {...} and you call #synchronized(self) {...}, you may find yourself in deadlock. It is best to create an internal object to lock upon that is not exposed outside of your object. Adding _lockObject = [[NSObject alloc] init]; inside your init function is cheap, easy, and safe.
EDIT:
I still get asked questions about this post, so here is an example of why it is a bad idea to use #synchronized(self) in practice.
#interface Foo : NSObject
- (void)doSomething;
#end
#implementation Foo
- (void)doSomething {
sleep(1);
#synchronized(self) {
NSLog(#"Critical Section.");
}
}
// Elsewhere in your code
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Foo *foo = [[Foo alloc] init];
NSObject *lock = [[NSObject alloc] init];
dispatch_async(queue, ^{
for (int i=0; i<100; i++) {
#synchronized(lock) {
[foo doSomething];
}
NSLog(#"Background pass %d complete.", i);
}
});
for (int i=0; i<100; i++) {
#synchronized(foo) {
#synchronized(lock) {
[foo doSomething];
}
}
NSLog(#"Foreground pass %d complete.", i);
}
It should be obvious to see why this happens. Locking on foo and lock are called in different orders on the foreground VS background threads. It's easy to say that this is bad practice, but if Foo is a library, the user is unlikely to know that the code contains a lock.
#synchronized alone doesn't make code thread safe but it is one of the tools used in writing thread safe code.
With multi-threaded programs, it's often the case of a complex structure that you want to be maintained in a consistent state and you want only one thread to have access at a time. The common pattern is to use a mutex to protect a critical section of code where the structure is accessed and/or modified.
#synchronized is thread safe mechanism. Piece of code written inside this function becomes the part of critical section, to which only one thread can execute at a time.
#synchronize applies the lock implicitly whereas NSLock applies it explicitly.
It only assures the thread safety, not guarantees that. What I mean is you hire an expert driver for you car, still it doesn't guarantees car wont meet an accident. However probability remains the slightest.
It's companion in GCD(grand central dispatch) is dispatch_once. dispatch_once does the same work as to #synchronized.
The #synchronized directive is a convenient way to create mutex locks on the fly in Objective-C code.
side-effects of mutex locks:
deadlocks
starvation
Thread safety will depend on usage of #synchronized block.