iOS Runtime 實踐(1)

不少時候咱們都在看iOS開發中的黑魔法——Runtime。懂不少,但如何實踐卻少有人說起。本文即是iOS Runtime的實踐第一篇。git

 

WebViewgithub

 

咱們此次的實踐主題,是使用針對接口編程的方式,藉助Excalibur系統,來達到動態切換UIWebView和WKWebkit的目的。web

爲何要動態切換?其實咱們衆所周知,Apple的UIWebView存在巨大的內存泄漏。當網頁內容較複雜,圖片較大時,常常會出現150MB+的內存佔用率;而且這個內存佔用率會一直存在沒法消除。雖然StackOverflow上有不少大神想出了各類方式,但做用卻很小。編程

Apple 從 iOS8 開始,推出了更新、優化更好的WKWebkit。這個庫是UIWebView的繼承者,在相同的瀏覽頁面下,WKWebKit提供的WKWebView的內存佔用率甚至能夠只有UIWebView的1/10。惋惜的是,咱們不少時候爲了保證用戶的覆蓋率,target iOS Version都是 iOS7。這時候咱們就須要使用UIWebView來達到顯示的目的。設計模式

那麼問題來了,如何實現根據iOS版原本達到動態加載的目的呢?優化

 

Excaliburatom

 

Excalibur是咱們用來映射類和字符串scheme對應關係的類。經過註冊scheme對應的類,來達到目的。url

註冊一個類:spa

 

+ (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上,但願對你有所幫助:

 

https://github.com/DemoMania/dynamicWebkit

相關文章
相關標籤/搜索