【IOS】IOS開發問題解決方法索引(三)

 

1       判斷js對象是否擁有某屬性

 

http://www.cnblogs.com/snandy/archive/2011/03/04/1970162.html

兩種方式,但稍有區別

1,in運算符

1

2

3

var obj = {name:'jack'};

alert('name' in obj); // --> true

alert('toString' in obj); // --> true

可看到無論是name,還是原形鏈上的toString,都能檢測到返回true。

 

2,hasOwnProperty方法

1

2

3

var obj = {name:'jack'};

obj.hasOwnProperty('name'); // --> true

obj.hasOwnProperty('toString'); // --> false

原型鏈上繼承過來的屬性無法通過hasOwnProperty檢測到,返回false。

需注意的是,雖然in能檢測到原型鏈的屬性,但for in通常卻不行。

當然重寫原型後for in在IE9/Firefox/Safari/Chrome/Opera下是可見的。見:for in的缺陷

 

2       objective-c與js交互

 Objective-C 取得與設定 JavaScript 對象

 

要從 Objective-C取得網頁中的 JavaScript 對象,也就是對 windowScriptObject做一些 KVC 調用,像是 valueForKey: valueForKeyPath:。如果我們在 JS 裏頭,想要知道目前的網頁位置,會這麼寫:

var location = window.location.href;

Objective-C 就可以這麼調用:

NSString *location = [[webView windowScriptObject]valueForKeyPath:@"location.href"];

 

如果我們要設定 window.location.href,要求開啓另外一個網頁,在JS 裏頭:

window.location.href = 'http://spring-studio.net';

Objective-C

[[webView windowScriptObject]setValue:@"http://spring-studio.net"forKeyPath:@"location.href"];

 

由於Objective-C JS 本身的語言特性不同,在兩種語言之間相互傳遞東西之間,就可以看到兩者的差別:

·      JS 雖然是 OO,但是並沒有 class,所以將 JS 對象傳到 Obj C 程序裏頭,除了基本字串會轉換成 NSString、基本數字會轉成 NSNumber,像是 Array 等其他對象,在 Objective-C 中,都是 WebScriptObject 這個 Class。意思就是,JS Array 不會幫你轉換成 NSArray

·       JS 裏頭傳一個空對象給 Objective-C 程序,用的不是 Objective-C 裏頭原本表示「沒有東西」的方式,像是 NULLnilNSNull 等,而是專屬 WebKit 使用的 WebUndefined

 

所以,如果我們想要看一個 JS Array 裏頭有什麼東西,就要先取得這個對象裏頭叫做 length value,然後用webScriptValueAtIndex: 去看在該 index 位置的內容。

假如我們在 JS 裏頭這樣寫:

 

var JSArray ={'zonble', 'dot', 'net'};

for (var i= 0; i < JSArray.length; i++) {

console.log(JSArray[i]);

}

 

Objective-C 裏頭就會變成這樣:

 

WebScriptObject*obj = (WebScriptObject *)JSArray;

NSUInteger count= [[obj valueForKey:@"length"] integerValue];

NSMutableArray *a= [NSMutableArray array];

for (NSUInteger i= 0; i < count; i++) {

    NSString *item= [obj webScriptValueAtIndex:i];

    NSLog(@"item:%@",item);

}

 

 ObjectiveC 調用 JavaScriptfunction

 

要用 Objective-C 調用網頁中的 JS function,大概有幾種方法。第一種是直接寫一段跟你在網頁中會撰寫的 JS 一模一樣的程序,叫 windowScriptObject evaluateWebScript: 執行。

例如,我們想要在網頁中產生一個新的 JS function,內容是:

 

function x(x){

    return x+ 1;

}

 

所以在 Objective-C 中可以這樣寫;

[[webViewwindowScriptObject] evaluateWebScript:@"function x(x) { return x +1;}"];

接下來我們就可以調用 window.x()

NSNumber *result= [[webView windowScriptObject] evaluateWebScript:@"x(1)"];

NSLog(@"result:%d",[result integerValue]); // Returns 2

 

由於在 JS 中,每個 funciton 其實都是對象,所以我們還可以直接取得 window.x 叫這個對象執行自己。

