4
- (NSString *)getAuthorizationHeader{
    iKMAppDelegate *delegate = (iKMAppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *header = [NSString stringWithFormat:@"Bearer %@", delegate.appDataObject.oauth2AcessToken];
    return header;
}

this method will got a warning in XCode9

[UIApplication delegate] must be called from main thread only

and i dont think a dispatch in main queue will be working for my function . so how to fix this warning well?

ximmyxiao
  • 2,622
  • 3
  • 20
  • 35
  • see this https://stackoverflow.com/questions/45081731/obj-c-uiapplication-delegate-must-be-called-from-main-thread-only – Anbu.Karthik Jul 24 '17 at 08:06
  • 2
    "and i dont think a dispatch in main queue will be working for my function" - and why is that? – mag_zbc Jul 24 '17 at 08:09
  • @mag_zbc because i have return something in my function , based on the delegate – ximmyxiao Jul 24 '17 at 08:24
  • It's very likely that your app delegate's `appDataObject`, or the data object's `oauth2AcessToken` is also not safe to access from a background thread. – Lily Ballard Sep 22 '17 at 00:41

2 Answers2

5

There's no going around accessing UIApplication's delegate on main thread, but you can easily do it using dispatch_sync

- (NSString *)getAuthorizationHeader{
    __block iKMAppDelegate *delegate;
    if([NSThread isMainThread]) {
        delegate = (iKMAppDelegate *)[UIApplication sharedApplication].delegate;
    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            delegate = (iKMAppDelegate *)[UIApplication sharedApplication].delegate;
        });
    }
    NSString *header = [NSString stringWithFormat:@"Bearer %@", delegate.appDataObject.oauth2AcessToken];
    return header;
}

As opposed to dispatch_async, the dispatch_sync function will wait until the block it's been passed finishes before returning.

With dispatch_sync it's necessary to check if the function isn't executed from the main thread, which would cause a deadlock.

Tumata
  • 1,507
  • 1
  • 11
  • 14
mag_zbc
  • 6,801
  • 14
  • 40
  • 62
0

You can take a pointer to the AppDelegate before your background function is called. And get it from that pointer.

<...Main thread in app...>
[[MyDelegateHolder holder] setDelegate: (iKMAppDelegate *)[UIApplication sharedApplication].delegate];

<...RunBackground task...>
[[MyDelegateHolder holder].delegate methodCall];

<...In MyDelegateHolder's delegate method  ....>
- (iKMAppDelegate*)delegate
{
  if (self.delegate == nil)
  {
    <Assert or...>
    runSyncOnMainThread(^(){//you should get delegate in sync method from the main thread
       self.delegate = (iKMAppDelegate *)[UIApplication sharedApplication].delegate;
     });
  }

  return self.delegate;
}

In my example MyDelegateHolder is a singleton.
If you need multithread access, before a delegate will not set, don't forget about locks for access (or mark field as atomic).

Andrew Romanov
  • 4,774
  • 3
  • 25
  • 40