iOS OC與JavaScript的交互

iOS OC與JavaScript的交互

概念了解

JavaScriptCore

javaScriptCore是iOS7后推出的框架诫欠,是封裝了JavaScript和Objective-C橋接的Objective-C API,我們只需要只要用很少的代碼浴栽,就可以做到JavaScript調(diào)用Objective-C荒叼,或者Objective-C調(diào)用JavaScript。

JavaScriptCore中類及協(xié)議

  • JSManagedValue:管理數(shù)據(jù)和方法的類
  • JSContent:JS執(zhí)行的環(huán)境
  • JSValue:JS和OC數(shù)據(jù)和方法的橋梁
  • JSVirtualMachine:處理線程相關(guān)典鸡,使用較少
  • JSExport:這是一個(gè)協(xié)議被廓,如果JS對(duì)象想直接調(diào)用OC對(duì)象里面的方法和屬性,那么這個(gè)OC對(duì)象只要實(shí)現(xiàn)這個(gè)JSExport協(xié)議就可以了萝玷。

代碼示例

我們先用終端創(chuàng)建個(gè)html文件拖入工程

test.html中代碼如下

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
        
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi"/>
        
        <title>JSCallOC</title>
        
        <style>
            *
            {
                //-webkit-tap-highlight-color: rgba(0,0,0,0);
                text-decoration: none;
            }
        
        html,body
        {
            -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
            -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
            -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
        }
        
        #div-a
        {
            background:#FBA;
            color:#FFF;
            
            border-radius: 25px 5px;
        }
        
        
            </style>
        
        <script type="text/javascript">
            
            function showResult(resultNumber)
            {
                //alert(resultNumber);
                document.getElementById("result").innerText = resultNumber;

            }
        
            function picCallBack(image) {
                alert(image);
            }
        
            </script>
        
</head>

<body style="background:#CDE; color:#FFF">
    
    <div>
        <font size="3" color="black">輸入一個(gè)整數(shù):</font>
        <textarea  id="input" style="font-size:10pt;color:black;"></textarea>
    </div>
    <br/>
    
    <div>
        <font size="3" color="black">結(jié)果: <b id="result"> </b> </font>
    </div>
    <br/>
    
    <div id="div-a">
        <center>
            
            <br/>
            <input type="button" value="計(jì)算階乘" onclick="native.calculateForJS(input.value);" />
            <br/>
            <br/>
            
            
            <input type="button" value="測(cè)試log" onclick="log('測(cè)試');" />
            <br/>
            <br/>
            
            <input type="button" value="oc原生Alert" onclick="alert('alert');" />
            <br/>
            <br/>
            
            <input type="button" value="addSubView" onclick="addSubView('view');" />
            <br/>
            <br/>
            
            <input type="button" value="removeSubView" onclick="removeSubView('view');" />
            <br/>
            <br/>
            
            <input type="button" value="多參數(shù)調(diào)用" onclick="mutiParams('參數(shù)1','參數(shù)2','參數(shù)3');" />
            <br/>
            <br/>
            
            <input type="button" value="獲取照片" onclick="native.callCamera()" />
            <br/>
            <br/>
            <a id="push" href="#" onclick="native.pushViewControllerTitle('SecondViewController','secondPushedFromJS');">
                push to second ViewController
            </a>
            
            <br/>
            <br/>
            
        </center>
    </div>
    
    
    
</body>

</html>
運(yùn)行效果圖

整個(gè)頁(yè)面均為HTML實(shí)現(xiàn)嫁乘,功能為:

1 計(jì)算階乘:輸入框輸入數(shù)字后調(diào)用OC中相關(guān)方法進(jìn)行計(jì)算,將計(jì)算結(jié)果顯示在HTML頁(yè)面上球碉。

2 測(cè)試log:點(diǎn)擊后蜓斧,在控制臺(tái)打印測(cè)試數(shù)據(jù)。

3 OC原生Alert:點(diǎn)擊后睁冬,彈出OC的提示框挎春。

4 addSubView:點(diǎn)擊后,在OC中添加一個(gè)View.

5 removeSubView: 點(diǎn)擊后豆拨,移除4中添加的View直奋。

6 多函數(shù)調(diào)用: 獲取HTML中的多個(gè)參數(shù)

7 獲取照片:訪問手機(jī)照片,并將選中照片顯示在HTML頁(yè)面上

8 push to Second View Controller:跳轉(zhuǎn)到下一個(gè)頁(yè)面施禾。

