您的位置:首页 > 移动开发 > IOS开发

iOS Runtime 实践(1)

2016-07-06 10:47 351 查看
很多时候我们都在看iOS开发中的黑魔法——Runtime。懂很多,但如何实践却少有人提及。本文便是iOS Runtime的实践第一篇。

WebView

我们这次的实践主题,是使用针对接口编程的方式,借助Excalibur系统,来达到动态切换UIWebView和WKWebkit的目的。

为什么要动态切换?其实我们众所周知,Apple的UIWebView存在巨大的内存泄漏。当网页内容较复杂,图片较大时,经常会出现150MB+的内存占用率;并且这个内存占用率会一直存在无法消除。虽然StackOverflow上有很多大神想出了各种方式,但作用却很小。

Apple 从 iOS8 开始,推出了更新、优化更好的WKWebkit。这个库是UIWebView的继承者,在相同的浏览页面下,WKWebKit提供的WKWebView的内存占用率甚至可以只有UIWebView的1/10。可惜的是,我们很多时候为了保证用户的覆盖率,target iOS Version都是 iOS7。这时候我们就需要使用UIWebView来达到显示的目的。

那么问题来了,如何实现根据iOS版本来达到动态加载的目的呢?

Excalibur

Excalibur是我们用来映射类和字符串scheme对应关系的类。通过注册scheme对应的类,来达到目的。

注册一个类:


+ (void)registerScheme:(nonnull NSString *)scheme

forClass:(nonnull __unsafe_unretained Class)aClass {

NSParameterAssert(scheme);

NSParameterAssert(aClass);

if ([Excalibur classForScheme:scheme]) {

[NSException raise:@"Scheme Already Exists"

format:@"'%@' Scheme Already Exists", scheme];

return;

}

if (![aClass isSubclassOfClass:[NSObject class]]) {

[NSException raise:@"Wrong Class Type"

format:@"Class should inherit from NSObject"];

return;

}

if ([scheme isEqualToString:@""]) {

[NSException raise:@"Scheme Wrong"

format:@"Scheme should not be blank"];

return;

}

[sharedInstance addScheme:scheme forClass:aClass];

}



从Excalibur中获取scheme指定的类:


+ (nullable __unsafe_unretained Class)classForScheme:(nonnull NSString *)scheme {

return [sharedInstance.mapTable objectForKey:scheme];

}



通过Excalibur,我们使用哪个类,就可以在Runtime时期才确定。

针对接口编程

在设计模式上,我们经常听到说,要针对接口编程。那么在iOS开发中,怎样才算是针对接口编程呢?这个又有什么好处呢?

在Objective-C语言中,我们一般认为Protocol便是接口功能的协议。

这里,我们想达到的目的,是在不同的iOS版本下,调用不同的Webkit来进行网页渲染。而网页的渲染一般放在一个ViewController下,因此我们可以针对这个需求,制订一个用来渲染指定URL的ViewController接口:


@protocol DWKProtocol

+ (instancetype)webViewControllerForUrl:(NSURL *)url;

@end



这里的接口,返回一个ViewController,该VC可以用来打开url网页。

现在我们可以写两个ViewController,分别是DWKWebViewController和DWKWebkitViewController;其中DWKWebViewController使用UIWebView来渲染网页:


@interface DWKWebViewController ()

@property (nonatomic, strong) UIWebView *webView;

@property (nonatomic, strong) NSURL *url;

@end



而 DWKWebkitViewController则使用WKWebView来渲染网页:


@interface DWKWebkitViewController ()

@property (nonatomic, strong) WKWebView *webView;

@property (nonatomic, strong) NSURL *url;

@end



接下来,二者在Runtime的初始化阶段向Excalibur注册自己:

DWKWebViewController


+ (void)load {

if (iOSVersion



DWKWebkitViewController


+ (void)load {

if (iOSVersion >= 8.0) {

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

[Excalibur registerScheme:DWK_MODULE_WEB_VC forClass:[self class]];

});

}

}



那么,8.0以下时,DWKWebViewController就会是DWK_MODULE_WEB_VC模块的实现者;而在8.0及其以上时,DWKWebkitViewController则是DWK_MODULE_WEB_VC模块的实现者。

调用

做好了以上两步准备,接下来便是调用DWK_MODULE_WEB_VC的模块来渲染网页了。

这里,我们已经约定好,实现DWK_MODULE_WEB_VC的ViewController肯定会实现DWKProtocol,因此我们可以这样来获取我们想要的ViewController Class:


Class webViewControllerClass = [Excalibur classForScheme:DWK_MODULE_WEB_VC];

UIViewController *webViewController = [webViewControllerClass webViewControllerForUrl:[NSURL URLWithString:@"www.baidu.com"]];



总结

至此,使用Runtime达到动态加载UIWebView和WKWebkit的目的达成。

代码链接

我把代码放到了Github上,希望对你有所帮助:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: