代码改变世界

NSInvocation简单使用

2013-02-16 15:39  三戒1993  阅读(156)  评论(0编辑  收藏  举报

多线程编程是防止主线程堵塞,增加运行效率等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。

本次介绍NSOperation的子集–NSInvocationOperation。

Ios代码  

  • @implementation MyCustomClass
  • - (void)launchTaskWithData:(id)data
  • {
  •     //创建一个NSInvocationOperation对象,并初始化到方法
  •     //selector参数后的值是你想在另外一个线程中运行的方法
  •     //object后的值是想传递给前面方法的数据
  •     NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
  •                     selector:@selector(myTaskMethod:) object:data];
  •     // 下面将我们建立的操作加入到本地程序的共享队列中(加入后方法就会立刻被执行)
  •     // 更多的时候是由我们自己建立操作队列
  •     [[MyAppDelegate sharedOperationQueue] addOperation:theOp];
  • }
  • // 这个是运行在另外一个线程的方法
  • - (void)myTaskMethod:(id)data
  • {
  • }
  • @end

 

一个NSOperationQueue操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的线程数量等

下面是建立并初始化一个操作队列:

Ios代码  

  • @interface MyViewController : UIViewController {
  •     //在头文件中声明该队列
  •     NSOperationQueue *operationQueue;
  • }
  • @end
  • @implementation MyViewController
  • - (id)init
  • {
  •     self = [super init];
  •     if (self) {
  •         //初始化操作队列
  •         operationQueue = [[NSOperationQueue alloc] init];
  •         //限定了该队列只同时运行一个线程
  •         //这个队列已经可以使用了
  •         [operationQueue setMaxConcurrentOperationCount:1];
  •     }
  •     return self;
  • }
  • - (void)dealloc
  • {
  •     //需要释放内存
  •     [operationQueue release];
  •     [super dealloc];
  • }
  • @end

简单介绍之后,其实可以发现这种方法是非常简单的。很多时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。


NSInvocation的使用


在 iOS中可以直接调用 某个对象的消息 方式有2种

一种是performSelector:withObject:

再一种就是NSInvocation

第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那就需要做些额外工作才能搞定。那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作

NSInvocation可以处理参数、返回值。会java的人都知道反射操作,其实NSInvocation就相当于反射操作。

下面这个例子描述了如何使用NSInvocation,以下例子中如果要正常运行,需要把不存在的类进行正确填写。

//方法签名类,需要被调用消息所属的类AsynInvoke ,被调用的消息invokeMethod:

NSMethodSignature *sig= [[AsynInvoke class] instanceMethodSignatureForSelector:@selector(invokeMethod:)];

//根据方法签名创建一个NSInvocation

NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:sig];

//设置调用者也就是AsynInvoked的实例对象,在这里我用self替代

[invocation setTarget:self];

//设置被调用的消息

[invocation setSelector:@selector(invokeMethod:)];

//如果此消息有参数需要传入,那么就需要按照如下方法进行参数设置,需要注意的是,atIndex的下标必须从2开始。原因为:0 1 两个参数已经被target 和selector占用

NSInteger num=10;

[invocation setArgument:&num atIndex:2];

//retain 所有参数,防止参数被释放dealloc

[invocation retainArguments];

//消息调用

[invocation invoke];

//如果调用的消息有返回值,那么可进行以下处理

 

//获得返回值类型

const char *returnType = sig.methodReturnType;

//声明返回值变量

id returnValue;

//如果没有返回值,也就是消息声明为void,那么returnValue=nil

if( !strcmp(returnType, @encode(void)) ){

returnValue =  nil;

}

//如果返回值为对象,那么为变量赋值

else if( !strcmp(returnType, @encode(id)) ){

[invocation getReturnValue:&returnValue];

}

else{

//如果返回值为普通类型NSInteger  BOOL

 

//返回值长度

NSUInteger length = [sig methodReturnLength];

//根据长度申请内存

void *buffer = (void *)malloc(length);

//为变量赋值

[invocation getReturnValue:buffer];

 

 

if( !strcmp(returnType, @encode(BOOL)) ) {

returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];

}

else if( !strcmp(returnType, @encode(NSInteger)) ){

returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];

}

returnValue = [NSValue valueWithBytes:buffer objCType:returnType];

}

 


区分不同版本的iPhone



执行环境

