jdg / MBProgressHUD

MBProgressHUD + Customizations
http://www.bukovinski.com/
MIT License
16.01k stars 3.56k forks source link

Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState] #552

Closed Arvin-J closed 5 years ago

Arvin-J commented 5 years ago

Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]

Arvin-J commented 5 years ago

[reports] Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState] PID: 850, TID: 218915, Thread name: com.apple.CoreMotion.MotionThread, Queue name: com.apple.root.default-qos.overcommit, QoS: 0 Backtrace: 4 libobjc.A.dylib 0x0000000185eb3894 + 56 5 CoreMotion 0x000000018c730760 CoreMotion + 304992 6 CoreMotion 0x000000018c730c94 CoreMotion + 306324 7 CoreMotion 0x000000018c730ba4 CoreMotion + 306084 8 CoreMotion 0x000000018c762384 CoreMotion + 508804 9 CoreMotion 0x000000018c7623e4 CoreMotion + 508900 10 CoreFoundation 0x0000000186c494d8 + 28 11 CoreFoundation 0x0000000186c48dbc + 276 12 CoreFoundation 0x0000000186c43ba4 + 1016 13 CoreFoundation 0x0000000186c43494 CFRunLoopRunSpecific + 452 14 CoreFoundation 0x0000000186c441f8 CFRunLoopRun + 84 15 CoreMotion 0x000000018c761d1c CoreMotion + 507164 16 libsystem_pthread.dylib 0x00000001868be9c8 + 132 17 libsystem_pthread.dylib 0x00000001868be924 _pthread_start + 52 18 libsystem_pthread.dylib 0x00000001868c6ddc thread_start + 4

ciffa commented 5 years ago

This only happens in iOS 12. Many libraries are affected by this. I believe Apple simply improved their Main Thread Checker and it surfaced a lot of UI activity on background threads.

matej commented 5 years ago

I don't think MBProgressHUD uses -[UIApplication applicationState] and that backtrace is not really helpful (all system libraries, no symbols). How does this relate to MBProgressHUD?

ciffa commented 5 years ago

I don't have a better backtrace, but it only appears when using MBProgressHUD. Seems like it's only a problem on Xs and Xs Max.

You can see that others have the problem by searching on Github: https://github.com/search?q=background+thread%3A+-%5BUIApplication+applicationState%5D&type=Issues

matej commented 5 years ago

MBProgressHUD uses motion effects, but those are set up on the main thread and there are no delegates or block callbacks involved that could be pushing the execution on a secondary thread. See updateBezelMotionEffects:

https://github.com/jdg/MBProgressHUD/blob/202d8895c6eb10ead2e4b3c5837a68c95693d51f/MBProgressHUD.m#L468-L494

I'd assume this is a system issue when motion effects are used. If that assertion is annoying or you want to play it safe, you can disable them by setting defaultMotionEffectsEnabled to NO. Their effect is barely noticeable anyways.

Closing this, as I don't really see anything we could do about it in client-side code.

Arvin-J commented 5 years ago

Thanks all @matej @ciffa

rocona commented 5 years ago

@matej I already disable them by setting defaultMotionEffectsEnabled to NO, But it don't work. and i find defaultMotionEffectsEnabled is YES when MBProgressHUD init.

matej commented 5 years ago

It's YES after initialization, but you can set it to NO afterwards. No idea what's up if that doesn't help.

xZeok commented 5 years ago

Any solution for this? in the code: let hud = MBProgressHUD.showAdded(to: self.view.window!, animated: true) its happening.. so setting defaultMotionEffectsEnabled afterwards does not prevent the error. testing on iPhone XS Max

Can we initialize MBProgressHud without showing it?? maybe we can set defaultMotionEffectsEnabled = false before showing it.. i checked that im on the main thread..

matej commented 5 years ago

Just initialize the hud, configure it and present it. You can check what that convenience initializer does and simply do the same in your code + disable motion effects.

https://github.com/jdg/MBProgressHUD/blob/202d8895c6eb10ead2e4b3c5837a68c95693d51f/MBProgressHUD.m#L46-L48

YangYouYong commented 5 years ago

The same as @xZeok testing on iPhone XS Max

Main Thread

Thread 1 Queue : com.apple.main-thread (serial)
0 __ulock_wait ()
7 -[UIView addMotionEffect:] ()
8 -[MBProgressHUD updateBezelMotionEffects]
9 -[MBProgressHUD setupViews]
10 -[MBProgressHUD commonInit]
11 -[MBProgressHUD initWithFrame:]
12 -[MBProgressHUD initWithView:]
13 +[MBProgressHUD showHUDAddedTo:animated:] 
24  UIApplicationMain ()
25  main

com.apple.CoreMotion.MotionThread (8)

