1

I have a custom implementation of a logger, which logs the actions and operations in my app.

A strong NSString reference is living in one of my Singleton Classes.

I call a method named "-(void)writeToFile: (BOOL) rightNow", whenever I need to log something. This method also lives in that Singleton class.

Yes passed to rightNow, will trigger the immediate call to writeData: on NSFileHandle, forcing the NSString to be written to the file and emptying the NSString afterwards.

No, will only append the string and wait for a Yes value in rightNow to do the write operation.

Whenever I call writeToFile from a method which is being called through a timer, with rightNow param as YES, my app halts for 5/6 mins.

Similarly, If I call writeToFile from a block (let's say a web service returned some data on a block), the app halts again.

To my understanding, since writeData works synchronously, for some reason, it breaks when called from block or timers (which I am assuming are called on different threads).

Is this the expected behavior? I could wrap my writeToFile call around GCD, but what if the thread I am requesting (main/global), the current state is that only? Wouldn't it create a deadlock calling main thread on main thread?

Code:

- (void) writeToFile: (NSString *) targetString rightNow: (BOOL) rightNow
{
    //_loggingString holds the data, which keeps on accumulating as the user performs operations. At some point of time (callbacks from API's I call this method, to actually, write this string in the file and clear this string afterwards.)
    NSString *oldString = [_loggingString copy];

    _loggingString = [oldString stringByAppendingString:targetString];

    if (rightNow)
    {
        if (![[NSFileManager defaultManager]fileExistsAtPath:@"somePath"])
        {
            [[NSFileManager defaultManager]createFileAtPath:@"somePath" contents:nil attributes:nil];
        }

        NSFileHandle *fileHandle =  [NSFileHandle fileHandleForWritingAtPath:@"somePath"];

        [fileHandle seekToEndOfFile];

        [fileHandle writeData:[_loggingString dataUsingEncoding:NSUTF8StringEncoding]];

        _loggingString = @"";
    }
}
nr5
  • 4,228
  • 8
  • 42
  • 82
  • I use a even simpler version of this logging function, which always logs the passed string to a file (no `rightNow` parameter), and I have no problems logging from many threads concurrently (other than mixed output from different threads). So, the problem is not caused by which thread is writing to the file. Please provide the actual code you're executing and we'll see (the code shown right now doesn't even have the `rightNow` parameter, and it's copying `_loggingString` but never using the copy!) – gog Oct 23 '18 at 09:03
  • @gog edited my question with the code. `_loggingString` is copied to `olderString` variable. – nr5 Oct 23 '18 at 09:51

1 Answers1

1

Since your logging takes 5/6 mins, and halts subsequent operations in your app, simply move the logging to another queue with GCD:

dispatch_queue_t loggingQueue = dispatch_queue_create("MyLoggingQueue", DISPATCH_QUEUE_SERIAL);

and then, perform the logging always on that queue:

dispatch_async(loggingQueue, ^(void){
    [TheLoggerSingleton writeToFile:@"LogMe" rightNow:YES];
});

At this point, it doesn't make any difference calling writeToFile with YES or NO as rightNow parameter; you could always use YES

gog
  • 1,220
  • 11
  • 30
  • Is it required to make loggingQueue as a strong prperty? just wondering since this method can be called frequently – nr5 Oct 23 '18 at 11:35
  • You can make it a property, or you can make it a private instance variable, like [this answer](https://stackoverflow.com/a/13263345/6059640) explains. If you make it an instance variable, you should declare another method `logToFile` in your logger singleton class which performs the `dispatch_async` call – gog Oct 23 '18 at 11:45