可以从 UIDevice 的属性 model 得到在现在执行的环境。例子如下:

1

2

3

4

5

6

7

8

9

10

NSString *modelname = [[UIDevice currentDevice]model];

if ([modelname isEqualToString:@"iPhone"]) {

  // iPhone

}

if ([modelname isEqualToString:@"IPod Touch"]) {

  // iPod touch

}

if ([modelname isEqualToString:@"iPhone Simulator"]) {

  // iPhone Simulator

}

或者


 

1

2

3

4

5

6

7

8

9

10

11

12

13

#import <TargetConditionals.h>


#if TARGET_OS_IPHONE

    // iPhone Device

#endif


#if TARGET_IPHONE_SIMULATOR

    // iPhone Simulator

#endif


#if !TARGET_IPHONE_SIMULATOR

    // iPhone Device

#endif

iPhone 机器版本

可以通过 uname 函数取得当前机器的版本。例子如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

struct utsname u;

uname(&u);

NSString *machine = [NSString stringWithCString:u.machine];


if ([machine isEqualToString:@"iPhone1,1"]) {

  // iPhone 1G

}

if ([machine isEqualToString:@"iPhone1,2"]) {

  // iPhone 3G

}

if ([machine isEqualToString:@"iPhone2,1"]) {

  // iPhone 3GS

}

if ([machine isEqualToString:@"iPod1,1"]) {

  // iPod touch 1G

}

if ([machine isEqualToString:@"iPod2,1"]) {

  // iPod touch 2G

}

if ([machine isEqualToString:@"iPod3,1"]) {

  // iPod touch Late2009

}

或者通过 sysctlbyname() 函数取得:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

- (NSString *) platform

{

  size_t size;

  sysctlbyname("hw.machine", NULL, &size, NULL, 0);

  char *machine = malloc(size);

  sysctlbyname("hw.machine", machine, &size, NULL, 0);

 

  NSString *platform = [NSString stringWithCString:machine];


  free(machine);

  return platform;

}

iPhone OS 版本

可以使用 UIDevice 的属性 systemVersion 来得到。例子如下:

1

2

3

4

5

6

7

8

9

10

NSString *osversion = [UIDevice currentDevice].systemVersion;

if ([osversion isEqualToString:@"2.1"]) {

  // iPhone

}

if ([osversion isEqualToString:@"2.2.1"]) {

  // iPod touch

}

if ([osversion isEqualToString:@"3.0"]) {

  // iPhone Simulator

}

这里有一个别人写好的类库,专门用来得到系统版本信息,用起来比较方便。

iPhone SDK 版本宏

就像在windows系统下用 WINVER 宏来判断 windows 系统版本一样,iPhone OS 中也有类似的宏。

1

2

3

4

// 当前系统支持的最小版本

__IPHONE_OS_VERSION_MIN_REQUIRED

// 当前系统支持的最大版本

__IPHONE_OS_VERSION_MAX_ALLOWED

比如用 iPhone OS SDK 3.1.2 编译的程序

1

2

__IPHONE_OS_VERSION_MIN_REQUIRED == __IPHONE_3_0

__IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_3_1

这时,我们可以在程序中使用下面类似的 $ifdef 语句:

1

2

3

4

5

#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_2_2

    // iPhone OS SDK 3.0 以后版本的处理

#else

    // iPhone OS SDK 3.0 之前版本的处理

#endif

又或者 iPhone OS SDK 4 推出的时候,可以:

1

2

3

4

5

6

7

8

9

#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_2_2

    #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_3_1

        // iPhone OS SDK 4.0 以后版本的处理

    #else

        // iPhone OS SDK 3.0 ~ 4.0 版本的处理

    #endif

#else

    // iPhone OS SDK 3.0 之前版本的处理

#endif


使用NSOperation建立多任务网络连接


在网络应用程序中,经常需要多任务连接来提高程序的性能。比如多任务下载,多任务HTTP请求等,即线程控制模型中的工作群模型。使用 NSOperation 可以很容易实现这个功能。下面就以使用NSOperation处理并行的HTTP请求为例子,说明其用法。

首先准备一个 NSOperation 的子类,用于处理 HTTP 请求。

1

2

3

4

5

6

7

8

@interface RequestOperation : NSOperation {

  NSURLRequest*  _request;

  NSMutableData* _data;

}


- (id)initWithRequest:(NSURLRequest *)request;


@end

下面是实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