總結(jié):以上功能都是OC中獲取HTML按鈕中的相關(guān)點(diǎn)擊事件脚线,然后在OC中執(zhí)行相關(guān)代碼。

ViewController.m中代碼如下

#import "OneViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import "SecondViewController.h"

@protocol TestJSExport <JSExport>
/*
 OC的函數(shù)命名和JS函數(shù)命名規(guī)則不同 我們可以通過JSExportAs這個(gè)宏優(yōu)化JS中調(diào)用的名稱
 這個(gè)宏只對(duì)有參數(shù)的selector起作用
 handleFactorialCalculateWithNumber:(NSNumber *)number作為 js方法:calculateForJS的別名*/
JSExportAs
(calculateForJS, - (void)handleFactorialCalculateWithNumber:(NSNumber *)number);
//- (void)calculateForJS:(NSNumber *)number;
//js方法
- (void)pushViewController:(NSString *)view title:(NSString *)title;
- (void)callCamera;

@end

@interface OneViewController ()<UIWebViewDelegate, TestJSExport>

@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) JSContext *context;//給JavaScript提供運(yùn)行的上下文環(huán)境
@property (nonatomic, strong) UIView *addView;

@end

@implementation OneViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.view addSubview:self.webView];
    
    NSString *path = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"test.html"];
    NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [_webView loadHTMLString:htmlString baseURL:nil];
    
}

- (UIWebView *)webView {
    if (_webView == nil) {
        _webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
        _webView.delegate = self;
    }
    return _webView;
}

- (UIView *)addView {
    if (_addView == nil) {
        _addView =[[UIView alloc] initWithFrame:CGRectMake(10, 550, 200, 100)];
        _addView.backgroundColor = [UIColor cyanColor];
    }
    return _addView;
}

#pragma mark UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    //將 html的title 設(shè)置為controller的title
    self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    //獲取當(dāng)前頁(yè)面的url
    NSString *url = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];
    //這個(gè)好像是私有屬性 審核時(shí)可能被蘋果拒絕
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //打印異常,由于JS的異常信息是不會(huì)在OC中被直接打印的,所以我們?cè)谶@里添加打印異常信息
    self.context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"exceptionValue --- %@",exceptionValue);
    };
    //以 JSExport 協(xié)議關(guān)聯(lián) native方法
    self.context[@"native"] = self;
    //以 block 形式關(guān)聯(lián) JavaScript function
    self.context[@"log"] = ^(NSString *str) {
        NSLog(@"%@",str);
    };
    //以 block 形式關(guān)聯(lián) JavaScript function
    self.context[@"alert"] = ^(NSString *str) {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"msg from js" message:str delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
            [alter show];
        });
    };
    //弱引用 避免循環(huán)引用
    __block typeof(self) weakSelf = self;
    self.context[@"addSubView"] = ^(NSString *viewName) {
        [weakSelf.view addSubview:weakSelf.addView];
    };
    
    self.context[@"removeSubView"] = ^(NSString *viewName) {
        [weakSelf.addView removeFromSuperview];
    };
    //多參數(shù)
    self.context[@"mutiParams"] = ^(NSString *a, NSString *b, NSString *c) {
        NSLog(@"%@ %@ %@",a,b,c);
    };
}

#pragma mark - JSExport Methods
- (void)handleFactorialCalculateWithNumber:(NSNumber *)number{
    NSLog(@"%@", number);
    NSNumber *result = [self calculateFactorialOfNumber:number];
    NSLog(@"%@", result);
    [self.context[@"showResult"] callWithArguments:@[result]];
}

- (void)pushViewController:(NSString *)view title:(NSString *)title{
    Class second = NSClassFromString(view);
    id secondVC = [[second alloc]init];
    ((UIViewController*)secondVC).title = title;
    [self.navigationController pushViewController:secondVC animated:YES];
}

//  假設(shè)此方法是在子線程中執(zhí)行的拾积,線程名sub-thread
- (void)callCamera {
    // 這句假設(shè)要在主線程中執(zhí)行殉挽,線程名main-thread
    NSLog(@"callCamera");
    
    // 下面這兩句代碼最好還是要在子線程sub-thread中執(zhí)行啊
    JSValue *picCallback = self.context[@"picCallBack"];
    [picCallback callWithArguments:@[@"photos"]];
}

- (void)calculateForJS:(NSNumber *)number {
    NSLog(@"點(diǎn)擊了計(jì)算階乘");
    
    JSValue *showResult = self.context[@"showResult"];
    [showResult callWithArguments:@[@"計(jì)算階乘"]];
    
}