JS 裏頭如果這樣寫:

window.x.call(window.x,1);

Objective-C 中便是這樣:

WebScriptObject *x= [[webView windowScriptObject] valueForKey:@"x"];

NSNumber *result= [x callWebScriptMethod:@"call"withArguments:[NSArray arrayWithObjects:x,[NSNumbernumberWithInt:1], nil]];

 

這種讓某個 WebScriptObject 自己執行自己的寫法,其實比較不會用於從 Objective-C 調用 JS 這一端,而是接下來會提到的,由JS 調用 Objective-C,因爲這樣 JS 就可以把一個 callback function 送到 Objective-C 程序裏頭。

 

如果我們在做網頁,我們只想要更新網頁中的一個區塊,就會利用 AJAX 的技巧,只對這個區塊需要的資料,對server 發出 request,並且在 request 完成的時候,要求執行一段callback function,更新這一個區塊的顯示內容。從 JS 調用 Objective-C也可以做類似的事情,如果 Objective-C程序裏頭需要一定時間的運算,或是我們可能是在 Objective-C 裏頭抓取網路資料,我們便可以把一個 callback function 送到 Objective-C程序裏,要求Objective-C程序在做完工作後,執行這段 callback function

 

 DOM

 

WebKit 裏頭,所有的 DOM 對象都繼承自 DOMObjectDOMObject 又繼承自 WebScriptObject,所以我們在取得了某個 DOM 對象之後,也可以從 Objective-C 程序中,要求這個 DOM 對象執行 JS 程序。

假如我們的網頁中,有一個 id 叫做 “#s” 的文字輸入框(text input),而我們希望現在鍵盤輸入的焦點放在這個輸入框上,在 JS 裏頭會這樣寫:

document.querySelector('#s').focus();

Objective-C中寫法:

DOMDocument*document = [[webView mainFrame] DOMDocument];

[[documentquerySelector:@"#s"]callWebScriptMethod:@"focus"withArguments:nil];

 

 JavaScript 存取 ObjectiveC  Value

 

要讓網頁中的 JS 程序可以調用 Objective-C 對象,方法是把某個 Objective-C 對象註冊成 JS window 對象的屬性。之後,JS 便也可以調用這個對象的 method,也可以取得這個對象的各種Value,只要是 KVC 可以取得的 Value,像是 NSStringNSNumberNSDateNSArrayNSDictionaryNSValue…等。JS Array Objective-C 時,還需要特別做些處理才能變成 NSArray,從 Obj C 傳一個 NSArray JS 時,會自動變成 JS Array

 

首先我們要注意的是將 Objective-C 對象註冊給 window 對象的時機,由於每次重新載入網頁,window 對象的內容都會有所變動-畢竟每個網頁都會有不同的 JS 程序,所以,我們需要在適當的時機做這件事情。我們首先要指定 WebView frame loading delegate(用 setFrameLoadDelegate:),並且實作 webView:didClearWindowObject:forFrame:WebView 只要更新了 windowScriptObject,就會調用這一段程序。

假如我們現在要讓網頁中的 JS 可以使用目前的 controller 對象,會這樣寫:

-(void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject*)windowObject forFrame:(WebFrame *)frame

{

    [windowObjectsetValue:self forKey:@"controller"];

}

如此一來,只要調用 window.controller,就可以調用我們的 Objective-C 對象。假如我們的 Objective-C Class 裏頭有這些成員變數:

@interface MyController: NSObject

{

    IBOutlet WebView*webView;

    IBOUtlet  NSWindow *window;

    NSString *stringValue;

    NSInteger numberValue;

    NSArray *arrayValue;

    NSDate *dateValue;

    NSDictionary *dictValue;

    NSRect frameValue;

}

@end

指定一下 Value

stringValue= @"string";

numberValue = 24;

arrayValue =[[NSArray arrayWithObjects:@"text",[NSNumbernumberWithInt:30], nil] retain];

dateValue =[[NSDate date] retain];

dictValue =[[NSDictionary dictionaryWithObjectsAndKeys:@"value1",@"key1", @"value2", @"key2", @"value3", @"key3", nil]retain];

frameValue =[window frame];

 

