First of all let me quote a chapter from Apple Threading Programming Guide:
Be Aware of Threats to Code Correctness
When using locks and memory barriers, you should always give careful thought to their
placement in your code. Even locks that seem well placed can actually
lull you into a false sense of security. The following series of
examples attempt to illustrate this problem by pointing out the flaws
in seemingly innocuous code. The basic premise is that you have a
mutable array containing a set of immutable objects. Suppose you want
to invoke a method of the first object in the array. You might do so
using the following code:
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[arrayLock unlock];
[anObject doSomething];
Because the array is mutable, the lock around the array prevents other
threads from modifying the array until you get the desired object. And
because the object you retrieve is itself immutable, a lock is not
needed around the call to the doSomething method.
There is a problem with the preceding example, though. What happens if
you release the lock and another thread comes in and removes all
objects from the array before you have a chance to execute the
doSomething method? In an application without garbage collection, the
object your code is holding could be released, leaving anObject
pointing to an invalid memory address. To fix the problem, you might
decide to simply rearrange your existing code and release the lock
after your call to doSomething, as shown here:
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[anObject doSomething];
[arrayLock unlock];
By moving the doSomething call inside the lock, your code guarantees
that the object is still valid when the method is called.
Unfortunately, if the doSomething method takes a long time to execute,
this could cause your code to hold the lock for a long time, which
could create a performance bottleneck.
The problem with the code is not that the critical region was poorly
defined, but that the actual problem was not understood. The real
problem is a memory management issue that is triggered only by the
presence of other threads. Because it can be released by another
thread, a better solution would be to retain anObject before releasing
the lock. This solution addresses the real problem of the object being
released and does so without introducing a potential performance
penalty.
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[anObject retain];
[arrayLock unlock];
[anObject doSomething];
[anObject release];
And the question is: Is there any way to solve the problem while using ARC?
ARC solves this problem for you automatically; by default every pointer is a strong pointer, which means that the object is guaranteed to be retained until you are done using that pointer.
This means that whenever you get an object out of an array, ARC always retains that object. This guarantees its lifetime, even if the object is later removed from the array.
Related
Let's assume that we create an instance of class var foo: Foo? = Foo() on the main thread and we call some time consuming instance method bar of Foo on another thread, after a short time we set foo to nil on main thread. What happens with the execution of bar, in my understanding bar should still continue its execution since invoking instance method implicitly passes self as the first argument, so even those the last explicit ref to foo was broken we still have a ref inside of a method and it should be good. But then I found this stackoverflow post which completely breaks my understanding. So, can somebody confirm/deny the fact that object cannot be deallocated during its method execution
Short answer is that your belief is correct, and you're looking at a question that's not relevant to Swift.
Your link is to non-ARC Objective-C. Swift always uses ARC, and ARC is very conservative about retains. Consider the following Objective-C call:
[target runMethod: parameter];
Under ARC, this is (conceptually) transformed into:
[target retain];
[parameter retain];
[target runMethod: parameter];
[parameter release];
[target release];
retain and release are atomic, thread-safe calls.
A similar behavior exists in Swift. Because of this, as a general rule (in the absence of Unsafe), a variable cannot "disappear" while you'll holding onto it.
This is the implementation detail. The better way to think about it is that by default variables and parameters are strong, and an object cannot be destroyed while there is a strong reference. This matches your understanding.
Prior to ARC, though, you needed to insert extra retains and releases yourself to protect against this kind of situation, and it was very common not to. (Prior to 10.6, most ObjC was single-threaded.)
Even without threads, there are ways this can go astray without ARC. Since callers often didn't immediately retain returned values if they only needed them temporarily, it was possible to get dangling pointers even without multiple threads. For example, with a trivial accessor with no memory management, this can crash:
NSString *name = [person name];
[person release];
[self doSomethingWithName: name];
This is why you often see old ObjC getters written in the form:
- (NSString*) title {
return [[title retain] autorelease];
}
This made sure that the returned value would survive until the end of the event loop even if self released it or self was deallocated.
Swift does similar things via ARC, but as the name suggests, it's all automatic.
I'm facing "Collection was mutated while being enumerated" crash in dispatch_async method. This is the code:
- (void) addCutObjectFromObject: (Object *) object {
dispatch_async(self.objectsQueue, ^{
ObjectCut *objectCut = [[[ObjectCut alloc] init] autorelease];
objectCut.objectId = object.uid;
objectCut.objectName = object.internal.name;
#synchronized(self.shownChannelsArray) {
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
}
});
}
Dispatch_queue is created in the init method like this:
_objectsQueue = dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_retain(_objectsQueue);
The code crashes without synchronized block. As far as I know, it should not crash, because there is no loop, and blocks should be added one-by-one waiting for their queue. Can someone explain me, why that is happening?
dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT) creates a concurrent queue. Arbitrarily many blocks dispatched to that queue may execute simultaneously. It's likely that containsObject: uses enumeration internally to do a linear search. Therefore one block is in the middle of containsObject: while another performs addObject:. NSMutableArrays are, like the other Foundation mutable objects, not guaranteed to be thread safe.
At the very least you want to use DISPATCH_QUEUE_SERIAL. Unless the order is really important to you or your objects don't implement hash, isEqual:, etc, you should probably also just use a set rather than sort of manually recreating one with an array.
Aren't you calling addCutObjectFromObject: from a loop or an enumeration ?
If It is, you may want to check this part.
Ah a tricky one.. from my knowledge (and I may be a bit off, so others, feel free to follow up, but this poor chap doesnt have any responses yet..
so when this executes, the async block will not run right away. It will first be scheduled, I would imagine you are calling this addCutObjectFromObject method more than once. Let's say for example sake that you are calling this method 5 times. Now think about threads. The first call gets scheduled, eventually begins running, while the second call gets scheduled, eventually starts running, etc.. and this is where the problem starts and I believe your culprit is in containsObject and addObject. Under the hood, containsObject is iterating through the array. Since there are more than 1 thread executing, while one thread is iterating the array, another could be calling addObject. Thus generating your crash of 'collection was mutated while being iterating'
Two options for solutions
1) in containsObject, check against a copy of the array rather than the array itself (this is a bad approach because it requires duplicating of the content of the array, BUT it is easy to put in, and for small array sizes, could be a quick fix)
if (![self.objectsArray containsObject: objectCut]) =>
if (![[NSArray arrayWithArray:self.objectsArray] containsObject: objectCut])
Again, this should stop your sporadic crashes, but this is a BAD SOLUTION (because of the memory duplication)
2) create a temporary array to add the objects to, and once all threads are finished, add the contents of the temporary array to self.objectsArray
The error says the reason of crash. It is not allowed to change an array while enumerating.
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
In the above code you were enumerating the array an inserting and object. This is where the error will be given. Hope this helps .. :)
Lately I am using enumerateKeysAndObjectsUsingBlock: and today I have hesitated when one of my colleague at work place pointed out that this enumeration method can be called from separate thread and my code may not work as expected. He even suggested I should use fast enumeration. Problem is I really like enumerateKeysAndObjectsUsingBlock: and dislike fast enumeration due to its nature when it comes to dictionary.
my method looked as following
- (NSArray *)someMethod
{
__block NSMutableArray *myArray = [NSMutableArray array];
[self.myDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[myArray addObject:obj];
}
return myArray;
}
Can I be sure that myArray will always return the expected values (assuming self.myDictionary is not empty) and it will be always called on the same thread as the someMethod?
I am aware there is a method enumerateKeysAndObjectsWithOptions:usingBlock: and calling it with NSEnumerationConcurrent option will run enumeration simultaneously on multiple threads.
But I can't find any documentation regarding enumerateKeysAndObjectsUsingBlock.
The same refers to enumerateObjectsUsingBlock: used on array.
As you observe, unless you call enumerateKeysAndObjectsWithOptions with the NSEnumerationConcurrent option, you can be assured that they won't be performed concurrently. As to whether the non-concurrent rendition of enumerateKeysAndObjects runs on the same thread or not (though, I'd wager it does), it doesn't really matter if it didn't run on the same queue because this is run synchronously with respect to the thread that you called the enumeration method. Bottom line, your coworker's thread-related concerns are without merit.
I would note, though, that in my anecdotal testing, these enumeration methods are slower than fast enumeration. If speed is of paramount interest (e.g. especially if using huge dictionaries/arrays), then you might want to employ fast enumeration. In most scenarios, though, the difference is not material.
I had developed an app and now I'm using Instruments to see the memory usage. I have a problem understanding the retain/release process of my object. This is what Instruments says:
The retain count increase when I add my object into an array, when I add it on my view and when I take off from the array.
So, when I use removeFromSuperview the object retainCount will never be zero, so the object don't release the memory.
EDIT 1:
I forgot to say i'm using ARC.
EDIT 2:
I describe exactly what happen :
I create the object together other objects in a class called NKLevelGenerator. Into it i alloc the NKIngredients and then i add all to a NSArray which will be returned. Here the retain count of every object is 2. In my NKLevelVC, my ViewController, i use this instruction :
[level addObjectsFromArray:[levelGenerator level1WithDelegate:self ciotola:ciotola bagliore:bagliore difficulty:NKDifficultyHard]];
Object level is a NSMutableArray that i alloc and init in viewDidLoad.
From here i call another method which performs this operations :
- (void)insertInArrayRandomly {
for (int i=0; i<[level count]; i++) {
[ingredienti insertObject:[level objectAtIndex:[[indexes objectAtIndex:i]integerValue]] atIndex:i];
}
}
Object ingredienti is another NSMutableArray that I alloc and init in viewDidLoad. indexes is an array of NSInteger which contains random indexes to extract NKIngredient object randomly.
Then i'm doing this :
NKIngredient *ing = [ingredienti objectAtIndex:index];
[[self view] insertSubview:ing belowSubview:navBar];
[ing animateIngredient];
[ingredienti removeObject:ing];
Before looking into Instruments have you tried the Static Analysis of your code? It may help for simple memory problems.
But the very first thing to check is: Did you follow the Golden Rule?
The Golden Rule being: For each alloc, copy or retain you must use one, and only one, releaseor autorelease.
This is the most important rule for memory management without ARC. So the fact that you object is retained by th array is none of your business, just remember what you have retained, allocated or copied and release it.
PS: Next time, your code would be much more helpful than the Instruments screenshot.
First: use instruments to see if effectively there's a memory leak, there's a tool made for this purpose and it tells you where you leak memory.
Second: it depend on how many object made [retain] on the view. If you add to an Array, it retain the view, but if you don't release it in the method that create the view, when you release the view from the array, the count will still be 1.
In most of the code that I've sen using autorelease, the object is ultimately returned from the function.Clearly release cannot be called after this point and autorelease is the way to go. However in situations where the object is going to passed to another object that will retain it is using autorelease just as valid?
For example
-(void)foo
{
SomeClass *someObject = [[[SomeClass alloc] init] autorelease];
//Do some things
self.someOtherClass.someProperty = someObject;
}
Is there any practical difference to releasing the object after it is assigned to someProperty:
-(void)foo
{
SomeClass *someObject = [[SomeClass alloc] init]];
//Do some things
self.someOtherClass.someProperty = someObject;
[someObject release];
}
Are there any situations where the later is more preferable to the former?
Both are acceptable, but you are accouraged to use the release version to avoid memory spikes an other problems.
It's acceptable to release here because you can assume that the receiver of the object will retain it, if it needs if later. So you can safely release as soon as it's given.
I think the latter will always perform slightly better in terms of memory usage and CPU usage, but only by a tiny amount per allocation.
Feel free to use the former if you prefer not to write three lines of code and are not having a performance problem. Note that the former can actually be written in one statement without a local variable at all.