iOS Runtime 實(shí)踐(1)

iOS Runtime

很多時候我們都在看iOS開發(fā)中的黑魔法——Runtime。懂很多,但如何實(shí)踐卻少有人提及。本文便是iOS Runtime的實(shí)踐第一篇。

WebView

我們這次的實(shí)踐主題,是使用針對接口編程的方式,借助Excalibur系統(tǒng),來達(dá)到動態(tài)切換UIWebViewWKWebkit的目的。
??為什么要動態(tài)切換?其實(shí)我們眾所周知,Apple的UIWebView存在巨大的內(nèi)存泄漏。當(dāng)網(wǎng)頁內(nèi)容較復(fù)雜,圖片較大時,經(jīng)常會出現(xiàn)150MB+的內(nèi)存占用率;并且這個內(nèi)存占用率會一直存在無法消除。雖然StackOverflow上有很多大神想出了各種方式,但作用卻很小。
??Apple 從 iOS8 開始,推出了更新、優(yōu)化更好的WKWebkit。這個庫是UIWebView的繼承者,在相同的瀏覽頁面下,WKWebKit提供的WKWebView的內(nèi)存占用率甚至可以只有UIWebView1/10。可惜的是,我們很多時候?yàn)榱吮WC用戶的覆蓋率,target iOS Version都是 iOS7。這時候我們就需要使用UIWebView來達(dá)到顯示的目的。
那么問題來了,如何實(shí)現(xiàn)根據(jù)iOS版本來達(dá)到動態(tài)加載的目的呢?

Excalibur

Excalibur是我們用來映射和字符串scheme對應(yīng)關(guān)系的類。通過注冊scheme對應(yīng)的類,來達(dá)到目的。
??注冊一個類:

+ (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時期才確定。

針對接口編程

在設(shè)計(jì)模式上,我們經(jīng)常聽到說,要針對接口編程。那么在iOS開發(fā)中,怎樣才算是針對接口編程呢?這個又有什么好處呢?
??在Objective-C語言中,我們一般認(rèn)為Protocol便是接口功能的協(xié)議。
??這里,我們想達(dá)到的目的,是在不同的iOS版本下,調(diào)用不同的Webkit來進(jìn)行網(wǎng)頁渲染。而網(wǎng)頁的渲染一般放在一個ViewController下,因此我們可以針對這個需求,制訂一個用來渲染指定URLViewController接口:

@protocol DWKProtocol <NSObject>

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

@end

這里的接口,返回一個ViewController,該VC可以用來打開url網(wǎng)頁。
??現(xiàn)在我們可以寫兩個ViewController,分別是DWKWebViewControllerDWKWebkitViewController;其中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以下時,DWKWebViewController就會是DWK_MODULE_WEB_VC模塊的實(shí)現(xiàn)者;而在8.0及其以上時,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_VCViewController肯定會實(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á)到動態(tài)加載UIWebViewWKWebkit的目的達(dá)成。

代碼鏈接

我把代碼放到了Github上,希望對你有所幫助:

https://github.com/DemoMania/dynamicWebkit

如果有問題,還請留言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容