JS 讀讀看:

var c =window.controller;

var main =document.getElementByIdx_x('main');

var HTML= '';

if (c) {

    HTML+= '

' + c.stringValue + '

';

    HTML+= '

' + c.numberValue + '

';

    HTML+= '

' + c.arrayValue + '

';

    HTML+= '

' + c.dateValue + '

';

    HTML+= '

' + c.dictValue + '

';

    HTML+= '

' + c.frameValue + '

';

    main.innerHTML= HTML;

}

結果如下:

string 24 text,302010-09-09 00:01:04 +0800 { key1 = value1; key2 = value2; key3 = value3; }NSRect: {{275, 72}, {570, 657}}

 

不過,如果你看完上面的範例,就直接照做,應該不會直接成功出現正確的結果,而是會拿到一堆 undefined,原因是,Objective-C 對象的 Value 預設被保護起來,不會讓 JS 直接存取。要讓 JS 可以存取 Objective-C 對象的 Value,需要操作+isKeyExcludedFromWebScript: 針對傳入的 Key 一一處理,如果我們希望 JS 可以存取這個 key,就回傳 NO

+(BOOL)isKeyExcludedFromWebScript:(const char *)name

{

    if (!strcmp(name, "stringValue")){

        return NO;

    }

    return YES;

}

除了可以讀取 Objective-C對象的 Value 外,也可以設定 Value,相當於在 Objective-C中使用 setValue:forKey:,如果在上面的 JS 程序中,我們想要修改 stringValue,直接調用 c.stringValue = ‘new value’ 即可。像前面提到,在這裡傳給 Objective-C JS 對象,除了字串與數字外,class都是 WebScriptObject,空對象是 WebUndefined

 

  JavaScript 調用 ObjectiveC method

 

Objective-C 的語法沿襲自 SmallTalkObjective-C selector,與 JS function 語法有相當的差異。WebKit 預設的實事是,如果我們要在JS 調用 Objective-C selector,就是把所有的參數往後面擺,並且把所有的冒號改成底線,而原來 selector 如果有底線的話,又要另外處理。

假使我們的 controller 對象有個 method,在 Objective-C 中寫成這樣:

- (void)setA:(id)ab:(id)b c:(id)c;

JS 中就這麼調用:

controller.setA_b_c_('a', 'b', 'c');

 

實在有點醜。所以 WebKit 提供一個方法,可以讓我們把某個 Objective-C selector 變成好看一點的 JS function。我們要實作 webScriptNameForSelector:

+(NSString *)webScriptNameForSelector:(SEL)selector

{

    if (selector== @selector(setA:b:c:)) {

        return @"setABC";

    }

    return nil;

}

以後就可以這麼調用:

controller.setABC('a', 'b', 'c');

 

我們同樣可以決定哪些 selector 可以給 JS 使用,哪些要保護起來,方法是實作isSelectorExcludedFromWebScript:。而我們可以改變某個 Objective-C selector JS 中的名稱,我們也可以改變某個 value key,方法是實作 webScriptNameForKey:

 

有幾件事情需要注意一下:

JavaScript 調用 Objective C 2.0 property

在上面,我們用 JS 調用 window.controller.stringValue,與設定裏頭的 value 時,這邊很像我們使用 Objective-C 2.0 的語法,但其實做的是不一樣的事情。用 JS 調用 controller.stringValue,對應到的 Objective-C 語法是 [controller valueForKey:@"stringValue"],而不是調用 Objective-C 對象的 property

 

如果我們的 Objective-C 對象有個 property 叫做 stringValue,我們知道,Objective-C property 其實會在編譯時,變成 getter/setter method,在 JS 裏頭,我們便應該要調用controller.stringValue() controller.setStringValue_()

 

Javascript 中,Function 即對象的特性

JS function 是對象,當一個 Objective-C 對象的 method 出現在 JS 中時,這個 method JS 中,也可以或多或少當做對象處理。我們在上面產生了 setABC,也可以試試看把它倒出來瞧瞧:

console.log(controller.setABC);

我們可以從結果看到:

function setABC() {[native code] }

這個 function native code。因爲是 native code,所以我們無法對這個 function 調用 call 或是 apply

 

