MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的UIWebView与WKWebView使用对比

2022-04-184.8k 阅读

UIWebView简介

UIWebView是iOS开发中用于在应用内显示网页内容的一个视图类,它是iOS早期版本中加载和展示网页内容的主要方式。UIWebView基于WebKit引擎,能够渲染HTML、CSS和JavaScript等网页相关的内容。它的使用相对简单,对于简单的网页加载需求,开发者可以轻松上手。

UIWebView的基本使用

在Objective-C中,使用UIWebView加载一个网页非常直观。首先,需要在视图控制器的头文件中引入UIWebView的头文件,并声明一个UIWebView的属性:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (nonatomic, strong) UIWebView *webView;

@end

在实现文件中,初始化并配置UIWebView:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:self.webView];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

@end

上述代码中,我们创建了一个UIWebView实例,并设置其大小为视图控制器视图的边界。然后构造一个NSURLRequest对象,请求目标网址,并使用loadRequest:方法加载网页。

UIWebView与JavaScript交互

UIWebView支持与网页中的JavaScript进行交互。例如,假设网页中有一个按钮,点击按钮后调用JavaScript函数,该函数需要将信息传递给原生Objective-C代码。首先,在HTML中添加按钮和JavaScript函数:

<!DOCTYPE html>
<html>
<head>
    <title>UIWebView Example</title>
</head>
<body>
    <button onclick="callObjectiveC()">Click Me</button>
    <script>
        function callObjectiveC() {
            window.webkit.messageHandlers.nativeFunction.postMessage('Hello from JavaScript');
        }
    </script>
</body>
</html>

在Objective-C端,需要设置UIWebView的代理,实现代理方法来接收JavaScript传递的消息:

#import "ViewController.h"

@interface ViewController () <UIWebViewDelegate>

@property (nonatomic, strong) UIWebView *webView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    self.webView.delegate = self;
    [self.view addSubview:self.webView];
    
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *requestString = [[request URL] absoluteString];
    if ([requestString hasPrefix:@"native://"]) {
        NSString *message = [requestString stringByReplacingOccurrencesOfString:@"native://" withString:@""];
        NSLog(@"Received from JavaScript: %@", message);
        return NO;
    }
    return YES;
}

@end

这里通过拦截特定格式的URL请求(native://开头)来接收JavaScript传递的消息。

WKWebView简介

WKWebView是iOS 8.0引入的新一代网页视图,它同样基于WebKit引擎,但在性能、功能和安全性等方面都有显著提升。WKWebView采用了多进程架构,使得网页加载和渲染更加流畅,并且对内存的管理也更加高效。

WKWebView的基本使用

使用WKWebView同样需要引入相应的头文件,并声明属性:

#import <WebKit/WebKit.h>
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (nonatomic, strong) WKWebView *webView;

@end

在实现文件中初始化和配置WKWebView:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:self.webView];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

@end

与UIWebView类似,构造请求并加载网页,但WKWebView需要先配置一个WKWebViewConfiguration对象。

WKWebView与JavaScript交互

WKWebView与JavaScript的交互方式与UIWebView有所不同,更加灵活和强大。例如,要实现与上述UIWebView类似的JavaScript调用原生代码的功能。首先,在Objective-C端注册一个JavaScript脚本消息处理程序:

#import "ViewController.h"

@interface ViewController () <WKScriptMessageHandler>

@property (nonatomic, strong) WKWebView *webView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    WKUserContentController *userContentController = [WKUserContentController new];
    [userContentController addScriptMessageHandler:self name:@"nativeFunction"];
    configuration.userContentController = userContentController;
    
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:self.webView];
    
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"Received from JavaScript: %@", message.body);
}

@end

在HTML中,调用该处理程序:

<!DOCTYPE html>
<html>
<head>
    <title>WKWebView Example</title>
</head>
<body>
    <button onclick="callObjectiveC()">Click Me</button>
    <script>
        function callObjectiveC() {
            window.webkit.messageHandlers.nativeFunction.postMessage('Hello from JavaScript');
        }
    </script>
</body>
</html>

WKWebView通过WKUserContentController来管理与JavaScript的交互,注册脚本消息处理程序后,JavaScript可以通过window.webkit.messageHandlers来调用原生代码。

性能对比

加载速度

