Im using XMPPFramework and in it's code there's a method like this:
- (NSDictionary *)occupants
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupants;
}
else
{
__block NSDictionary *result;
dispatch_sync(moduleQueue, ^{//IT BLOCKS HERE, WITHOUT MESSAGE
result = [occupants copy];
});
return [result autorelease];
}
}
[EDIT]
It blocks inconsistently, not always, since the app is not doing anything I pause it and I see the thread has stopped there, and it never continues to execute.
What is wrong? Any ideas?
Thanks
The behavior you explain perfectly matches with the one that appears when you try to send perform an operation on main thread via GCD while being on the main thread. So you should check if moduleQueue is the main queue, then this is it. Try checking if it is the main queue if it is, skip the dispatch_sync block.
Blocks sometimes need to retain variables to ensure they are available when they execute. If you use a local variable inside a block, you should initialise it to zero where you declare it outside the block.
Related
i have a non arc project. i'm trying to use dispatch_async to get data from server and save it in sqlite. the dispatch_async happens inside a method with callback. on calling the method the app crashes with exc bad access. here is how i've implemented the code.
- (void) HandleData:(const char*) receivedData WithSuccess:(void(^)(BOOL finishing))completed
{
dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
dispatch_async(fetchQ, ^{
[self write_data_in_sqlite]// **<--crash happens here in the method which is called here**
}
dispatch_sync(dispatch_get_main_queue(), ^{
completed(YES);
});
});
dispatch_release(fetchQ);
}
and i call the method as follow:
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess:^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
}];
if i remove the dispatch_async then it doesnt crash, but my UI gets blocked while writing to the sqlite.
what am i doing wrong?
edit:
removing the block and using dipatch_async produces the same exc_bad_access crash.
edit 2:
i tried example answer given below, it still crashes.
i thought to copy it then autorelease it. it crashes still but nit that often. i'm gonna check for memory leak. i'll report.
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess: [[^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
} copy] autorelease];
edit 3:
the crash happens in strlen even the xml content is in xmlResopnse. but why this happen with dispatch and not without it
xmlDocPtr xml= xmlParseMemory(xmlResopnse, strlen(xmlResponse);
edit 4:
as in answer below suggested not to use c objects in dispatch async. so i converted xmlResponse from const char* to nsstring and it doesnt crash.
Everything you've shown seems to be okay in terms of blocks and memory management. It must be something else.
I notice that you're passing in a C string (the char pointer receivedData) that you're not using. If you're not showing us the real code, and you are actually using the receivedData variable in the block, then that could be a problem, because the block simply captures the char pointer, but does not manage the memory of the string behind the pointer (it is not an Objective-C object). Therefore, it is possible that the C string is only valid in the calling scope (before the asynchronous operation), and no longer valid when the asynchronous operation runs. Your statement that something is crashing at strlen supports the idea that there is something wrong with some C string. You should try using NSString objects instead, since as objects they are properly memory-managed by blocks.
We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.
Say you do
MyLock *lock = [[MyLock new] autorelease];
#synchronized(lock) {
NSLog(#"Hello World");
//some very long process
}
In the main thread. Does that mean till //some very long process is done, the main thread is locked? If some other thread call
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
//Do some updates
});
That some updates will never be called? Am I correct?
If the code in the first code snippet never finishes, the second one won't be called, regardless of the #synchronized statement. The thread is blocked by the code that you're executing. The #synchronized statement is to synchronize data access between multiple threads and to be useful, it requires that all participating threads actually use the statement. It will not "magically" lock access to the data structure, unless all participating threads "agree" on it.
You don't use #synchronized to ensure that only one method executes on a given (single) thread, that is the case anyhow.
To give you a concrete example of its use, let's say you have an NSMutableArray that you want to protect from getting modified from different threads at the same time (which could lead to data corruption). In that case, you could always access it in a #synchronized block with the same lock token.
Example:
//Thread 1:
#synchronized (myArray) {
[myArray addObject:#"foo"];
}
//Thread 2:
#synchronized (myArray) {
[myArray removeObjectAtIndex:0];
}
This will ensure that the blocks of code that are enclosed by the #synchronized will never execute simultaneously. When one thread enters the block, other threads wait until it finishes, but only if they also use the same #synchronized statement. If you forget to use it on one thread, it doesn't help at all if you used it on the other.
The short answer is no. I think you dont understand the concept of locking. You should read more about syncchronization for example here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
You have to synchronize using the same locking object (same instance!) in every case when you access the code, which you are trying to protect. You can store the locking object as property of a class.
In your case:
self.lock = [[MyLock new] autorelease]; //in init method initialize retain or strong lock property
...
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
});
If you can use as the locking object, the object which your are trying to protect.
Im using XMPPFramework and in it's code there's a method like this:
- (NSDictionary *)occupants
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupants;
}
else
{
__block NSDictionary *result;
dispatch_sync(moduleQueue, ^{//IT BLOCKS HERE, WITHOUT MESSAGE
result = [occupants copy];
});
return [result autorelease];
}
}
[EDIT]
It blocks inconsistently, not always, since the app is not doing anything I pause it and I see the thread has stopped there, and it never continues to execute.
What is wrong? Any ideas?
Thanks
The behavior you explain perfectly matches with the one that appears when you try to send perform an operation on main thread via GCD while being on the main thread. So you should check if moduleQueue is the main queue, then this is it. Try checking if it is the main queue if it is, skip the dispatch_sync block.
Blocks sometimes need to retain variables to ensure they are available when they execute. If you use a local variable inside a block, you should initialise it to zero where you declare it outside the block.
Executing a block inside #synchronized seems to negate the lock.
- (void)method {
#synchronized(self) {
if(ivar == nil) {
ivar = [myBlock() retain];
}
}
}
The instance variable ivar is not written in any other location.
I've observed that the block myBlock sometimes is executed twice in my application.
How can this ever happen? How to avoid this an do a real working lock?
Maybe you could move the locking inside the block.
The lock worked fine as synchronized locks only threads, and the same thread was accessing the region twice. The problem was, that myBlock executed itself inside under some circumstances.