#pragma mark - Factorial Method
- (NSNumber *)calculateFactorialOfNumber:(NSNumber *)number{
    NSInteger i = [number integerValue];
    if (i < 0){
        return [NSNumber numberWithInteger:0];
    }
    if (i == 0){
        return [NSNumber numberWithInteger:1];
    }
    NSInteger r = (i * [(NSNumber *)[self calculateFactorialOfNumber:[NSNumber numberWithInteger:(i - 1)]] integerValue]);
    
    return [NSNumber numberWithInteger:r];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.context[@"native"] = nil;
}


@end

獲取HTML中的點(diǎn)擊事件

在HTML中丰涉,為一個(gè)元素添加點(diǎn)擊事件的兩種方法

第一種

<input type="button" value="計(jì)算階乘" onclick="native.calculateForJS(input.value);" />

在JS交互中,很多事情都是在webView的delegate方法中完成的斯碌,通過JSContent創(chuàng)建一個(gè)使用JS的環(huán)境一死,所以這里,我們先將self.content在這里面初始化;

#pragma mark UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    //這個(gè)好像是私有屬性 審核時(shí)可能被蘋果拒絕
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //打印異常,由于JS的異常信息是不會(huì)在OC中被直接打印的,所以我們?cè)谶@里添加打印異常信息
    self.context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"exceptionValue --- %@",exceptionValue);
    };
    //以JSExport 協(xié)議關(guān)聯(lián) native 的方法
    self.context[@"native"] = self;
}

我們需要聲明一個(gè)集成JSExport協(xié)議傻唾,協(xié)議中聲明JS使用的OC方法

@protocol TestJSExport <JSExport>
/*
 OC的函數(shù)命名和JS函數(shù)命名規(guī)則不同 我們可以通過JSExportAs這個(gè)宏優(yōu)化JS中調(diào)用的名稱
 這個(gè)宏只對(duì)有參數(shù)的selector起作用
 handleFactorialCalculateWithNumber:(NSNumber *)number作為 js方法:calculateForJS的別名*/
JSExportAs
(calculateForJS, - (void)handleFactorialCalculateWithNumber:(NSNumber *)number);
@end

當(dāng)然你也可以按下面的寫法

@protocol TestJSExport <JSExport>

- (void)calculateForJS:(NSNumber *)number;

@end

第二種

<input type="button" value="oc原生Alert" onclick="alert('alert');" />

這種我們需要使用block的形式關(guān)聯(lián)JavaScript function

self.context[@"alert"] = ^(NSString *str) {

};

對(duì)HTML中的事件進(jìn)行處理

第一種 協(xié)議形式

我們協(xié)議中制定的方法名一定要和HTML中的方法名相同投慈。
當(dāng)我們協(xié)議需要使用JS中的方法時(shí),用下面的代碼進(jìn)行調(diào)用:

HTML中的方法

function showResult(resultNumber)
{
document.getElementById("result").innerText = resultNumber;

}

OC調(diào)用

JSValue *showResult = self.context[@"showResult"];
[showResult callWithArguments:@[@"計(jì)算階乘"]];

第二種 Block形式

注意避免循環(huán)引用冠骄,同時(shí)刷新UI的工作應(yīng)該放到主線程

self.context[@"alert"] = ^(NSString *str) {
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertView *alter = [[UIAlertView alloc] initWithTitle:@"msg from js" message:str delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
        [alter show];
    });
};

使用注意

OC調(diào)用JavaScript是同步伪煤,JavaScript調(diào)用OC是異步

JavaScript調(diào)用本地方法是在子線程中執(zhí)行的凛辣,這里要根據(jù)實(shí)際情況考慮線程之間的切換抱既,而在回調(diào)JavaScript方法的時(shí)候最好是在剛開始調(diào)用此方法的線程中去執(zhí)行那段JavaScript方法的代碼,看下面的代碼解釋:

// 假設(shè)此方法是在子線程中執(zhí)行的扁誓,線程名sub-thread
- (void)callCamera {
    // 這句假設(shè)要在主線程中執(zhí)行防泵,線程名main-thread
    NSLog(@"callCamera");
    
    // 下面這兩句代碼最好還是要在子線程sub-thread中執(zhí)行啊
    JSValue *picCallback = self.context[@"picCallBack"];
    [picCallback callWithArguments:@[@"photos"]];
}

本文demo: 點(diǎn)我下載

內(nèi)存管理陷阱

