【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,故一旦完成初始化,其数组大小就已经确定了,数组长度不再可变。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江中散人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值