在加载速度方面,WKWebView通常比UIWebView更快。这是因为WKWebView采用了多进程架构,能够并行处理网页的资源加载和渲染。例如,在加载一个包含大量图片和JavaScript的复杂网页时,UIWebView可能会出现短暂的卡顿,而WKWebView能够更流畅地加载并显示网页内容。

我们可以通过一个简单的测试来验证这一点。创建两个视图控制器,分别使用UIWebView和WKWebView加载同一个复杂网页,例如包含多个图片、CSS样式和JavaScript脚本的网页。通过记录从发起请求到网页完全加载完成的时间来对比两者的加载速度。

在UIWebView的视图控制器中:

#import "UIWebViewController.h"

@interface UIWebViewController () <UIWebViewDelegate>

@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) NSDate *startDate;

@end

@implementation UIWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    self.webView.delegate = self;
    [self.view addSubview:self.webView];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com/complex-page"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    self.startDate = [NSDate date];
    [self.webView loadRequest:request];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSTimeInterval duration = -[self.startDate timeIntervalSinceNow];
    NSLog(@"UIWebView loading time: %f seconds", duration);
}

@end

在WKWebView的视图控制器中:

#import "WKWebViewController.h"

@interface WKWebViewController () <WKNavigationDelegate>

@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) NSDate *startDate;

@end

@implementation WKWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    self.webView.navigationDelegate = self;
    [self.view addSubview:self.webView];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com/complex-page"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    self.startDate = [NSDate date];
    [self.webView loadRequest:request];
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSTimeInterval duration = -[self.startDate timeIntervalSinceNow];
    NSLog(@"WKWebView loading time: %f seconds", duration);
}

@end

通过实际测试,通常会发现WKWebView的加载时间明显短于UIWebView。

内存占用

WKWebView在内存管理方面也优于UIWebView。由于UIWebView是单进程架构,当加载多个网页或者复杂网页时,容易导致内存占用过高,甚至可能引发应用程序的内存警告。而WKWebView的多进程架构使得每个网页内容在独立的进程中加载和渲染,减少了对主应用程序内存的压力。

同样可以通过一个测试来观察两者的内存占用情况。创建一个应用,在其中交替使用UIWebView和WKWebView加载多个不同的网页,然后使用 Instruments 工具来监测应用的内存使用情况。在 Instruments 中,选择“Allocations”模板,运行应用并观察内存占用曲线。

在加载相同数量和复杂度的网页时,会发现UIWebView导致的内存增长曲线较为陡峭,而WKWebView的内存增长相对平缓,并且在网页关闭后,WKWebView能够更有效地释放内存,使内存占用回到较低水平。

功能特性对比

支持的HTML5特性

WKWebView对HTML5特性的支持更加全面和完善。例如,WKWebView能够更好地支持HTML5的视频播放、地理位置定位、WebGL等功能。在使用UIWebView时,对于一些较新的HTML5特性可能会出现兼容性问题,或者需要额外的处理才能正常使用。

以HTML5视频播放为例,在WKWebView中,直接加载包含视频标签的网页,视频能够正常播放,并且支持全屏播放等功能。而在UIWebView中,可能需要一些额外的配置和处理才能实现同样的效果。假设网页中有如下视频标签:

<video width="320" height="240" controls>
    <source src="movie.mp4" type="video/mp4">
    Your browser does not support the video tag.
</video>

在WKWebView的视图控制器中加载该网页:

#import "WKWebViewController.h"

@interface WKWebViewController ()

@property (nonatomic, strong) WKWebView *webView;

@end

@implementation WKWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:self.webView];
    
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"video" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

@end

视频能够正常播放。而在UIWebView中,可能需要设置一些属性,如:

#import "UIWebViewController.h"

@interface UIWebViewController () <UIWebViewDelegate>

@property (nonatomic, strong) UIWebView *webView;

@end

@implementation UIWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    self.webView.delegate = self;
    self.webView.mediaPlaybackRequiresUserAction = NO;
    [self.view addSubview:self.webView];
    
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"video" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

@end

即使设置了mediaPlaybackRequiresUserAction = NO,某些情况下可能还是会出现播放问题,相比之下,WKWebView的兼容性更好。

手势操作

WKWebView对手势操作的支持更加灵活和丰富。WKWebView默认支持常见的手势,如缩放、滚动等,并且开发者可以通过添加手势识别器来自定义手势操作。而UIWebView虽然也支持基本的滚动和缩放,但在自定义手势操作方面相对受限。

例如,要在WKWebView上添加一个自定义的长按手势:

#import "WKWebViewController.h"

@interface WKWebViewController ()

@property (nonatomic, strong) WKWebView *webView;

@end

@implementation WKWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:self.webView];
    
    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    [self.webView addGestureRecognizer:longPressGesture];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
    NSLog(@"Long press gesture detected on WKWebView");
}

@end

在UIWebView上添加同样的长按手势时,由于UIWebView本身对添加手势识别器的支持有限,可能需要通过一些复杂的方式,如创建一个覆盖在UIWebView上的透明视图来添加手势识别器,并且在处理手势事件时需要注意与UIWebView的交互逻辑,避免影响正常的网页操作。

安全性对比

防止跨站脚本攻击(XSS)

WKWebView在防止跨站脚本攻击方面有更好的机制。它能够更有效地过滤和阻止恶意的JavaScript代码注入。WKWebView会对网页内容进行更严格的安全检查,当检测到可能存在XSS攻击的代码时,会采取相应的措施,如阻止脚本执行。

而UIWebView虽然也有一定的安全防护机制,但相对较弱。例如,在某些情况下,UIWebView可能无法及时识别和阻止一些复杂的XSS攻击代码,使得应用存在安全风险。

假设我们有一个测试网页,其中包含一段试图进行XSS攻击的恶意JavaScript代码:

<!DOCTYPE html>
<html>
<head>
    <title>XSS Test</title>
</head>
<body>
    <script>
        document.write('<script src="http://malicious-site.com/script.js"><\/script>');
    </script>
</body>
</html>

在WKWebView中加载该网页时,WKWebView会检测到这种恶意的代码注入,并阻止脚本的执行。而在UIWebView中,可能无法有效地阻止该脚本的执行,从而导致应用受到XSS攻击的威胁。

资源加载安全策略

WKWebView提供了更细粒度的资源加载安全策略配置。开发者可以通过WKWebViewConfigurationWKNavigationDelegate方法来控制资源的加载,例如,可以阻止加载来自不信任来源的资源。

在WKWebView中,可以实现如下代理方法来控制资源加载:

#import "WKWebViewController.h"

@interface WKWebViewController () <WKNavigationDelegate>

@property (nonatomic, strong) WKWebView *webView;

@end

@implementation WKWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    self.webView.navigationDelegate = self;
    [self.view addSubview:self.webView];
    
    NSURL *url = [NSURL URLWithString:@"https://www.example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL *url = navigationAction.request.URL;
    if ([url.host isEqualToString:@"untrusted-site.com"]) {
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

@end

上述代码中,当检测到请求的资源来自untrusted-site.com时,会取消加载,从而保证应用的安全性。而UIWebView虽然也可以通过代理方法shouldStartLoadWithRequest:navigationType:来控制资源加载,但在配置的灵活性和安全性方面不如WKWebView。

兼容性与未来发展

系统版本兼容性

UIWebView最早在iOS 2.0引入,一直到iOS 12.0都有支持,但从iOS 13.0开始被正式弃用。这意味着在开发面向iOS 13及更高版本的应用时,不能再使用UIWebView。

而WKWebView从iOS 8.0引入,至今仍然是官方推荐的用于在应用内显示网页内容的视图类,并且随着iOS系统的不断更新,WKWebView也在持续得到优化和改进,对新的系统特性和功能有更好的支持。

因此,如果应用需要支持iOS 8.0及以上版本,WKWebView是一个更具前瞻性的选择,能够保证应用在未来的系统版本中持续正常运行。

社区支持与发展趋势

由于WKWebView是iOS开发中新一代的网页视图,社区对其的支持越来越广泛。在Stack Overflow、GitHub等开发者社区平台上,可以找到大量关于WKWebView的开发资源、开源项目和解决方案。同时,苹果官方也在不断更新WKWebView的文档和API,提供更多的功能和优化。

相比之下,UIWebView由于已经被弃用,社区对其的关注度逐渐降低,相关的开发资源和更新也越来越少。这使得开发者在使用UIWebView遇到问题时,可能较难找到有效的解决方案。

从未来发展趋势来看,随着移动应用对网页内容展示需求的不断增加,以及对性能、安全性和功能特性要求的提高,WKWebView无疑将成为iOS开发中处理网页相关功能的主流选择。开发者应尽快熟悉和掌握WKWebView的使用,以更好地开发出高质量、高性能且安全的应用程序。