Is it safe to use +load in a dynamic framework to set up a quick WKNavigationDelegate?
Clash Royale CLAN TAG#URR8PPP
Is it safe to use +load in a dynamic framework to set up a quick WKNavigationDelegate?
I'm setting up a simple WKNavigationDelegate
in my dynamic framework to get WKWebView
's default user agent string:
WKNavigationDelegate
WKWebView
@interface MyDelegate: NSObject <WKNavigationDelegate>
@end
static NSString *_defaultUserAgent;
static WKWebView *_defaultWebView;
static MyDelegate *_myDelegate;
@implementation MyDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
DispatchHelper.runOnMain = ^
[_defaultWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id __nullable userAgent, NSError * __nullable error)
_defaultUserAgent = userAgent;
_defaultWebView = nil;
_myDelegate = nil;
];
;
@end
@implementation WKWebView (Util)
+ (void)load
_myDelegate = MyDelegate.new;
WKWebView *wkWebView = WKWebView.new;
wkWebView.navigationDelegate = _myDelegate;
[wkWebView loadHTMLString:@"<HTML><BODY>TEST</BODY></HTML>" baseURL:nil];
_defaultWebView = wkWebView;
@end
Is this safe, or is +load too early to try something like this? In my testing I haven't noticed any issues with it, but after reading this Mike Ash blog, he says using +load is dangerous/tricky.
Specifically from the blog:
Keep in mind that there's no autorelease pool present at loading time
(usually) so you'll need to wrap your code in one if you're calling
into Objective-C stuff.
Am I at risk here by not using @autoreleasepool? I'm confused on how adding
+ (void)load
@autoreleasepool
_myDelegate = MyDelegate.new;
WKWebView *wkWebView = WKWebView.new;
wkWebView.navigationDelegate = _myDelegate;
[wkWebView loadHTMLString:@"<HTML><BODY>TEST</BODY></HTML>" baseURL:nil];
_defaultWebView = wkWebView;
helps me here.
1 Answer
1
+load
is rife with danger and fragility. I have had to track down many a fun bug due to +load
surprises over the decades. In general, it should be avoided. And when used, it should touch a minimal amount of the rest of the system specifically because you'll be changing the order within which things are initialized at runtime.
+load
+load
I would recommend that you have some kind of initialization hook in your framework that your framework's clients are expected to call in the app, typically during the app delegate's didFinishLaunching:....
method.
didFinishLaunching:....
You can put in assert()s
along other code paths that can warn or raise if the framework was not properly initialized.
assert()s
Thanks for this - I'm gonna go with an initialization hook as suggested. I did have someone complaining about a crash (message being sent to null
MyDelegate
type). I'm still trying to understand the situation for that occurring as I can't reproduce it myself. Apparently somehow when the system tries to fire the webView:didFinishNavigation
delegate method, my delegate object has already been deallocated. I'm guessing this has something to do with the fact that the delegate method is fired from the main run loop, but I set up the delegate outside of the main run loop.– Adam Johns
Aug 12 at 20:01
MyDelegate
webView:didFinishNavigation
Or maybe the issue is I'm not setting
_defaultWebView.navigationDelegate
back to nil when I set _myDelegate
to nil. And the system is attempting to fire another delegate method on the webview later.– Adam Johns
Aug 12 at 20:15
_defaultWebView.navigationDelegate
_myDelegate
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
BTW: The autorelease pool is needed because +load runs outside of a run loop. There is no automatic autoreleasepool in place.
– bbum
Aug 10 at 19:27