I have test code like this
- (void)viewDidLoad
{
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(test) object:nil];
[thread start];
}
-(void)test
{
MyClass *my = [[[MyClass alloc] init] autorelease];
NSLog(#"%#",[my description]);
}
I did not create any autoreleasepool for my own thread, but when the thread exit, object "my" just dealloc.why?
even though I change my test code as below
- (void)viewDidLoad
{
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(test) object:nil];
[thread start];
}
-(void)test
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyClass *my = [[[MyClass alloc] init] autorelease];
NSLog(#"%#",[my description]);
}
I create my own autoreleasepool but not drain it when the thread exit. object "my" can still dealloc anyway. why?
I use Xcode5 and not using ARC
It's not documented, but the answer appears to be Yes, on OS X 10.9+ and iOS 7+.
The Objective-C runtime is open-source so you can read the source to see what's going on. The latest version of the runtime (646, which shipped with OS X 10.10 and iOS 8) does indeed add a pool if you perform an autorelease without a pool on the current thread. In NSObject.mm:
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// No pool in place.
assert(!hotPage());
if (obj != POOL_SENTINEL && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// Push an autorelease pool boundary if it wasn't already requested.
if (obj != POOL_SENTINEL) {
page->add(POOL_SENTINEL);
}
// Push the requested object.
return page->add(obj);
}
This function is called when you push the first pool (in which case the thing pushed is POOL_SENTINEL), or you autorelease with no pool. When the first pool is pushed, it sets up the autorelease stack. But as you see from the code, as long as the DebugMissingPools environmental variable is not set (it's not set by default), when autorelease is done with no pool, it also sets up the autorelease stack, and then pushes a pool (pushes a POOL_SENTINEL).
Similarly, (it's a little hard to follow without looking at the other code, but this is the relevant part) when the thread is destroyed (and the Thread-Local Storage is destroyed), it releases everything in the autorelease stack (that's what the pop(0); does) so it doesn't rely on the user to pop the last pool:
static void tls_dealloc(void *p)
{
// reinstate TLS value while we work
setHotPage((AutoreleasePoolPage *)p);
pop(0);
setHotPage(nil);
}
The previous version of the runtime (551.1, which came with OS X 10.9 and iOS 7), also did this, as you can see from its NSObject.mm:
static __attribute__((noinline))
id *autoreleaseSlow(id obj)
{
AutoreleasePoolPage *page;
page = hotPage();
// The code below assumes some cases are handled by autoreleaseFast()
assert(!page || page->full());
if (!page) {
// No pool. Silently push one.
assert(obj != POOL_SENTINEL);
if (DebugMissingPools) {
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
push();
page = hotPage();
}
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
But the version before that (532.2, which came with OS X 10.8 and iOS 6), does not:
static __attribute__((noinline))
id *autoreleaseSlow(id obj)
{
AutoreleasePoolPage *page;
page = hotPage();
// The code below assumes some cases are handled by autoreleaseFast()
assert(!page || page->full());
if (!page) {
assert(obj != POOL_SENTINEL);
_objc_inform("Object %p of class %s autoreleased "
"with no pool in place - just leaking - "
"break on objc_autoreleaseNoPool() to debug",
obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return NULL;
}
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
Note that the above works for any pthreads, not just NSThreads.
So basically, if you are running on OS X 10.9+ or iOS 7+, autoreleasing on a thread without a pool should not lead to a leak. This is not documented and is an internal implementation detail, so be careful relying on this as Apple could change it in a future OS. However, I don't see any reason why they would remove this feature as it is simple and only has benefits and no downsides, unless they completely re-write the way autorelease pools work or something.
Apple documentation says (4th paragraph):
You create an NSAutoreleasePool object with the usual alloc and
init messages and dispose of it with drain (or release—to understand
the difference, see Garbage Collection). Since you cannot retain an
autorelease pool (or autorelease it—see retain and autorelease),
draining a pool ultimately has the effect of deallocating it. You
should always drain an autorelease pool in the same context
(invocation of a method or function, or body of a loop) that it was
created. See Using Autorelease Pool Blocks for more details.
Related
iOS project in a mix of Obj-C++ and C++ proper. I have a POD struct about 1 MB in size. There is a global instance of it. If I create a local instance of the same in a function that is invoked on a worker thread, the copy operation crashes (when invoked from a worker thread) in debug builds on a simulator. The release builds don't crash.
This smells like running out of stack size.
The worker thread in question is not created manually - is an NSOperationQueue worker.
The questions are twofold:
why does automatic stack growth fail?
how does one increase stack size on NSOperationQueue threads?
The repro goes:
struct S
{
char s[1024*1024];
};
S gs;
-(void)f
{
S ls;
ls = gs; //Crash!
}
Okay, I see about rewriting NSOperationQueue to grow the stack. That said - the **compiler (Clang) definitely has some kind of workaround for that. I'll check the disassembly just in case, but the same code doesn't crash in release builds. How come it's not engaging in this case? OBTW, checked the same code on Android, saw same crash.
PS: the motivating case is from not from my code, it's from a third party algorithm library. The original library doesn't really need a deep copy; the local variable is never written to, only read from; a reference (even a const one) would do just as well. My guess is that the author wanted a reference but messed it up. In the release build, the compiler optimizes is to a reference, thus no crash. In the debug build, it does not.
I don't know how up to date this documentation is, but according to Apple non-main threads top out at 512kiB stack size unless otherwise configured during thread creation.
I struggle to see a good reason to store such a data structure on the stack, particularly with (Obj-)C++ where you can easily wrap it in something like a std::unique_ptr which manages heap allocations and deallocations automatically. (Or indeed any other RAII based abstraction, or even storing it as an ivar in an ARC-enabled Objective-C class if you're so inclined.)
One downside to opting into very large stack sizes is that this memory likely stays resident but unused until the thread terminates, particularly on iOS where this memory won't even be swapped to disk. This is fine if you're explicitly starting up a thread and shut it down once you're done with your giant-stack-requiring-algorithm. But if you're running a one-off job on a pooled thread, you've now effectively leaked 1MB of memory. Maybe it's the embedded developer in me (or that I remember when iPhones only had 128MB RAM) but I'd prefer not to write code like that. (Or can someone come up with evidence that the low-memory warning mechanism purges unused stack space?)
Under the hood the threading mechanism of Cocoa uses unix POSIX threads for which stack size follows the following rules:
Default stack size, if it's not explicitly specified (e.g. in macOS you can find this value by running ulimit -s command, which for my machine is 8192 KiB, but for iOS is very likely a few times less)
Arbitrary stack size if it's specified during creating of a thread
Answering your first question:
why does automatic stack growth fail?
It "fails" because it's not allowed to grow beyond the allocated size for a given thread. More interesting question in this case - why it doesn't fail for release build? And frankly I don't have an answer here. I assume it most likely has something to do with the optimisation, where the compiler is allowed to bypass certain memory flow routines or discard some parts of code completely.
For the second question:
how does one increase stack size on NSOperationQueue threads?
The main thread of an application always has the default system stack size, and can only be altered in macOS (or rooted iOS device) with use of ulimit (size is given in KiB):
# Sets 32 MiB default stack size to a thread
% ulimit -s 32768
All other threads (to my knowledge) under both iOS and macOS have their size specified explicitly and it equals to 512 KiB. You will have to somehow forward the stack size to the pthread_create(3) function for them, something like this:
#import <Foundation/Foundation.h>
#import <pthread.h>
struct S {
char s[1024 * 1024];
};
void *func(void *context) {
// 16 MiB stack variable
S s[16];
NSLog(#"Working thread is finished");
auto* result = new int{};
return result;
}
int main(int argc, const char * argv[]) {
pthread_attr_t attrs;
auto s = pthread_attr_init(&attrs);
// Allocates 32 MiB stack size
s = pthread_attr_setstacksize(&attrs, 1024 * 1024 * 32);
pthread_t thread;
s = pthread_create(&thread, &attrs, &func, nullptr);
s = pthread_attr_destroy(&attrs);
void* result;
s = pthread_join(thread, &result);
if (s) {
NSLog(#"Error code: %d", s);
} else {
NSLog(#"Main is finished with result: %d", *(int *)result);
delete (int *)result;
}
#autoreleasepool {
}
return 0;
}
Unfortunately neither of queue API (GCD or NSOperation) exposes allocation part of their thread pools, let alone that NSThread doesn't let you to specify your own pthread explicitly for underlying execution. If you want to rely on those APIs, you will have to implement it "artificially".
Sample NSOperation subclass with arbitrary stack size thread
The interface of such a class can look something like this (provided the thread's stack size is constant and is not supposed to be an injected dependency):
// TDWOpeartion.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^TDWOperationBlock)(void);
__attribute__((__objc_direct_members__))
#interface TDWOperation: NSOperation
#property (copy, nonatomic, readonly) TDWOperationBlock executionBlock;
- (instancetype)initWithExecutionBlock:(TDWOperationBlock)block NS_DESIGNATED_INITIALIZER;
#end
NS_ASSUME_NONNULL_END
The implementation file:
// TDWOpeartion.mm
#import "TDWOpeartion.h"
#import <pthread.h>
#define EXECUTE_WITH_ERROR(codeVar, execution) if((codeVar = execution)) {\
NSLog(#"Failed to execute " #execution " with error code: %d", codeVar);\
return;\
}
NS_ASSUME_NONNULL_BEGIN
__attribute__((__objc_direct_members__))
#interface TDWOperation ()
#property (assign, getter=tdw_p_isThreadStarted) BOOL tdw_p_threadStarted;
#property (assign, nonatomic) pthread_t tdw_p_underlyingThread;
#property (strong, nonatomic, readonly) dispatch_queue_t tdw_p_productsSyncQueue;
#end
NS_ASSUME_NONNULL_END
#implementation TDWOperation
#synthesize tdw_p_threadStarted = _tdw_p_threadStarted;
#pragma mark Lifecycle
- (instancetype)initWithExecutionBlock:(TDWOperationBlock)block {
if (self = [super init]) {
_executionBlock = block;
_tdw_p_threadStarted = NO;
_tdw_p_productsSyncQueue = dispatch_queue_create("the.dreams.wind.property_access.TDWOperation.isThreadStarted",
DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (instancetype)init {
return [self initWithExecutionBlock:^{}];
}
#pragma mark NSOperation
- (void)main {
pthread_attr_t attrs;
int statusCode;
EXECUTE_WITH_ERROR(statusCode, pthread_attr_init(&attrs))
// Allocates 32 MiB stack size
EXECUTE_WITH_ERROR(statusCode, pthread_attr_setstacksize(&attrs, 1024 * 1024 * 32))
pthread_t thread;
EXECUTE_WITH_ERROR(statusCode, pthread_create(&thread, &attrs, &tdw_p_runExecutionBlock, (__bridge_retained void *)self))
EXECUTE_WITH_ERROR(statusCode, pthread_attr_destroy(&attrs))
void* result = nullptr;
if (!self.cancelled) {
self.tdw_p_threadStarted = YES;
EXECUTE_WITH_ERROR(statusCode, pthread_join(thread, &result));
self.tdw_p_threadStarted = NO;
}
NSLog(#"Main is finished with result: %d", *(int *)result);
delete (int *)result;
}
#pragma mark Properties
- (void)setExecutionBlock:(TDWOperationBlock)executionBlock {
if (self.tdw_p_isThreadStarted) {
[NSException raise:NSInternalInconsistencyException
format:#"Cannot change execution block when execution is already started"];
}
_executionBlock = executionBlock;
}
- (BOOL)tdw_p_isThreadStarted {
__block BOOL result;
dispatch_sync(_tdw_p_productsSyncQueue, ^{
result = _tdw_p_threadStarted;
});
return result;
}
- (void)setTdw_p_threadStarted:(BOOL)threadStarted {
dispatch_barrier_async(_tdw_p_productsSyncQueue, ^{
self->_tdw_p_threadStarted = threadStarted;
});
}
#pragma mark Private
void *tdw_p_runExecutionBlock(void *args) {
TDWOperation *self = (__bridge_transfer TDWOperation *)args;
if (self.executionBlock) {
self.executionBlock();
}
int *result = new int{};
return result;
}
#end
And now you can use it just like a regular NSOperation instance:
#import "TDWOpeartion.h"
#include <type_traits>
struct S {
unsigned char s[1024 * 1024];
};
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperations:#[
[[TDWOperation alloc] initWithExecutionBlock:^{
using elemType = std::remove_all_extents_t<decltype(S::s)>;
S arr[16];
auto numOfElems = sizeof(S::s) / sizeof(elemType);
for(decltype(numOfElems) i = 0; i < numOfElems; ++i) {
for (auto val: arr) {
val.s[i] = i % sizeof(elemType);
}
}
NSLog(#"Sixteen MiB were initialized");
}]
] waitUntilFinished:YES];
}
return 0;
}
I would like to make a function that only run once and cancel if is still running.
I tried it using a simple lock boolean on start/end, but sometimes it's "overlapping".
There's a better and secure way to do that?
#property (assign) BOOL lock;
- (void)myFuntion
{
if (self.lock) {
NSLog(#"(Canceled) Syncing is already running...");
return;
}
self.lock = YES;
// My Code
self.lock = NO;
}
The NSLock class should be able to help you here. I have not tried this example directly, but something like:
NSLock *myFunctionLock=[NSLock new]; // this should be a class data member/property/etc.
- (void)myFuntion
{
if (![myFunctionLock tryLock])
return; /* already running */
// My Synchronized Code
[myFunctionLock unlock];
}
We are all assuming that you are talking about concurrent programming, where you are running the same code on different threads. If that's not what you mean then you would need to explain what you DO mean, since code that runs on the same thread can only execute a function once at any particular moment.
Take a look at NSLock's tryLock function. The first caller to assert the lock gets back a TRUE, and can proceed to access the critical resource. Other callers get back FALSE and should not access the critical resource, but won't block.
Basic task
I have some multiplatform library which is using some C++ stream interface. I have to use this stream interface to upload data by NSURLSession. My implementation should work on OS X and iOS (currently I'm testing on OS X)
What I did
Task looks quite simple and I was sure I will implement this quite fast.
I have configured NSURLSession which is working fine if I'm using NSURLRequest with simple NSData.
I'm trying to use stream like this:
NSURLSessionDataTask *dataTask = [m_Private.session uploadTaskWithStreamedRequest: request];
HTTPDownoadTaskProxy *dataTaskProxy = [HTTPDownoadTaskProxy new];
// store data to properly handle delegate
dataTaskProxy.coreTask = dataTask;
dataTaskProxy.cppRequest= req;
dataTaskProxy.cppResponseHandler = handler;
dataTaskProxy.cppErrorHandler = errorHandler;
m_Private.streamedDataTasks[dataTask] = dataTaskProxy;
[dataTask resume];
So far so good. According to documentation of uploadTaskWithStreamedRequest I should receive notification from delegate and I do receive it:
- (void)URLSession: (NSURLSession *)session
task: (NSURLSessionTask *)task
needNewBodyStream: (void (^)(NSInputStream *bodyStream))completionHandler
{
HTTPDownoadTaskProxy *proxyTask = self.streamedDataTasks[task];
CppInputStreamWrapper *objcInputStream = [[CppInputStreamWrapper alloc] initWithCppInputStream:proxyTask.cppRequest.GetDataStream()];
completionHandler(objcInputStream);
}
Now I should receive calls in subclass of NSInputStream which is in my case CppInputStreamWrapper, and also it is quite simple:
#implementation CppInputStreamWrapper
- (void)dealloc {
NSLog(#"%s", __PRETTY_FUNCTION__);
}
- (instancetype)initWithCppInputStream: (const std::tr1::shared_ptr<IInputStream>&) cppInputStream
{
if (self = [super init]) {
_cppInputStream = cppInputStream;
}
return self;
}
#pragma mark - overrides for NSInputStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len {
return (NSInteger)self.cppInputStream->Read(buffer, len);
}
- (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len {
return NO;
}
- (BOOL)hasBytesAvailable {
return !self.cppInputStream->IsEOF();
}
#pragma mark - this methods are need to be overridden to make stream working
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
#pragma mark - Undocumented CFReadStream Bridged Methods
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
callback:(__unused CFReadStreamClientCallBack)inCallback
context:(__unused CFStreamClientContext *)inContext {
return NO;
}
#end
So I'm using workaround needed when subclassing NSInputStream.
Problem
Now this should work. But I'm not receiving any call of methods of CppInputStreamWrapper (except for my call when construction object).
No errors no warning are reported, nothing!
When I've added exception breakpoint I'm catching
thread #8: tid = 0x155cb3, 0x00007fff8b770743 libobjc.A.dylib`objc_exception_throw, name = 'com.apple.NSURLConnectionLoader', stop reason = breakpoint 1.1
This comes from thread com.apple.NSURLConnectionLoader which I didn't create.
I'm totally puzzled and have no idea what else I can do.
Update
I've used code form link in comment which is hosted on github.
Now at least some parts of my class are invoked by framework, but I see strange crash.
Crash is located in this method:
- (BOOL)_setCFClientFlags:(CFOptionFlags)inFlags
callback:(CFReadStreamClientCallBack)inCallback
context:(CFStreamClientContext *)inContext {
if (inCallback != NULL) {
requestedEvents = inFlags;
copiedCallback = inCallback;
memcpy(&copiedContext, inContext, sizeof(CFStreamClientContext));
if (copiedContext.info && copiedContext.retain) {
copiedContext.retain(copiedContext.info);
}
copiedCallback((__bridge CFReadStreamRef)self, kCFStreamEventHasBytesAvailable, &copiedContext); // CRASH HERE
} else {
requestedEvents = kCFStreamEventNone;
copiedCallback = NULL;
if (copiedContext.info && copiedContext.release) {
copiedContext.release(copiedContext.info);
}
memset(&copiedContext, 0, sizeof(CFStreamClientContext));
}
return YES;
}
Crash is EXC_BAD_ACCESS (when running tests on OS X). when I see this code everything looks fine. It should work! self is pointing to proper object with retain count 3 so I have no idea why it is crashing.
Undocumented private bridging API is not the only problem in custom NSInputStream implementation especially in the context of CFNetworking integration. I'd like to recommend to use my POSInputStreamLibrary as basic building block. Instead of implementing a lot of NSInputStream methods and supporting async notifications you should implement much simpler POSBlobInputStreamDataSource interface. At least you can look at POSBlobInputStream to consult what kind of functionality you should implement to support NSInputStream contract completely.
POSInputStreamLibrary is used in the most popular Russian cloud storage service Cloud Mail.Ru and uploads >1M files per day without any crashes.
Good luck and feel free to ask any questions.
I see you do have implementations of the undocumented CFReadStream bridge methods -- that is one of the more common issues. However... note the comment in the NSStream.h header for the NSStream class:
// NSStream is an abstract class encapsulating the common API to NSInputStream and NSOutputStream.
// Subclassers of NSInputStream and NSOutputStream must also implement these methods.
That means you also need to implement -open, -close, -propertyForKey:, -streamStatus, etc. -- every method that is declared on NSStream and NSInputStream, basically. Try calling -open yourself in your code (which NSURLConnection will eventually do) -- you will get the idea since it should crash right there. You will probably need at least some minimal status handling so that -streamStatus does not return NSStreamStatusNotOpen after -open is called, for example. Basically, every concrete subclass needs to implement the entirety of the API. It's not like a normal class cluster where just a couple of core methods need to be overridden -- even the -delegate and -setDelegate: methods must be implemented (the superclass does not have instance variable storage for it, I'm pretty sure).
AFNetworking has an internal AFMultipartBodyStream which has the minimal implementations needed -- you can see that example inside AFURLRequestSerialization.m. Another code example is HSCountingInputStream.
I am in a situation where I need to call multiple web service requests at a time to call to my server to delete messages on the server and I am having a difficult time trying to figure out the best way to trigger some methods to refresh my data at the completion of these group of web service calls.
From what I have researched using a semaphore counter should work for what I am wanting to do, but I am running into a problem where upon calling dispatch_release() on my semaphore token my application crashes with this -
libdispatch.dylib`_dispatch_semaphore_dispose$VARIANT$mp:
0x3c03ed70: push {r7, lr}
0x3c03ed72: ldr r1, [r0, #40]
0x3c03ed74: mov r7, sp
0x3c03ed76: ldr r2, [r0, #36]
0x3c03ed78: cmp r1, r2
0x3c03ed7a: bge 0x3c03ed7e ; _dispatch_semaphore_dispose$VARIANT$mp + 14
0x3c03ed7c: trap
Everything i've found on this problem points to the semaphore token being referenced by something but I can't see what would have a reference to the token.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
long messagesCount = [messages count] - 1;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(messagesCount);
BOOL (^isLastRequest)(void) = ^BOOL (void) {
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
if (0 == result) {
return false;
}
dispatch_release(semaphore);
return true;
};
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
if (error) {
isLastRequest();
completion(NO);
} else {
if (isLastRequest()) {
completion(YES);
}
}
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
EDIT
Thanks for the great answers. As Dustin said I was attempting to use dispatch_semaphore for something that it should not be used for. I accepted his answer because it was simple to implement and didn't need any re-structure of what i'm doing currently to send my web services. I now have some good reading material though about dispatch_groups in general though!
Thanks for all your help!
I'm not sure this is the most efficient way to solve your problem, but just looking through your code, one problem you might be running into is that you're creating the semaphore with a count and trying to release it when that count is less than the initial count. In GCD, always create a semaphore with 0 and then signal the semaphore to the correct number of "resources" you need to count. The reason is because semaphores can't be destroyed/released if their count is less than the initial count. It kinda makes sense if you think of it as a resource counter. Having a semaphore number less than the initial count means that you have a worker still using one of the resources.
You can see this code here http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c. The code that will throw the exception in _dispatch_semaphore_dispose is:
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
This is exactly what dispatch_group is designed to address. You dispatch several blocks to a group, and when they have all completed, another block will be executed (or you can wait on them if you need a synchronous behavior).
First see Waiting on Groups of Queued Tasks in the Concurrency Programming Guide, and see the dispatch_group functions in the GCD reference. To see them in action, see the JuliaCell example from Chapter 13 of iOS:PTL. Cocoa Samurai also has some examples.
Even if you can't actually dispatch the blocks to a group (it may not work with how MSMClient operates), you can still use dispatch groups manually by calling dispatch_group_enter() and dispatch_group_leave() to get the same behavior you're trying to get from the semaphore.
As a side note, BOOL is not the same as bool. BOOL return YES and NO, which are different than true and false (which I assume means you're compiling this as ObjC++, which always makes me shudder, but that's a different issue). Mixing them can matter because they can be (and sometimes are) different sizes. I've had to crashes due to that personally.
This is an incorrect usage for dispatch_semaphore -- it isn't meant to do what you are attempting. What you need is a counting variable that is thread safe. You can get this using __sync_sub_and_fetch.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
__block long messagesCount = [messages count];
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
long which = __sync_sub_and_fetch(&messageCount, 1);
if(which == 0)
completion(error == nil);
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
__sync_sub_and_fetch tells the CPU that you want it to take latest version of 'messageCount' from all threads (and cores), subtract 1, and give you the result.
I am using shared delegate for getting data on URL connection. I'm getting a memory leak on my code. Can anybody please tell me what I have done wrong?
Analyser Warning:
/Users/sathish/Documents/XXX 20100908 ManageMem/Classes/Data Download/XXX DataConnect.m:68:22: warning: Potential leak of an object allocated on line 68
gXXXDataConnect = [[XXXDataConnect alloc] customInit] ;
XXXDataConnect.h
(XXXDataConnect*)sharedXXXDataConnect;
XXXDataConnect.m
(XXXDataConnect *)sharedXXXDataConnect
{
if(gXXXDataConnect == nil)
gXXXDataConnect = [[XXXDataConnect alloc] customInit] ;
return gXXXDataConnect ;
}
Well, you're never deallocating your XXXDataConnect you've just allocated. If you don't deallocate it, who will?
Assuming this is Objective-C, I think you need a return [gXXXDataConnect autorelease] as your end-of-function. http://www.otierney.net/objective-c.html#retain might be a helpful link here.