另外,在把我們的 Objective-C 對象註冊成 window.controller 後,我們會許也會想要讓 controller 變成一個 function 來執行,像是調用window.controller();或是,我們就只想要產生一個可以讓 JS 調用的 function,而不是整個對象都放進 JS 裏頭。我們只要在 Objective-C 對象中,實作invokeDefaultMethodWithArguments:,就可以回傳在調用window.controller() 時想要的結果。

 

現在我們可以綜合練習一下。前面提到,由於我們可以把 JS 對象以 WebScriptObject 這個 class 傳入 Obj C 程序,Objective-C 程序中也可以要求執行 WebScriptObject 的各項 function。我們假如想把 A B 兩個數字丟進 Objective-C 程序裏頭做個加法,加完之後出現在網頁上,於是我們寫了一個 Objective-C method

-(void)numberWithA:(id)a plusB:(id)b callback:(id)callback

{

    NSInteger result= [a integerValue] + [b integerValue];

    [callbackcallWebScriptMethod:@"call" withArguments:[NSArrayarrayWithObjects:callback,[NSNumber numberWithInteger:result],nil]];

}

JS 裏頭就可以這樣調用:

window.controller.numberWithA_plusB_callback_(1,2,function(result) {

    var main= document.getElementByIdx_x('main');

    main.innerText= result;

});

 

 其他平臺上 WebKit 的用法



除了 Mac OS XWebKit 這幾年也慢慢移植到其他的作業系統與framework 中,也或多或少都有 Native API 要求 WebView 執行 Js,以及從 JS 調用 Native API 的機制。

 

Mac OS X 比較起來,IOS UIWebView 的公開 API 實在少上許多。想要讓 UIWebView 執行一段 JS,可以透過調用stringByEvaluatingJavaScriptFromString:,只會回傳字串結果,所以能夠做到的事情也就變得有限,通常大概就拿來取得像 window.title 這些資訊。在 IOS 上我們沒辦法將某個 Objective-C 對象變成 JS 對象,所以,在網頁中觸發了某些事件,想要通知 Objective-C 這一端,往往會選擇使用像「zonble://」這類 Customized URL scheme

 

ChromeOS 完全以 WebKit 製作使用者介面,不過我們沒辦法在 ChomeOS 上寫我們在這邊所討論的桌面或行動應用程序,所以不在我們討論之列。(順道岔題,ChromeOS 是設計來給 Netbook 使用的作業系統,可是像 Toshiba 都已經用 Android,做出比 Netbook 更小的 Smartbook,而且應用程序更多,ChromeOS 的產品做出來的話,實在很像Google 拿出兩套東西,自己跟自己對打)。

 

Android WebView 對象提供一個叫做 addJavascriptInterface() method,可以將某個 Java 對象註冊成 JS window 對象的某個屬性,就可以讓 JS 調用 Java 對象。不過,在調用 Java 對象時,只能夠傳遞簡單的文字、數字,複雜的 JS 對象就沒辦法了。而在 Android 上想要 WebView 執行一段 JS,在文件中沒看到相關資料,網路上面找到的說法是,可以透過 loadUrl(),把某段 JS bookmarklet 的形式傳進去。

 

QtWebKit 裏頭,可以對 QWebFrame 調用addToJavaScriptWindowObject,把某個 QObject 暴露在 JS 環境中,我不清楚 JS 可以傳遞哪些東西到 QObject 裏頭就是了。在 QtWebKit 中也可以取得網頁裏頭的 DOM 對象(QWebElement
QWebElementCollection),我們可以對 QWebFrame 還有這些 DOM 對象調用 evaluateJavaScript,執行 Javascript

GTK 方面,因爲是 C API,所以在應用程序與 JS 之間,就不是透過操作包裝好的對象,而是調用 WebKit 裏頭 JavaScript Engine C API

 

 JavaScriptCore Framework

 

我們在 Mac OS X 上面,也可以透過 C API,要求 WebView 執行 Javascript。首先要 import 。如果我們想要簡單改一下window.location.href

JSGlobalContextRefglobalContext = [[webView mainFrame] globalContext];

JSValueRefexception = NULL;

