最近一直在做手机H5的东西,网页写多了,测试也测出问题来了,打开十几个网页后,app出现无响应,app的webview界面出现黑屏等等奇怪的问题。
我试了几遍,APP内存占用从20M飙升到100M+,到了100M的时候,xcode被断开了,然后问题就一个个冒出来了-_-!
搜索了一下,是uiwebview内存泄漏,然后我就兼容了wkwebview。
wkwebview遇到的问题主要有几个:
1、多个wkwebview数据不同步,localstorage在a页面能用,去到b页面能用,在b页面写入东西,在a页面看不到
2、跳转新增webview,加载很慢
3、receiveMemoryWarning 之后,或者一段时间没用之后,返回之前的页面,页面变成空白
我解决了这几个问题,并写了一个替代的兼容webview
所以标题也可以叫uiwebview的无痛迁移
// // GsWebView.swift // iosclient // // Created by Yeshen on 16/8/31. // Copyright © 2016年 L. All rights reserved. // import Foundation import UIKit import WebKit protocol GsWebViewDelegate{ func webView(webView: GsWebView,shouldStartLoadWithRequest request: String?) -> Bool func webViewDidStartLoad(webView: GsWebView) func webViewDidFinishLoad(webView: GsWebView) func webView(webView: GsWebView,didFailLoadWithError error: NSError?) } class GsWebView :NSObject,UIWebViewDelegate,WKNavigationDelegate,WKUIDelegate{ var delegate:GsWebViewDelegate? var view:UIView? init(frame:CGRect) { super.init() if #available(iOS 8.0,*) { let config = WKWebViewConfiguration() config.processPool = ProcessPool.ins.get() config.preferences = ProcessPool.ins.getPreferences() let v = WKWebView(frame: frame,configuration: config) v.navigationDelegate = self v.UIDelegate = self v.opaque = false self.view = v }else{ let v = UIWebView(frame: frame) v.delegate = self v.dataDetectorTypes = UIDataDetectorTypes.None self.view = v } } func loadRequest(url:String){ if let url = NSURL(string: url.EncodeURL()){ let request = NSURLRequest(URL: url) if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { wkweb.loadRequest(request) } } else { if let uiweb = view as? UIWebView{ uiweb.loadRequest(request) } } } } func setBackgroundColor(color:UIColor){ view?.backgroundColor = color } func getScrollView() -> UIScrollView?{ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { return wkweb.scrollView } } else { if let uiweb = view as? UIWebView{ return uiweb.scrollView } } return nil } func runJs(action:String?){ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { wkweb.runJs(action) } } else { if let uiweb = view as? UIWebView{ uiweb.runJs(action) } } } func JavaScriptGet(action:String?,callback:((AnyObject?,NSError?) -> Void)){ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { wkweb.JavaScriptGet(action,callback: callback) return } } else { if let uiweb = view as? UIWebView{ let str = uiweb.JavaScriptGet(action) callback(str,nil) return } } callback(nil,nil) } func getTitle() -> String{ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { return wkweb.getTitle() } } else { if let uiweb = view as? UIWebView{ return uiweb.getTitle() } } return Strings.empty } func reloadFromOrigin(){ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { wkweb.reloadFromOrigin() } } else { if let uiweb = view as? UIWebView{ uiweb.reload() } } } func reload(){ self.reload(nil) } func reload(optionalUrl:String?){ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { if(wkweb.URL == nil && optionalUrl != nil){ loadRequest(optionalUrl!) return } wkweb.reload() } } else { if let uiweb = view as? UIWebView{ uiweb.reload() } } } func canGoBack() -> Bool{ if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { return wkweb.canGoBack } } else { if let uiweb = view as? UIWebView{ return uiweb.canGoBack } } return false } func goBack() { if #available(iOS 8.0,*) { if let wkweb = view as? WKWebView { wkweb.goBack() } } else { if let uiweb = view as? UIWebView{ uiweb.goBack() } } } //call on viewDidAppear //for wkwebview'bug : Blank screen after running app for a while var ensure:Int = 0 func ensurePageValid(){ if #available(iOS 8.0,*) { ensure++ if(ensure > 1){ JavaScriptGet("document.body.children.length > 0",callback: {(data,error) -> Void in var isVaild = true if(error == nil && data != nil){ if let vaild = data as? Bool{ isVaild = vaild } }else if(error != nil){ if(error?.domain == WKErrorDomain && error?.code == 1){ isVaild = false } } if(!isVaild){ NSOperationQueue.mainQueue().addOperationWithBlock({()-> Void in self.reloadFromOrigin() }) } }) } }else{ //ignore it } } ///////////// func webView(webView: UIWebView,shouldStartLoadWithRequest request: NSURLRequest,navigationType: UIWebViewNavigationType) -> Bool{ if delegate != nil,let url = request.URL?.description.DecodeURL(){ return delegate!.webView(self,shouldStartLoadWithRequest: url) } return true } @available(iOS 8.0,*) func webView(webView: WKWebView,decidePolicyForNavigationAction navigationAction: WKNavigationAction,decisionHandler: (WKNavigationActionPolicy) -> Void){ var policy = WKNavigationActionPolicy.Allow if delegate != nil,let url = navigationAction.request.URL?.debugDescription.DecodeURL(){ if(!delegate!.webView(self,shouldStartLoadWithRequest: url)){ policy = WKNavigationActionPolicy.Cancel } } decisionHandler(policy) } ////////////// func webViewDidStartLoad(webView: UIWebView){ if delegate != nil{ delegate?.webViewDidStartLoad(self) } } @available(iOS 8.0,didCommitNavigation navigation: WKNavigation!){ if delegate != nil{ delegate?.webViewDidStartLoad(self) } } //////////// func webViewDidFinishLoad(webView: UIWebView){ if delegate != nil{ delegate?.webViewDidFinishLoad(self) } } @available(iOS 8.0,didFinishNavigation navigation: WKNavigation!){ if delegate != nil{ delegate?.webViewDidFinishLoad(self) } } ////////////// func webView(webView: UIWebView,didFailLoadWithError error: NSError?){ if delegate != nil{ delegate?.webView(self,didFailLoadWithError: error) } } @available(iOS 8.0,didFailNavigation navigation: WKNavigation!,withError error: NSError){ if delegate != nil{ delegate?.webView(self,didFailProvisionalNavigation navigation: WKNavigation!,didFailLoadWithError: error) } } }
@available(iOS 8.0,*) extension WKWebView{ func JavaScriptGet(jscode:String?,NSError?) -> Void)?) -> String?{ if let js = jscode{ self.evaluateJavaScript("javascript:" + js,completionHandler: callback) } return nil } func runJs(jscode:String?){ if let js = jscode{ self.evaluateJavaScript("javascript:" + js,completionHandler: {(error,data) -> Void in }) } } func getTitle() -> String{ return self.title == nil ? Strings.empty : self.title! } } extension UIWebView{ func JavaScriptGet(jscode:String?) -> String?{ if let js = jscode{ return self.stringByEvaluatingJavaScriptFromString("javascript:" + js) } return nil } func runJs(jscode:String?){ if let js = jscode{ self.stringByEvaluatingJavaScriptFromString("javascript:" + js) } } func getTitle() -> String{ if let title = stringByEvaluatingJavaScriptFromString("document.title"){ return title } return Strings.empty } }
// // ProcessPool.swift // iosclient // // Created by Yeshen on 16/9/2. // Copyright © 2016年 L. All rights reserved. // import Foundation import WebKit @available(iOS 8.0,*) class LandowProcessPool { private var pool:WKProcessPool? private var preferences:WKPreferences? class var ins: ProcessPool{ struct Static { static var onceToken:dispatch_once_t = 0 static var instance:ProcessPool? = nil } dispatch_once(&Static.onceToken){ Static.instance = ProcessPool() } return Static.instance! } func get() -> WKProcessPool{ if(pool != nil){ return pool! } pool = WKProcessPool() return pool! } func getPreferences() ->WKPreferences{ if(preferences != nil){ return preferences! } preferences = WKPreferences() return preferences! } }
此外,在Target general 的linked frameworks and libraries中要引入webkit.framework
以上,后面有问题再补充修改