@implementation RequestOperation


- (id)initWithRequest:(NSURLRequest *)request {

  if (self = [self init]) {

    _request = [request retain];

    _data = [[NSMutableData data] retain];

  }

  return self;

}


- (void)dealloc {

  [_request release];

  [_data release];

  [super dealloc];

}


// 如果不载下面的函数,会出错

- (BOOL)isConcurrent {

  return YES;

}


// 开始处理

- (void)start {

  if (![self isCancelled]) {

    // 以异步方式处理事件

    [NSURLConnection connectionWithRequest:_request delegate:self];

  }

}


// 取得数据

- (void)connection:(NSURLConnection*)connection

    didReceiveData:(NSData*)data {

  // 添加数据

  [_data appendData:data];

}


// HTTP请求结束

- (void)connectionDidFinishLoading:(NSURLConnection*)connection {

}


@end

如果没有重载 isConcurrent 函数,缺省是返回NO,就是说只能以同步的方式处理。而如果又使用了connectionWithRequest:delegate: 以异步方式处理事件后,会产生下面的错误信息:

1

_NSAutoreleaseNoPool(): Object 0x18a140 of class NSURLConnection autoreleased with no pool in place - just leaking

然后在你的 Controller 类中用 NSOperationQueue 来处理各个任务。

1

2

3

4

5

@interface xxViewController : UIViewController {

  NSOperationQueue* _queue;

}


@end

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@implementation xxViewController


- (IBAction)buttonClicked:(id) sender {

  _queue = [[NSOperationQueue alloc] init];

  // 第一个请求

  NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];

  RequestOperation* operation = [[RequestOperation alloc] initWithRequest:request];

  [operation autorelease];

  // 第二个请求

  NSURLRequest* request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.yahoo.co.jp"]];

  RequestOperation* operation2 = [[RequestOperation alloc] initWithRequest:request2];

  [operation2 autorelease];

  // 开始处理

  [_queue addOperation:operation];

}


@end

以上,用 NSOperation 来并行处理不同的任务,使用 NSOperationQueue 来控制复数的 NSOperation,并且可以限制Queue的大小,而不是无限制的使用任务。当一个任务完成,就执行待机中的任务。

 

原文链接 : http://www.yifeiyang.net/iphone-web-development-skills-of-the-articles-3-use-a-multi-task-network-connection-nsoperation/


iPhone开发之文件创建、删除、读取、写入


创建与删除://创建文件管理器NSFileManager *fileManager = [NSFileManager defaultManager];//获取路径//参数NSDocumentDirectory要获取那种路径NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentsDirectory = [paths objectAtIndex:0];

//更改到待操作的目录下[fileManager changeCurrentDirectoryPath:[documentsDirectory stringByExpandingTildeInPath]];

//创建文件fileName文件名称,contents文件的内容,如果开始没有内容可以设置为nil,attributes文件的属性,初始为nil[fileManager createFileAtPath:@"fileName" contents:nil attributes:nil];

//删除待删除的文件[fileManager removeItemAtPath:@"createdNewFile" error:nil];

写入数据://获取文件路径NSString *path = [documentsDirectory stringByAppendingPathComponent:@"fileName"];//待写入的数据NSString *temp = @”Welcome to blog.iosxcode4.com”;int data0 = 100000;float data1 = 23.45f;

//创建数据缓冲NSMutableData *writer = [[NSMutableData alloc] init];

//将字符串添加到缓冲中[writer appendData:[temp dataUsingEncoding:NSUTF8StringEncoding]];

//将其他数据添加到缓冲中[writer appendBytes:&data0 length:sizeof(data0)];[writer appendBytes:&data1 length:sizeof(data1)];

//将缓冲的数据写入到文件中[writer writeToFile:path atomically:YES];[writer release];

读取数据:int gData0;float gData1;NSString *gData2;

NSData *reader = [NSData dataWithContentsOfFile:path];gData2 = [[NSString alloc] initWithData:[reader subdataWithRange:NSMakeRange(0, [temp length])]encoding:NSUTF8StringEncoding];[reader getBytes:&gData0 range:NSMakeRange([temp length], sizeof(gData0))];[reader getBytes:&gData2 range:NSMakeRange([temp length] + sizeof(gData0), sizeof(gData1))];

NSLog(@”gData0:%@  gData1:%i gData2:%f”, gData0, gData1, gData2);