JSStringRef script=JSStringCreateWithUTF8CString("window.location.href='http://spring-studio.net'");

JSEvaluateScript(globalContext,script, NULL, NULL, 0, &exception);

JSStringRelease(script);

 

如果我們想要讓 WebView 裏頭的 JS,可以調用我們的 C Function

-(void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject*)windowObject forFrame:(WebFrame *)frame

{

    JSGlobalContextRefglobalContext = [frame globalContext];

    JSStringRefname = JSStringCreateWithUTF8CString("myFunc");

    JSObjectRefobj = JSObjectMakeFunctionWithCallback(globalContext, name,(JSObjectCallAsFunctionCallback)myFunc);

    JSObjectSetProperty(globalContext, [windowObject JSObject], name, obj, 0, NULL);

    JSStringRelease(name);

}

那麼,只要 JS 調用 window.myFunc(),就可以取得們放在 myFunc 這個 C function 中回傳的結果:

JSValueRefmyFunc(JSContextRef ctx, JSObjectRef function, JSObjectRefthisObject, size_t argumentCount, const JSValueRefarguments[], JSValueRef* exception)

{

    return JSValueMakeNumber(ctx,42);

}

 

 

iOS開發之Objective-C與JavaScript的交互

http://www.cnblogs.com/zhuqil/archive/2011/08/03/2126562.html

iOS UIWebView中javascript與Objective-C交互、獲取攝像頭

http://www.cnblogs.com/lwme/p/ios-call-objc-camera-from-javascript-in-uiwebview.html

 

IOS開發之——objective-c與javascript交互

http://blog.sina.com.cn/s/blog_50e0bce501018ydu.html

 

若是傳入參數是字符串,記得加上單引號‘’

 

3       編寫自文檔化的代碼

編寫自文檔化的代碼

http://www.cnblogs.com/anderslly/archive/2009/06/21/write-self-documenting-code.html

編寫自文檔化的代碼

http://kb.cnblogs.com/page/47707/

 

4       objective-c與js交互

如何在Objective-C的類裏面聲明私有方法.

http://hi.baidu.com/shiqyn/item/52887ff19d3df61aa729885f

Objective-C中的類本身並沒有私有方法這個概念,聲明在 .h 文件中的方法都是公有的。不過,要想實現私有方法的效果還是有辦法的,就是用Category

// Hello.h

#import<Cocoa/Cocoa.h>

@interface Hello: NSObject {

    //變量聲明

}

// 方法聲明

@end

//

 

 

 

 

// Hello.m

#import"Hello.h"

@interfaceHello () //=>此處Hello命名一致,後邊跟括號

//=>@property(某種) aType ivarName ; 可實現私有變量

// 私有方法聲明

 

- (void)test;

@end

 

@implementationHello

// 私有方法實現

//=>@synthesizeivarName; 有私有變量的話

- (void)test {

// ..

}

// 方法實現

@end

在上面這個例子中,test 就是 Hello 類的“私有方法”了。再次證明,Category這個東東真的很強大~

5       iphone中的delegate委託機制

Objective-C回調機制(delegate,protocol)

http://blog.sina.com.cn/s/blog_6545eb460100pyjy.html

iphone中的delegate委託機制

http://wsqwsq000.iteye.com/blog/1121155

詳解Objective-C中委託和協議

http://mobile.51cto.com/iphone-283416.htm

 

6       iOS delegate使用時注意

delegate方法調用前,最好先判斷是否可以回調:

if([_testDelegaterespondsToSelector:@selector(onResult:)])

{

     [_testDelegateonResult:str];

}

 

7       ios 關於文件操作 獲取 文件大小

ios 關於文件操作 獲取 文件大小

http://blog.csdn.net/xlxying/article/details/8047695

c語言 實現

 

#include "sys/stat.h"

