I can't undarstand why my code is crashes:
<__NSCFSet: 0x74957b0> was mutated while being enumerated
Previously I read simular topics but their problem was that the code calls in different threads. My code always calls in Thread 1.
It crashes times to time.
Here is a code where it happens:
- (void)processReceivedResponse:(JTResponse *)aResponse {
NSParameterAssert(aResponse);
id <JTRequestDelegate> delegate = [self processResponseWithReceiver:aResponse];
if (delegate == nil) {
for (JTObserver *someObserver in observers) {
if (someObserver.requestType == aResponse.type &&
![someObserver.delegate isEqual:delegate]) {
[someObserver.delegate didReceiveResponse:aResponse];
}
}
}
}
The error you're getting is caused by something changing the "observers" set while you're looping through it.
It's hard to tell what that might be from just the snippet you posted. Is something in the delegate you're invoking on someObserver changing the "observers" set?
You could try simply copying the observers set and looping over the copy:
for (JTObserver *someObserver in [[observers copy] autorelease]) {
...
}
If you're using ARC (automatic reference counting), you don't need to use autorelease.
Related
I've written a Grand Central Dispatch like class that queues blocks to be executed on a pool of threads. (Why you wonder? Because I need a GCD with thread affinity).
The code is quite simple:
static let sharedInstance=CuprumOperationQueue()
func addOperation(operation: ()->()) {
dispatch_async(_operationsQueue) {
//Place the operation on the queue
self._operations.append(operation)
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
self.pushQueue()
}
}
}
func getOperation() -> ClosureType? {
var operation: ClosureType?=nil;
dispatch_sync(_operationsQueue) {
if (!self._operations.isEmpty) {
operation=self._operations.removeFirst();
}
}
return operation;
}
func pushQueue() {
for thread in _threads {
if (CFRunLoopIsWaiting(thread.runLoop)) {
if let operation = self.getOperation() {
CFRunLoopPerformBlock(thread.runLoop, kCFRunLoopDefaultMode, operation); //(2)
CFRunLoopWakeUp(thread.runLoop);
} else {
//There are no more operations to perform
break;
}
}
}
}
I use a GCD serial queue (_operationsQueue) to serialize access to the array with blocks (_operations). The threads (running a CFRunLoop with an observer) call pushQueue when the run loop is about to sleep.
I use the queue as follows:
[[CuprumOperationQueue sharedInstance] addOperation:^{ //(1)
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
NSLog(#"test %p",self);
}];
}];
The problem is that the object calling the above snippet is never released. It's not reported as a leak in Instruments, so there is at least some reachable reference to it. I just don't see where. I suspect a retain cycle somewhere, because removing the self reference from the NSLog statement does cause the object to be released. But this can't be the whole problem, as replacing the line with (1) for the equivalent using a NSOperationQueue also causes the object to be properly released (even with self reference in the second block). So it's only in combination with my custom queue that there is a problem.
If, in the Swift-based queue class, I remove the call on the line with (2), the object is also properly released.
As you can see there isn't much code left to be causing problems, but I really don't see where this might cause a retain cycle.
Any insights will be highly appreciated.
I would like to know if both of the following solutions for lazy initialization are correct.
I have a class AppContext that is supposed to hold references to other class that should only exist once (Avoiding making every single one of these classes a singleton). Let's say one of these other classes is called ReferencedClass. That being said, I would like to lazy-initialize the references with defaults, in a thread-safe way.
It has been discussed before, and I have read a lot about it, but I am still unsure. Personal preferences aside, what I would like know is: Are these two solutions a correct way to implemented my desired behavior?
Solution 1: Originally I wanted to implement it like this:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for lock and check again after locking.
if (_referencedClass == nil) {
#synchronized(self) {
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
}
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
#synchronized(self) {
_referencedClass = referencedClass;
}
}
Solution 2: Then I decided to go with GCD instead, so I wrote this:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for "lock" and check again after "locking".
if (_referencedClass == nil) {
dispatch_sync(syncDispatchQueue, ^{
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
});
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
dispatch_sync(syncDispatchQueue, ^{
_referencedClass = referencedClass;
});
}
Of course, somewhere (for example in the init-Method) I have initialized the syncDispatchQueue with something like:
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
Is this correct, thread-safe and deadlock-free code? Can I use the double-checked-locking together with the temp-variable? If this double-checked-locking is not safe, would my code in both cases be safe if I removed the outer checks? I guess so, right?
Thanks very much in advance!
[Side note: I am aware of dispatch_once and that some people say that (in contrary to the Apple documentation) it can also be used with instance variables. For now I would like to use one of these two options though. If possible. ]
As far as I understand it, your "double-checked locking" mechanism is not thread-safe,
because the assigment _referencedClass = ... is not atomic. So one thread might read a partially initialized variable in the outer if (_referencedClass == nil) check.
If you remove the outer checks, both versions look OK to me.
You may be interested in
What advantage(s) does dispatch_sync have over #synchronized?
which has a great answer explaining the differences in implementation and performance.
I recently changed a bit of code using Archiver to replace the object being decoded with another if the object was in a shared list. My app is using ARC.
- (id)awakeAfterUsingCoder:(NSCoder*)decoder {
Player* player = [[Team getCurrentTeam] getPlayerWithId: self.id];
// If the player is in the current team then use that instance instead of this one.
if (player != nil) {
return player;
} else {
return self;
}
}
The code worked great until I added the awakeAfterUsingCoder. Now I am getting malloc: error for object 0x7567430: pointer being freed was not allocated. It would appear that the object being replaced is being released but it was never retained? Since I'm using ARC I'm not sure what I might do to resolve this.
I have found some problems when saving NSManagedObjectContext inside NSOperation with turned on ARC. Without ARC everything was fine before. It is always gives EXC_BAD_ACCESS during saving. The code looks like this:
//on the main thread
-(void)someFunc
{
array = ... //fetching an array of entities from a core data
for(SomeEntity * obj in array)
{
NSSomeOperation * op = [[NSSomeOperation alloc] initWithValue:[obj someField]];
//start an operation
}
}
//NSSomeOperation implementation
//...
- (void)main {
//some code
NSError * error = nil;
[mainContext lock];
if (![mainContext save:&error]) { //<--- HERE EXC_BAD_ACCESS
//process error
}
[mainContext unlock];
//some code
}
//...
Using of [mainContext setRetainsRegisteredObjects:YES] and objectWithID don't resolve this issue.
EXC_BAD_ACCESS (code=1)
EXC_BAD_ACCESS (code=13)
-[__NSCFType contextDidSave:]: unrecognized selector sent to instance 0x7fc5c505d940
An observer of NSManagedObjectContextDidSaveNotification illegally threw an exception.
Objects saved = {
inserted = "{(\n)}";
updated = "{(\n <SomeEntity: 0x7fc5c55b6220> (entity: SomeEntity; id: 0x7fc5c5052b20 ... )}"; }
and exception = -[__NSCFType contextDidSave:]: unrecognized selector sent to instance 0x7fc5c505d940 with userInfo = (null)
I use a separate managed object context and fetch my managed objects inside this NSOperation.
Maybe it is something related to Core Data bugs or ARC? Maybe ARC cleans some of objects, that must be saved?
Because, without ARC everything was fine, all worked. When I turned on ARC - EXC_BAD_ACCESS.
Does anyone know why it occurs?
Maybe ARC deallocates some object that receives NSManagedObjectContextDidSaveNotification and this causes the exception?
I had something similar, and fixed it by making sure to removeObserver: before the object gets deallocated.
Note that the CoreData exception actually hides the notification center exception, so you don't get to see it.
I have a block of code which is similar to the following:
for (NSDictionary *tmp in aCollection) {
if ([[bar valueForKey:#"id"] isEqualToString:[tmp valueForKey:#"id"]])
{
break;
}
else
{
[aCollection addObject:bar];
}
}
Is this technically an exception in Objective-C 2.0? It appears you cannot mutate a collection with fast enumeration. This is the result of an error:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSCFArray: 0x396000> was mutated while being enumerated.'
What's the best way to solve this?
Well the way to solve it is not to mutate the array (e.g. add an object) while enumerating it :)
The problem here is that modifying the array by adding/removing elements could cause the enumeration values to become invalid, hence why it's a problem.
In your case The easiest way to solve this is fixing the bug in your code. Your code is doing the "else add" clause for every item in the array and I'm quite sure that's not what you want.
Try this;
bool found = false;
for (NSDictionary *tmp in aCollection)
{
if ([[bar valueForKey:#"id"] isEqualToString:[tmp valueForKey:#"id"]])
{
found = true;
break;
}
}
if (!found)
{
[aCollection addObject:bar];
}
Make a copy of the collection and iterate through that. Then you can swap out or add to your original collection without issues.
The line
[aCollection addObject:bar]
is your problem. You cannot modify aCollection while enumerating it. The better approach would be to create a temporary NSMutableArray, add bar to that, then call [aCollection addObjectsFromArray:] with your temporary array.
For example:
NSMutableArray *foundObjects = [NSMutableArray array];
for (NSDictionary *aDictionary in aCollection) {
if ([[bar objectForKey:#"id"] isEqual:[aDictionary objectForKey:#"id"]])
break;
[foundObjects addObject:bar];
}
[aCollection addObjectsFromArray:foundObjects];
From The Objective-C 2.0 Programming Language, you cannot modify the collection being enumerated:
Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.
Just copy the array. Otherwise you're messing with the collection that you're iterating:
for (NSDictionary *tmp in aCollection.copy) {
if ([[bar valueForKey:#"id"] isEqualToString:[tmp valueForKey:#"id"]])
break;
[aCollection addObject:bar];
}
Everyone else has offered useful answers, all of which seem correct.
Your code, however, does not do what they think it does. It probably doesn't do what you think it does either.
You try to add 'bar' to the collection if the first object returned by the enumerator does not match bar.
Everyone thinks that you are trying to add bar if it doesn't exist in the collection at all.
I suspect you taken a Python construct and ported it incorrectly.
solved
i has same problem in coredata objects enumeration .
do not change sub object which relies in ARRAY on which Loop is Running
Like, if i changed/Modify object while On loop .. it will give this error
for (LoadList *objLL in ArrLoadList) { // here Enumaration is Going on
// here i has removed objects in ArrLoadList and reassign the ArrLoadList ..
// it will gives me this error
// So Don't change the Main Array object
}