Objective-C的內(nèi)存管理機(jī)制是引用計(jì)數(shù),JavaScript的內(nèi)存管理機(jī)制是垃圾回收蝗敢。在大部分情況下捷泞,JavaScriptCore能做到在這兩種內(nèi)存管理機(jī)制之間無(wú)縫無(wú)錯(cuò)轉(zhuǎn)換,但也有少數(shù)情況需要特別注意寿谴。

在block內(nèi)捕獲JSContext

Block會(huì)為默認(rèn)為所有被它捕獲的對(duì)象創(chuàng)建一個(gè)強(qiáng)引用锁右。JSContext為它管理的所有JSValue也都擁有一個(gè)強(qiáng)引用。并且讶泰,JSValue會(huì)為它保存的值和它所在的Context都維持一個(gè)強(qiáng)引用咏瑟。這樣JSContext和JSValue看上去是循環(huán)引用的,然而并不會(huì)峻厚,垃圾回收機(jī)制會(huì)打破這個(gè)循環(huán)引用响蕴。
看下面的例子:

self.context[@"getVersion"] = ^{
    NSString *versionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

    versionString = [@"version " stringByAppendingString:versionString];

    JSContext *context = [JSContext currentContext]; // 這里不要用self.context
    JSValue *version = [JSValue valueWithObject:versionString inContext:context];

    return version;
};

使用[JSContext currentContext]而不是self.context來在block中使用JSContext谆焊,來防止循環(huán)引用惠桃。

JSManagedValue

當(dāng)把一個(gè)JavaScript值保存到一個(gè)本地實(shí)例變量上時(shí),需要尤其注意內(nèi)存管理陷阱辖试。 用實(shí)例變量保存一個(gè)JSValue非常容易引起循環(huán)引用辜王。

看以下下例子,自定義一個(gè)UIAlertView罐孝,當(dāng)點(diǎn)擊按鈕時(shí)調(diào)用一個(gè)JavaScript函數(shù):

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

@interface MyAlertView : UIAlertView

- (id)initWithTitle:(NSString *)title
            message:(NSString *)message
            success:(JSValue *)successHandler
            failure:(JSValue *)failureHandler
            context:(JSContext *)context;

@end

按照一般自定義AlertView的實(shí)現(xiàn)方法呐馆,MyAlertView需要持有successHandler,failureHandler這兩個(gè)JSValue對(duì)象

向JavaScript環(huán)境注入一個(gè)function

self.context[@"presentNativeAlert"] = ^(NSString *title,
                                        NSString *message,
                                        JSValue *success,
                                        JSValue *failure) {
   JSContext *context = [JSContext currentContext];
   MyAlertView *alertView = [[MyAlertView alloc] initWithTitle:title 
                                                       message:message
                                                       success:success
                                                       failure:failure
                                                       context:context];
   [alertView show];
};

因?yàn)镴avaScript環(huán)境中都是“強(qiáng)引用”(相對(duì)Objective-C的概念來說)的莲兢,這時(shí)JSContext強(qiáng)引用了一個(gè)presentNativeAlert函數(shù),這個(gè)函數(shù)中又強(qiáng)引用了MyAlertView 等于說JSContext強(qiáng)引用了MyAlertView搏讶,而MyAlertView為了持有兩個(gè)回調(diào)強(qiáng)引用了successHandler和failureHandler這兩個(gè)JSValue坯临,這樣MyAlertView和JavaScript環(huán)境互相引用了。

所以蘋果提供了一個(gè)JSMagagedValue類來解決這個(gè)問題坟岔。

看MyAlertView.m的正確實(shí)現(xiàn):

#import "MyAlertView.h"

@interface XorkAlertView() <UIAlertViewDelegate>
@property (strong, nonatomic) JSContext *ctxt;
@property (strong, nonatomic) JSMagagedValue *successHandler;
@property (strong, nonatomic) JSMagagedValue *failureHandler;
@end

@implementation MyAlertView

- (id)initWithTitle:(NSString *)title
            message:(NSString *)message
            success:(JSValue *)successHandler
            failure:(JSValue *)failureHandler
            context:(JSContext *)context {

    self = [super initWithTitle:title
                    message:message
                   delegate:self
          cancelButtonTitle:@"No"
          otherButtonTitles:@"Yes", nil];

    if (self) {
        _ctxt = context;

        _successHandler = [JSManagedValue managedValueWithValue:successHandler];
        // A JSManagedValue by itself is a weak reference. You convert it into a conditionally retained
        // reference, by inserting it to the JSVirtualMachine using addManagedReference:withOwner:
        [context.virtualMachine addManagedReference:_successHandler withOwner:self];

        _failureHandler = [JSManagedValue managedValueWithValue:failureHandler];
        [context.virtualMachine addManagedReference:_failureHandler withOwner:self];
    }
    return self;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == self.cancelButtonIndex) {
        JSValue *function = [self.failureHandler value];
        [function callWithArguments:@[]];
    } else {
        JSValue *function = [self.successHandler value];
        [function callWithArguments:@[]];
    }

    [self.ctxt.virtualMachine removeManagedReference:_failureHandler withOwner:self];
    [self.ctxt.virtualMachine removeManagedReference:_successHandler withOwner:self];
}    

@end

分析上面例子,從外部傳入的JSValue對(duì)象在類內(nèi)部使用JSManagedValue來保存摔桦。

JSManagedValue本身是一個(gè)弱引用對(duì)象社付,需要調(diào)用JSVirtualMachine的addManagedReference:withOwner:把它添加到JSVirtualMachine對(duì)象中,確保使用過程中JSValue不會(huì)被釋放

當(dāng)用戶點(diǎn)擊AlertView上的按鈕時(shí)邻耕,根據(jù)用戶點(diǎn)擊哪一個(gè)按鈕鸥咖,來執(zhí)行對(duì)應(yīng)的處理函數(shù),這時(shí)AlertView也隨即被銷毀兄世。 這時(shí)需要手動(dòng)調(diào)用removeManagedReference:withOwner:來移除JSManagedValue啼辣。

參考文章

http://www.reibang.com/p/cdaf9bc3d65d
https://hjgitbook.gitbooks.io/ios/content/04-technical-research/04-javascriptcore-note.html
http://my.oschina.net/whforever/blog/669813
http://www.reibang.com/p/f896d73c670a

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市御滩,隨后出現(xiàn)的幾起案子熙兔,更是在濱河造成了極大的恐慌,老刑警劉巖艾恼,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件住涉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钠绍,警方通過查閱死者的電腦和手機(jī)舆声,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柳爽,“玉大人媳握,你說我怎么就攤上這事×赘” “怎么了蛾找?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)赵誓。 經(jīng)常有香客問我打毛,道長(zhǎng),這世上最難降的妖魔是什么俩功? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任幻枉,我火速辦了婚禮,結(jié)果婚禮上诡蜓,老公的妹妹穿的比我還像新娘熬甫。我一直安慰自己,他們只是感情好蔓罚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布椿肩。 她就那樣靜靜地躺著瞻颂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郑象。 梳的紋絲不亂的頭發(fā)上蘸朋,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音扣唱,去河邊找鬼藕坯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛噪沙,可吹牛的內(nèi)容都是我干的炼彪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼正歼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼辐马!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起局义,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤喜爷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后萄唇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檩帐,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年另萤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湃密。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡四敞,死狀恐怖泛源,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忿危,我是刑警寧澤达箍,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站铺厨,受9級(jí)特大地震影響缎玫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜努释,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一碘梢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伐蒂,春花似錦、人聲如沸肛鹏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缕减,卻和暖如春雷客,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桥狡。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工搅裙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裹芝。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓部逮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嫂易。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兄朋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • JavaScriptCore框架主要是用來實(shí)現(xiàn)iOS與H5的交互。由于現(xiàn)在混合編程越來越多怜械,H5的相對(duì)講多颅和,所以研...
    水靈芳蕥閱讀 1,386評(píng)論 1 8
  • OC與JS交互之JavaScriptCore 本文摘抄自:https://hjgitbook.gitbooks.i...
    大沖哥閱讀 1,017評(píng)論 0 1
  • 隨著H5技術(shù)的興起,在iOS開發(fā)過程中缕允,難免會(huì)遇到原生應(yīng)用需要和H5頁(yè)面交互的問題峡扩。其中會(huì)涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,060評(píng)論 1 8
  • 項(xiàng)目中涉及OC與網(wǎng)頁(yè)的交互,查找資料時(shí)看到了JavaScriptCore.framework障本,就對(duì)照文章ios7 ...
    YaoYaoX閱讀 2,327評(píng)論 7 11
  • 同族伯父張愛元:一位紅火熱鬧人 電腦上打出“張愛元”的時(shí)候有额,我內(nèi)心一陣巨痛,好像有無(wú)數(shù)螞蟻爬在身上彼绷,那樣難受巍佑,那樣...
    大可can713閱讀 236評(píng)論 0 2