iOS Runtime
很多時(shí)候我們都在看iOS開發(fā)中的黑魔法——Runtime跪楞。懂很多闪金,但如何實(shí)踐卻少有人提及。本文便是iOS Runtime的實(shí)踐第一篇菇爪。
WebView
我們這次的實(shí)踐主題唠梨,是使用針對接口編程的方式袋励,借助Excalibur
系統(tǒng),來達(dá)到動(dòng)態(tài)切換UIWebView
和WKWebkit
的目的当叭。
??為什么要?jiǎng)討B(tài)切換茬故?其實(shí)我們眾所周知,Apple的UIWebView
存在巨大的內(nèi)存泄漏蚁鳖。當(dāng)網(wǎng)頁內(nèi)容較復(fù)雜磺芭,圖片較大時(shí),經(jīng)常會(huì)出現(xiàn)150MB+的內(nèi)存占用率醉箕;并且這個(gè)內(nèi)存占用率會(huì)一直存在無法消除钾腺。雖然StackOverflow上有很多大神想出了各種方式,但作用卻很小讥裤。
??Apple 從 iOS8
開始垮庐,推出了更新、優(yōu)化更好的WKWebkit
坞琴。這個(gè)庫是UIWebView
的繼承者,在相同的瀏覽頁面下逗抑,WKWebKit
提供的WKWebView
的內(nèi)存占用率甚至可以只有UIWebView
的1/10
剧辐。可惜的是邮府,我們很多時(shí)候?yàn)榱吮WC用戶的覆蓋率荧关,target iOS Version
都是 iOS7
。這時(shí)候我們就需要使用UIWebView
來達(dá)到顯示的目的褂傀。
那么問題來了忍啤,如何實(shí)現(xiàn)根據(jù)iOS
版本來達(dá)到動(dòng)態(tài)加載的目的呢?
Excalibur
Excalibur
是我們用來映射類
和字符串scheme
對應(yīng)關(guān)系的類。通過注冊scheme
對應(yīng)的類同波,來達(dá)到目的鳄梅。
??注冊一個(gè)類:
+ (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
,我們使用哪個(gè)類
未檩,就可以在Runtime
時(shí)期才確定戴尸。
針對接口編程
在設(shè)計(jì)模式上,我們經(jīng)常聽到說冤狡,要針對接口編程
孙蒙。那么在iOS開發(fā)中,怎樣才算是針對接口編程
呢悲雳?這個(gè)又有什么好處呢挎峦?
??在Objective-C
語言中,我們一般認(rèn)為Protocol
便是接口
功能的協(xié)議合瓢。
??這里坦胶,我們想達(dá)到的目的,是在不同的iOS
版本下歪玲,調(diào)用不同的Webkit
來進(jìn)行網(wǎng)頁渲染迁央。而網(wǎng)頁的渲染一般放在一個(gè)ViewController
下,因此我們可以針對這個(gè)需求滥崩,制訂一個(gè)用來渲染指定URL
的ViewController
接口:
@protocol DWKProtocol <NSObject>
+ (instancetype)webViewControllerForUrl:(NSURL *)url;
@end
這里的接口岖圈,返回一個(gè)ViewController
,該VC
可以用來打開url
網(wǎng)頁钙皮。
??現(xiàn)在我們可以寫兩個(gè)ViewController
蜂科,分別是DWKWebViewController
和DWKWebkitViewController
;其中DWKWebViewController
使用UIWebView
來渲染網(wǎng)頁:
@interface DWKWebViewController ()
@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) NSURL *url;
@end
而 DWKWebkitViewController
則使用WKWebView
來渲染網(wǎng)頁:
@interface DWKWebkitViewController ()
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) NSURL *url;
@end
接下來短条,二者在Runtime
的初始化階段向Excalibur
注冊自己:
DWKWebViewController
+ (void)load {
if (iOSVersion < 8.0) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[Excalibur registerScheme:DWK_MODULE_WEB_VC forClass:[self class]];
});
}
}
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以下時(shí),DWKWebViewController
就會(huì)是DWK_MODULE_WEB_VC
模塊的實(shí)現(xiàn)者茸时;而在8.0及其以上時(shí)贡定,DWKWebkitViewController
則是DWK_MODULE_WEB_VC
模塊的實(shí)現(xiàn)者。
調(diào)用
做好了以上兩步準(zhǔn)備可都,接下來便是調(diào)用DWK_MODULE_WEB_VC
的模塊來渲染網(wǎng)頁了缓待。
這里,我們已經(jīng)約定好渠牲,實(shí)現(xiàn)DWK_MODULE_WEB_VC
的ViewController
肯定會(huì)實(shí)現(xiàn)DWKProtocol
旋炒,因此我們可以這樣來獲取我們想要的ViewController Class
:
Class <DWKProtocol> webViewControllerClass = [Excalibur classForScheme:DWK_MODULE_WEB_VC];
UIViewController<DWKProtocol> *webViewController = [webViewControllerClass webViewControllerForUrl:[NSURL URLWithString:@"www.baidu.com"]];
總結(jié)
至此,使用Runtime
達(dá)到動(dòng)態(tài)加載UIWebView
和WKWebkit
的目的達(dá)成签杈。
代碼鏈接
我把代碼放到了Github
上瘫镇,希望對你有所幫助:
https://github.com/DemoMania/dynamicWebkit
如果有問題,還請留言。