#0  0x000000010292d6d4 in __main_thread_checker_on_report ()
#1  0x000000010292da38 in __ASSERT_API_MUST_BE_CALLED_FROM_MAIN_THREAD_FAILED__ ()
#2  0x000000010292ddfc in checker_c ()
#3  0x000000010292d654 in trampoline_c ()
#4  0x00000001028ed3fc in handler_start ()
#5  0x00000001c474b894 in -[NSObject performSelector:] ()
#6  0x00000001cafc8760 in ___lldb_unnamed_symbol1517$$CoreMotion ()
#7  0x00000001cafc8c94 in ___lldb_unnamed_symbol1539$$CoreMotion ()
#8  0x00000001cafc8ba4 in ___lldb_unnamed_symbol1536$$CoreMotion ()
#9  0x00000001caffa384 in ___lldb_unnamed_symbol2712$$CoreMotion ()
#10 0x00000001caffa3e4 in ___lldb_unnamed_symbol2715$$CoreMotion ()
#11 0x00000001c54e14d8 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#12 0x00000001c54e0dbc in __CFRunLoopDoBlocks ()
#13 0x00000001c54dc0c0 in __CFRunLoopRun ()
#14 0x00000001c54db494 in CFRunLoopRunSpecific ()
#15 0x00000001c54dc1f8 in CFRunLoopRun ()
#16 0x00000001caff9d1c in ___lldb_unnamed_symbol2700$$CoreMotion ()
#17 0x00000001c51569c8 in _pthread_body ()
#18 0x00000001c5156924 in _pthread_start ()
#19 0x00000001c515eddc in thread_start ()
matej commented 5 years ago

Hm.. ok. Looks like updateBezelMotionEffects is called from initialization so disabling mention effects still triggers an update in this code path.

YangYouYong commented 5 years ago

yeah, so, the updateBezelMotionEffects function should be optional in initialization

damikdk commented 5 years ago

Any update here? :(

damikdk commented 5 years ago

Problem fixed for me with new iOS update (12.1.4)

lexuanquynh commented 5 years ago

no update here?

Boggartfly commented 5 years ago

Problem fixed for me with new iOS update (12.1.4)

Doesn't work for me yet. Still Crashes on iOS 12.1.4 iPhone XS Max. Created issue on the ResearchKit Repo.

Boggartfly commented 5 years ago

I've filed a rdar on bugreport.apple.com and engineering has marked my bug as a duplicate. Lets hope they fix this soon.

iwill commented 5 years ago

It is not fixed yet on iOS 12.2.

MihaiPantiru commented 5 years ago

Compiling our app with Xcode 10.2 blocks the entire UI for a couple of seconds when we add an MBProgressHUD because of this issue.

kylebrowning commented 5 years ago

^

tonitonisilence commented 5 years ago

我的xs,直接build的时候就会出现这个问题,但是再点击运行,或者直接拔掉线不连接电脑就没事。

SeongBrave commented 5 years ago

@muzizhu007 i test, it's Ok,but why?

iwill commented 5 years ago

I cannot wait at all, I temporarily fixed it in my project -- Create a subclass of MBProgressHUD, implement the areDefaultMotionEffectsEnabled method which return NO if iOS 12 && iPhone X Series, return [super areDefaultMotionEffectsEnabled] otherwise.

kylebrowning commented 5 years ago

I removed This library entirely. 🤷🏻‍♂️

damikdk commented 5 years ago

Problem fixed for me with new iOS update (12.1.4)

It's broken again on 12.2.

SandeepDhull1990 commented 5 years ago

I have been using this library for more than 6 years, First time I ever faced issues with it :(

dpanzer commented 5 years ago

@iwill 's solution worked for me however the method you have to override is areDefaultMotionEffectsEnabled because that is defined as the getter for the property defaultMotionEffectsEnabled

iwill commented 5 years ago

@dpanzer Oh, yes. I actually used areDefaultMotionEffectsEnabled in my project, thank you!

wonderffee commented 5 years ago

I encountered the same issue. Interestingly, without XCode attached, the dev device has no issues. Try to run your code without being connected to the Debugger. Also you can solve this problem by setting defaultMotionEffectsEnabled to NO in [MBProgressHUD commonInit] method.

abotkin-cpi commented 5 years ago

I encountered the same issue. Interestingly, without XCode attached, the dev device has no issues. Try to run your code without being connected to the Debugger. Also you can solve this problem by setting defaultMotionEffectsEnabled to NO in [MBProgressHUD commonInit] method.

You don't hit the issue when you're not attached as you're not running with the Main Thread Checker enabled then like when you hit run from Xcode. Main thread checker is causing the main thread freeze as it records the stack. Folks can also avoid this by disabling the Main Thread Checker by going to Edit Scheme -> Run -> Diagnostics, but then you run the risk that you commit that and don't get the benefits of Main Thread Checker when your code is the cause.

This won't affect your users either as they're not running your app with Main Thread Checker.

javaboyjunior commented 5 years ago

As you can see from the radar, this happens the first time a CMMotionManager gets created. I've solved this by initializing a throwaway CMCoreMotionManager in my AppDelegate. You get the delay cause by the OS threading bug while your splash screen is up. I think this is better than getting a UI freeze in the app. Just in case useful.

dengzemiao commented 5 years ago

I solved the problem in this way for the time being.

- (void)updateBezelMotionEffects {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
    MBBackgroundView *bezelView = self.bezelView;
    if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return;

//    if (self.defaultMotionEffectsEnabled) {
//        CGFloat effectOffset = 10.f;
//        UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
//        effectX.maximumRelativeValue = @(effectOffset);
//        effectX.minimumRelativeValue = @(-effectOffset);
//
//        UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
//        effectY.maximumRelativeValue = @(effectOffset);
//        effectY.minimumRelativeValue = @(-effectOffset);
//
//        UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
//        group.motionEffects = @[effectX, effectY];
//
//        [bezelView addMotionEffect:group];
//    } else {
        NSArray *effects = [bezelView motionEffects];
        for (UIMotionEffect *effect in effects) {
            [bezelView removeMotionEffect:effect];
        }
//    }
#endif
}
qing-song commented 5 years ago

Just set _defaultMotionEffectsEnabled = NO and no care other method

remizorrr commented 5 years ago

there is no api to set defaultMotionEffectsEnabled tofalse before it is used. It is used in the "init" flow, and couldn't be changed prior to that. The ability to set the variable to false should be brought up to pre-init, or init.

dklinzh commented 5 years ago

You can try this to guarantee -[UIApplication applicationState] called on a main thread.

Boggartfly commented 5 years ago

Do you have a Swift 4 solution? @dklinzh

dklinzh commented 5 years ago

Do you have a Swift 4 solution? @dklinzh

@Boggartfly It is the Swift 4 version below and recommended ONLY be used on Debug configuration :

#if DEBUG
extension UIApplication {

    private class ApplicationState {

        static let shared = ApplicationState()

        var current = UIApplication.State.inactive

        private init() {
            let center = NotificationCenter.default
            let mainQueue = OperationQueue.main
            center.addObserver(forName: NSNotification.Name.UIApplicationDidEnterBackground, object: nil, queue: mainQueue) { (notification) in
                self.current = UIApplication.shared.applicationState
            }
            center.addObserver(forName: NSNotification.Name.UIApplicationWillEnterForeground, object: nil, queue: mainQueue) { (notification) in
                self.current = UIApplication.shared.applicationState
            }
            center.addObserver(forName: NSNotification.Name.UIApplicationDidFinishLaunching, object: nil, queue: mainQueue) { (notification) in
                self.current = UIApplication.shared.applicationState
            }
            center.addObserver(forName: NSNotification.Name.UIApplicationDidBecomeActive, object: nil, queue: mainQueue) { (notification) in
                self.current = UIApplication.shared.applicationState
            }
            center.addObserver(forName: NSNotification.Name.UIApplicationWillResignActive, object: nil, queue: mainQueue) { (notification) in
                self.current = UIApplication.shared.applicationState
            }
        }
    }

    @objc
    private var __applicationState: UIApplication.State {
        if Thread.isMainThread {
            return self.__applicationState
        } else {
            return ApplicationState.shared.current
        }
    }

    /// FIXME: -[UIApplication applicationState] called on a background thread.
    public static func mainThreadApplicationState() {
        if let originalMethod = class_getInstanceMethod(UIApplication.self, #selector(getter: applicationState)),
            let swizzledMethod = class_getInstanceMethod(UIApplication.self, #selector(getter: __applicationState)) {
            _ = ApplicationState.shared
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}
#endif
matej commented 5 years ago

Since https://github.com/jdg/MBProgressHUD/pull/578 motion effects will not longer be created if defaultMotionEffectsEnabled is set to NO / false BEFORE the HUD is displayed.

Let me reiterate. This is not an issue with MBProgressHUD. This is a system bug triggered by using motion effects on certain devices. What you're seeing is the Xcode main thread checker catching the bug (UIKit access on the main thread). This is a debug time helper and won't crash your app in production (at least not in this way).

While I don't believe this is actually causing any issues in production, I still decided to modify the default value of this property to NO in https://github.com/jdg/MBProgressHUD/commit/9a0d8e288df21958be225418d5b7791d42d97b20. This means you don't need to do anything other than updating to resolve the assertions.

You will however need to set defaultMotionEffectsEnabled to YES / true fro now on if you want motion effects.

ph4r05 commented 5 years ago

@matej will you pls do a new release so we can update via pods/Carthage? thanks! For now, I forked your repo and made 1.1.1 release.

So the Cartfile would be:

github "ph4r05/MBProgressHUD" ~> 1.1.1
mubasherkhan commented 5 years ago

A Quick Fix

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
@interface MKProgressHUD:MBProgressHUD {

}
@end
@implementation MKProgressHUD
-(BOOL) areDefaultMotionEffectsEnabled{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"12.0")) {
        return FALSE;
    }
    return TRUE;
}
- (id)init {
    return self;
}

@end