最近一直在做手机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, *)
func webView(webView: WKWebView, didCommitNavigation navigation: WKNavigation!){
if delegate != nil{
delegate?.webViewDidStartLoad(self)
}
}
func webViewDidFinishLoad(webView: UIWebView){
if delegate != nil{
delegate?.webViewDidFinishLoad(self)
}
}
@available(iOS 8.0, *)
func webView(webView: WKWebView, 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, *)
func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError){
if delegate != nil{
delegate?.webView(self, didFailLoadWithError: error)
}
}
@available(iOS 8.0, *)
func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError){
if delegate != nil{
delegate?.webView(self, didFailLoadWithError: error)
}
}
}
@available(iOS 8.0, *)
extension WKWebView{
func JavaScriptGet(jscode:String?,callback:((AnyObject?, 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
以上,后面有问题再补充修改