- (long long) fileSizeAtPath:(NSString*) filePath{ 

    struct statst; 

    if(lstat([filePathcStringUsingEncoding:NSUTF8StringEncoding], &st) == 0){ 

        returnst.st_size; 

    } 

    return 0; 

 

objective-c 語言實現

-(long long) fileSizeAtPath:(NSString*) filePath{ 

  NSFileManager*manager = [NSFileManager defaultManager]; 

  if ([managerfileExistsAtPath:filePath]){ 

    return [[managerattributesOfItemAtPath:filePath error:nil] fileSize]; 

  } 

  return 0; 

 

如果將兩種方法循環1000次,我們就可以發現兩者之間巨大的性能差距了,在我的測試環境中,結果如下,c函數的耗時僅是第一種方法的5%,在此推薦 c語言

 

一個空的文件夾 其中獲取大小爲68k ,應該是系統文件吧。

8       objective-c中NSString默認編碼格式不是utf-8

iOS NSString 轉換爲UTF-8編碼

http://blog.csdn.net/u011872945/article/details/11771651

 

9       Audio Queue開發——退出程序時要關閉音頻通道

使用Audio Queue Service進行音頻操作時,使用了AudioQueueNewOutput方法,來開闢一個Audio Queue輸出到硬件的通道,開啓後,在程序退出前,一定要用AudioQueueDispose方法釋放通道(而且最好是設置爲YES,即立即釋放),不然程序無法再次啓動該通道,除非IOS重啓。

 

10    AudioQueue開發——Buffer緩存設置

AudioQueue開發時,需要用到一個緩衝池隊列,該隊列的預讀取只需要一次就行了,不需要放在play方法中,只需放在初始化方法中就行。

 

11    objective-csetter方法調用時機

在保存類成員的數據時,需要使用self.***的操作來調用setter方法,才能最終保存數據。

 

12    對象nil狀態的使用

若一個對象需要重複使用,即alloc 後會release,那麼在release後,最好將其置爲nil,才能利用是否等於nil來判斷對象狀態。

13    IOS多線程—— GCD使用

使用GCD

http://blog.devtang.com/blog/2012/02/22/use-gcd/

iOS多線程編程之GrandCentral Dispatch(GCD)介紹和使用

http://blog.csdn.net/totogo2010/article/details/8016129

GCD的使用方法

http://beauty-soft.net/blog/ceiba/object-c/20130513/639.html

GCD之dispatchqueue

http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html

GCD介紹(一): 基本概念和Dispatch Queue

http://www.dreamingwish.com/dream-2012/gcd介紹(一)-基本概念和dispatch-queue.html

 

14    IOS——GDataXML使用

如何在項目中設置使用GDataXML解析類庫

http://www.cnblogs.com/lovecode/articles/2305416.html

IOS學習筆記27—使用GDataXML解析XML文檔

http://blog.csdn.net/ryantang03/article/details/7868246

【iOS開發】GDataXML使用實例

http://blog.csdn.net/qbins/article/details/12043813

 

15    objective-c選擇器Selector

IOS SEL (@selector) 原理及使用總結(一)

http://blog.csdn.net/fengsh998/article/details/8612969

【IOS】Object-C 中的Selector 概念

http://blog.sina.com.cn/s/blog_735065f90100yopd.html

 

16    IOS中延時執行的幾種方式

iOS延時執行的幾種方法

http://blog.csdn.net/czcty/article/details/7730089

IOS中延時執行的幾種方式的比較和彙總

http://bluevt.org/?p=128

 

 

17    Xcode工程中添加js文件,需要添加到copy Bundle Resources

 

18    OC中^符號使用

用^符號支持將一個代碼段以參數形式添加到方法中;

 

19    IOS內存管理

Autorelease只能用於屬性,不能用於內部成員變量,因爲內部成員變量賦值時不會有retain方法來增加引用計數。

            對於引用計數:

            Self.屬性 = 時,會增加一次引用計數。若是self.test = [[NSString alloc] init];最終引用計數2,而不是1

            所以正確的聲明方式是:

            _test =[[NSString alloc] init];

或者self.test =[[[NSString alloc] init] autorelease];

20    UIImageView不支持多組圖片載入播放

使用UIImageView播放幾組圖片以實現動畫效果時,同一個UIImageView對象實例無法切換載入多組圖片。

            究其原因:是因爲UIImageView的AnimationImage屬性是一個NSArray類型,而不是NSMutableArray,故一旦完成初始化,其數組大小就已經確定了,數組長度不再可變。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章