线程专题总结


haoxue2011-01-08 21:03
[iOS]深入浅出 iOS 之多线程 NSThread 
 
 
iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用起来也越方便,也是苹果最推荐使用的方法。 
 
 
下面简要说明这三种不同范式:

Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间,它们拥有对数据相同的访问权限。 
你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。在 iOS 中我们可以使用多种形式的 thread: 
Cocoa threads: 使用NSThread 或直接从 NSObject 的类方法 performSelectorInBackground:withObject: 来创建一个线程。如果你选择thread来实现多线程,那么 NSThread 就是官方推荐优先选用的方式。 
POSIX threads: 基于 C 语言的一个多线程库, 
 
Cocoa operati*****是基于 Obective-C实现的,类 NSOperation 以面向对象的方式封装了用户需要执行的操作,我们只要聚焦于我们需要做的事情,而不必太操心
线程的管理,同步等事情, 
因为NSOperation已经为我们封装了这些事情。 NSOperation 是一个抽象基类,我们必须使用它的子类。iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation。 
 
Grand Central Dispatch : iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,它的关注点更高:如何在多个 cpu 上提升效率。
 
 
有了上面的总体框架,我们就能清楚地知道不同方式所处的层次以及可能的效率,便利性差异。下面我们先来看看 NSThread 的使用,包括创建,启动,同步,通信等相关知识。这些与 win32/Java 下的 thread 使用非常相似。
 
 
线程创建与启动
 
NSThread的创建主要有两种直接方式: 
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil]; 
和 
NSThread* myThread = [[NSThread alloc] initWithTarget:self 
                                        selector:@selector(myThreadMainMethod:) 
                                        object:nil]; 
[myThread start]; 
 
这两种方式的区别是:前一种一调用就会立即创建一个
线程来做事情;而后一种虽然你 alloc 了也 init了,但是要直到我们手动调用 start 启动线程时才会真正去创建线程。 
这种延迟实现思想在很多跟资源相关的地方都有用到。后一种方式我们还可以在启动线程之前,对线程进行配置,比如设置 stack 大小,线程优先级。 
 
还有一种间接的方式,更加方便,我们甚至不需要显式编写 NSThread 相关代码。那就是利用 NSObject 的类方法 performSelectorInBackground:withObject: 来创建一个
线程: 
[myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil]; 
其效果与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。 
 
 
线程同步
 
线程
的同步方法跟其他系统下类似,我们可以用原子操作,可以用 mutex,lock等。 
iOS的原子操作函数是以 OSAtomic开头的,比如:OSAtomicAdd32, OSAtomicOr32等等。这些函数可以直接使用,因为它们是原子操作。 
 
iOS中的 mutex 对应的是 NSLock,它遵循 NSLooking协议,我们可以使用 lock, tryLock, lockBeforeData:来加锁,用 unLock来解锁。使用示例:
 
BOOL moreToDo = YES; 
NSLock *theLock = [[NSLock alloc] init]; 
... 
while (moreToDo) { 
    /* Do another increment of calculation */ 
    /* until there’s no more to do. */ 
    if ([theLock tryLock]) { 
        /* Update display used by all threads. */ 
        [theLock unlock]; 
    } 

 
我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
 
- (void)myMethod:(id)anObj 

    @synchronized(anObj) 
    { 
        // Everything between the braces is protected by the @synchronized directive. 
    } 

  
还有其他的一些锁对象,比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等,在这里就不一一介绍了,大家去看官方文档吧。 
 
 
用NSCodition同步执行的顺序
 
NSCodition 是一种特殊类型的锁,我们可以用它来同步操作执行的顺序。它与 mutex 的区别在于更加精准,等待某个 NSCondtion 的线程一直被 lock,直到其他线程给那个 condition 发送了信号。 
下面我们来看使用示例:
某个线程等待着事情去做,而有没有事情做是由其他线程通知它的。 
 
 
[cocoaCondition lock]; 
while (timeToDoWork <= 0) 
    [cocoaCondition wait]; 
  
timeToDoWork--;  
// Do real work here. 
[cocoaCondition unlock]; 
 
其他
线程发送信号通知上面的线程可以做事情了: 
[cocoaCondition lock]; 
timeToDoWork++; 
[cocoaCondition signal]; 
[cocoaCondition unlock]; 
 
 
线程间通信
 
线程
在运行过程中,可能需要与其它线程进行通信。我们可以使用 NSObject 中的一些方法: 
在应用程序主线程中做事情: 
performSelectorOnMainThread:withObject:waitUntilDone: 
performSelectorOnMainThread:withObject:waitUntilDone:modes: 
 
在指定
线程中做事情: 
performSelector:onThread:withObject:waitUntilDone: 
performSelector:onThread:withObject:waitUntilDone:modes: 
 
在当前
线程中做事情: 
performSelector:withObject:afterDelay: 
performSelector:withObject:afterDelay:inModes: 
 
取消发送给当前
线程的某个消息 
cancelPreviousPerformRequestsWithTarget: 
cancelPreviousPerformRequestsWithTarget:selector:object: 
 
如在我们在某个
线程中下载数据,下载完成之后要通知主线程中更新界面等等,可以使用如下接口:- (void)myThreadMainMethod 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    // to do something in your thread job 
    ... 
    [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO]; 
    [pool release]; 
}
 
 
RunLoop 
说到 NSThread 就不能不说起与之关系相当紧密的 NSRunLoop。Run loop 相当于 win32 里面的消息循环机制,它可以让你根据事件/消息(鼠标消息,键盘消息,计时器消息等)来调度线程是忙碌还是闲置。 
系统会自动为应用程序的主线程生成一个与之对应的 run loop 来处理其消息循环。在触摸 UIView 时之所以能够激发 touchesBegan/touchesMoved 等等函数被调用, 
就是因为应用程序的主线程
在 UIApplicationMain 里面有这样一个 run loop 在分发 input 或 timer 事件。
 
 

haoxue2011-01-08 21:07
performSelector:withObject:afterDelay: 
 
这段代码中@selector 搜索setMake方法 并返回SEL类型的变量。 
格式如下: 
SEL 变量名 =  @selector(方法名:  第二个参数名 : 参数); 
[myCar2performSelector:carMethod withObject:@"11111" withObject:@"2222"afterDelay:10]; 
这句话将搜索到的SEL类型的消息 发送给 myCar2对象。performSelector关键字可以调用到SEL类型的变量。延迟十秒执行。 
其语句格式如下: 
[消息接受者  performSelector:SEL类型的变量  withObject: 第一个参数  withObject: 第二个参数  afterDelay :秒数]; 
请问performSelectorInBackGround和NSThread中的detachNewThreadSelector有什么区别    
                如题:请问performSelectorInBackGround和NSThread中的detachNewThreadSelector还有performSelectorOnMainThread有什么区别,为什么有的地方要用[self performSelectorOnMainThread:@seletor(displayThread1Counts:withObject:myNumber waitUntialDone:YES),不直接用[self displayThread1Counts:]],还有那个waiUntilDone:YES代表什么意思detachNewThreadSelector还有performSelectorOnMainThread前者是开启一个线程,后者是回到主线程(更新UI的话必须到主线程)。 
 

haoxue2011-01-08 22:01
                timer,runloop,thread,task小总结 
 
               
 对这几个也算不上有很深的理解,只是平时用到些许timer,thread。 
想起有次去baidu笔试遇到runloop和timer等的区别,当时就不会。 
两三月过去了,如今终于稍微整理了下。 
有不对的地方盼指正。 
(版权所有哦) 
 
· NSThread:常见的线程 
每个进程里都有多个线程,我们一般如下实用thread: 
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil]; 
如果函数需要输入参数,那么可以从object传进去。你也可以这样实现 
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil]; 
[myThread start]; // Actually create the thread 
(from apple: threading PG) 
你的对象也可以直接使用线程: 
[myObj performSelectorInBackground:@selector(doSomething) withObject:nil]; 
 
 
· NSTimer:定时器 
等待一定时间后,触发某个事件发生,可循环触发。默认是添加到当前runloop。你也可以添加到自己新建的runloop里去,注意如果添加的话runloop会retain timer,你应当release timer而将timer交给runloop,就像将operation加入operationQueue中一样。 
可以很简单的调用: 
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(addLabel) userInfo:nil repeats:YES]; 
- (void)addLabel 

 
label.text = [NSString stringWithFormat:@"%d",num++]; 

每隔2秒,就触发定时器,向self发送addLabel消息。 
· NSRunLoop 
当有输入信号(input source,比如键盘鼠标的操作、),NSPort和NSConnection对象时,runloop提供了一个程序接口,即等待输入。但是我们从来都不需要去创建或者去管理一个runloop。在每个进程中都相应的有runloop,不管时当前的主进程还是你新建的进程。如果需要访问当前进程的runloop可以调用类方法:+ (NSRunLoop *)currentRunLoop。 
[[NSRunLoop currentRunLoop] performSelector:@selector(addLabel2) 
  target:self  
  argument:nil  
  order:0 
  modes:[NSArray arrayWithObjects:@"NSDefaultRunLoopMode",nil]] 
 //举个例子而已,一般不会这样用 
一般需要使用runloop也就是对于netservice,stream等对象以某种模式schedule在当前的runloop,如: 
[[_session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];。 
 
 
Runloop的作用在于当有事情要做时它使当前的thread工作,没有事情做时又使thread 休眠sleep。注意Runloop并不是由系统自动控制的,尤其是对那些你新建的次线程你需要对其进行显示的控制。 
 
Runloop顾名思义就是一个不停的循环,不断的去check输入,如下图。 
 
我们需要了解runloop modes这对判断事件来源以及添加到runloop时很有必要。 
 
正如之前我们所说,只有创建了次线程才需要我们管理runloop,但是也并不是创建了次线程就一定需要管理runloop,仅当: 
o Use ports or custom input sources to communicate with other threads. 
o Use timers on the thread. 
o Use any of the performSelector... methods in a Cocoa application. 
o Keep the thread around to perform periodic tasks. 
你还可以注册runloop,这样可以使用kvo。 
· NSTask: 
使用task你可以运行其它程序作为当前程序的子进程,并监控它的运行。它和线程的不同之处在于它并不何创建它的进程(父进程)共享内存。可以说是“完全”独立的一个东西。 
             
 

haoxue2011-09-28 09:40
创建NSThread 的 run loop  
 
 
 
虽然iphone为我们提供了很多简单易于操作的线程方法。IPhone多线程编程提议用NSOperation和NSOperationQueue,这个确实很好用。但是有些情况下,我们还是在运行一些长线任务或者复杂任务的时候需要用比较原始的NSThread。这就需要为NSThread创建一个run loop. 
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(playerThread: ) object:nil]; 
[thread start]; 
//如果要利用NSOperation,原理类似。只需要加入到queue里面去就好了。。queue会在合适的时机调用方法,下面代码作为参考。 
[pre]- (void) playerThread:(void*)unused {    audioRunLoop = CFRunLoopGetCurrent();//子线程的runloop引用。    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//子线程的run loop    [self initPlayer];     CFRunLoopRun(); //运行子线程的run loop,这里就会停住了。    [pool release]; } 
// 实现一个timer,用于检查子线程的工作状态,并在合适的时候做任务切换。或者是合适的时候停掉自己的run loop 
-(void) initPlayer{    // 在这里你可以初始化一个工作类。比如声音或者视频播放。NSTimer *stateChange = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(checkStatuserInfo:nil repeats:YES]; } 
-(void) checkState:(NSTimer*) timer{            if(需要退出自线程了)           {               //释放子线程里面的资源。        CFRunLoopStop( CFRunLoopGetCurrent());//结束子线程任务           }}[/pre]
 
 

haoxue2011-09-28 09:47
iPhone SDK中多线程的使用方法以及注意事项 
  虽然现在大部分PC应用程序都支持多线程/多任务的开发方式,但是在iPhone上,Apple并不推荐使用多线程的编程方式。但是多线程编程毕竟是发展的趋势,而且据说即将推出的iPhone OS4将全面支持多线程的处理方式。所以说掌握多线程的编程方式,在某些场合一定能挖掘出iPhone的更大潜力 
从例子入手 
    先从一个例程入手,具体的代码参考了这里。还有例程可以下载。多线程程序的控制模型可以参考这里,一般情况下都是使用 管理者/工人模型, 这里,我们使用iPhone SDK中的 NSThread 来实现它。 
    首先创建一个新的 View-based application 工程,名字为 "TutorialProject" 。界面如下图所示,使用UILabel实现两部分的Part(Thread Part和Test Part),Thread Part中包含一个UIProgressView和一个UIButton;而Test Part包含一个值和一个UISlider。 
 
    接下来,在 TutorialProjectViewController.h 文件中创建各个UI控件的 IBOutlets. 
@interface TutorialProjectViewController : UIViewController { 
 
    // ------ Tutorial code starts here ------ 
 
    // Thread part 
    IBOutlet UILabel *threadValueLabel; 
    IBOutlet UIProgressView *threadProgressView; 
    IBOutlet UIButton *threadStartButton; 
 
    // Test part 
    IBOutlet UILabel *testValueLabel; 
 
    // ------ Tutorial code ends here ------ 
 
}
 
    同时,也需要创建outlets变量的property. 
@property (nonatomic, retain) IBOutlet UILabel *threadValueLabel; 
@property (nonatomic, retain) IBOutlet UIProgressView *threadProgressView; 
@property (nonatomic, retain) IBOutlet UIProgressView *threadStartButton; 
@property (nonatomic, retain) IBOutlet UILabel *testValueLabel;
 
    接下来定义按钮按下时的动作函数,以及slider的变化函数。 
- (IBAction) startThreadButtonPressed:(UIButton *)sender; 
- (IBAction) testValueSliderChanged:(UISlider *)sender;
 
    然后在 TutorialProjectViewController.m 文件中synthesize outlets,并在文件为实现dealloc释放资源。 
@synthesize threadValueLabel, threadProgressView, testValueLabel, threadStartButton; 
 
... 
 
- (void)dealloc { 
 
    // ------ Tutorial code starts here ------ 
 
    [threadValueLabel release]; 
    [threadProgressView release]; 
    [threadStartButton release]; 
 
    [testValueLabel release]; 
 
    // ------ Tutorial code ends here ------ 
 
    [super dealloc]; 
}
 
    现在开始线程部分的代码,首先当 thread button 被按下的时候,创建新的线程. 
- (IBAction) startThreadButtonPressed:(UIButton *)sender { 
    threadStartButton.hidden = YES; 
    threadValueLabel.text = @"0"; 
    threadProgressView.progress = 0.0; 
    [NSThread detachNewThreadSelector:@selector(startTheBackgroundJob) toTarget:self withObject:nil]; 
}
 
    该按钮被按下后,隐藏按钮以禁止多次创建线程。然后初始化显示值和进度条,最后创建新的线程,线程的函数为 startTheBackgroundJob。具体的 startTheBackgroundJob 函数定义如下. 
- (void)startTheBackgroundJob { 
 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    // 线程开始后先暂停3秒(这里只是演示暂停的方法,你不是必须这么做的) 
    [NSThread sleepForTimeInterval:3]; 
    [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; 
    [pool release]; 
 
}
 
    在第1行,创建了一个 NSAutoreleasePool 对象,用来管理线程中自动释放的对象资源。这里 NSAutoreleasePool 在线程退出的时候释放。这符合 Cocoa GUI 应用程序的一般规则。最后一行,阻塞调用(waitUntilDone状态是ON)函数 makeMyProgressBarMoving。 
- (void)makeMyProgressBarMoving { 
 
    float actual = [threadProgressView progress]; 
    threadValueLabel.text = [NSString stringWithFormat:@"%.2f", actual]; 
    if (actual < 1) { 
        threadProgressView.progress = actual + 0.01; 
        [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(makeMyProgressBarMoving) userInfo:nil repeats:NO]; 
    } 
    else threadStartButton.hidden = NO; 
 
}
 
    这里计算用于显示的进度条的值,利用 NSTimer ,每0.5秒自增0.01,当值等于1的时候,进度条为100%,退出函数并显示刚才被隐藏的按钮。 
    最后,添加 UISlider 的实现函数,用来更改主线程中 Test Part 中的 label 值。 
- (IBAction) testValueSliderChanged:(UISlider *)sender { 
 
    testValueLabel.text = [NSString stringWithFormat:@"%.2f", sender.value]; 
 
}
 
    编译执行,按下线程开始按钮,你将看到进度条的计算是在后台运行。 
 
 
 

haoxue2011-09-28 09:48
使用线程的注意事项 
线程的堆栈大小 
    iPhone设备上的应用程序开发也是属于嵌入式设备的开发,同样需要注意嵌入式设备开发时的几点问题,比如资源上限,处理器速度等。 
    iPhone 中的线程应用并不是无节制的,官方给出的资料显示iPhone OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB。并且该值不能通过编译器开关或线程API函数来更改。 
    你可以用下面的例子测试你的设备,这里使用POSIX Thread(pthread),设备环境是 iPhone 3GS(16GB)、SDK是3.1.3。   
#include "pthread.h" 
 
void *threadFunc(void *arg) { 
    void*  stack_base = pthread_get_stackaddr_np(pthread_self()); 
    size_t stack_size = pthread_get_stacksize_np(pthread_self()); 
    NSLog(@"Thread: base:%p / size:%u", stack_base, stack_size); 
    return NULL; 

 
- (void)applicationDidFinishLaunching:(UIApplication *)application { 
    void*  stack_base = pthread_get_stackaddr_np(pthread_self()); 
    size_t stack_size = pthread_get_stacksize_np(pthread_self()); 
    struct rlimit limit; 
    getrlimit(RLIMIT_STACK, &limit); 
    NSLog(@"Main thread: base:%p / size:%u", stack_base, stack_size); 
    NSLog(@"  rlimit-> soft:%llu / hard:%llu", limit.rlim_cur, limit.rlim_max); 
 
    pthread_t thread; 
    pthread_create(&thread, NULL, threadFunc, NULL); 
 
    // Override point for customization after app launch 
    [window addSubview:viewController.view]; 
    [window makeKeyAndVisible]; 
}
 
    结果如下: 
模拟器 
Main thread: base:0xc0000000 / size:524288 
rlimit-> soft:8388608 / hard:67104768 
Thread: base:0xb014b000 / size:524288
 
设备 
Main thread: base:0x30000000 / size:524288 
rlimit-> soft:1044480 / hard:1044480 
Thread: base:0xf1000 / size:524288
 
    由此可见,当你测试多线程的程序时,模拟器和实际设备的堆栈大小是不一样的。如果有大量递归函数调用可要注意了。 
Autorelease 
    如果你什么都不考虑,在线程函数内调用 autorelease 、那么会出现下面的错误: 
NSAutoReleaseNoPool(): Object 0x********* of class NSConreteData autoreleased with no pool in place …. 
    一般,在线程中使用内存的模式是,线程最初 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; 
    而在线程结束的时候 [pool drain] 或 [pool release]。 
子线程中描画窗口 
    多线程编程中普遍遵循一个原则,就是一切与UI相关的操作都有主线程做,子线程只负责事务,数据方面的处理。那么如果想在子线程中更新UI时怎么做呢?如果是在windows下,你会 PostMessage 一个描画更新的消息,在iPhone中,需要使用performSelectorOnMainThread 委托主线程处理。 
    比如,如果在子线程中想让 UIImageView 的 image 更新,如果直接在线程中 
imageView.image = [UIImage imageNamed:@"Hoge.png"]; 
    这么做,什么也不会出现的。需要将该处理委托给主线程来做,像下面: 
[delegate performSelectorOnMainThread:@selector(theProcess:) withObject:nil waitUntilDone:YES]; 
    就OK了! 

haoxue2011-09-28 09:50
iphone开发-多线程的使用 
 
 
多线程之NSInvocationOperation 
多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。 
本次介绍NSOperation的子集,简易方法的NSInvocationOperation: 
@implementationMyCustomClass 
 -(void)launchTaskWithData:(id)data 

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

  
// 这个是真正运行在另外一个线程的“方法” 
-(void)myTaskMethod:(id)data 

   // Perform thetask. 

  
@end一个NSOperationQueue 操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等。下面是建立并初始化一个操作队列: 
@interface MyViewController : UIViewController{ 
  
   NSOperationQueue*operationQueue; 
   //在头文件中声明该队列 

@end 
  
@implementationMyViewController 
  
-(id)init  

   self = [superinit]; 
   if (self){ 
       operationQueue = [[NSOperationQueue alloc] init];//初始化操作队列 
       [operationQueuesetMaxConcurrentOperationCount:1]; 
       //在这里限定了该队列只同时运行一个线程 
       //这个队列已经可以使用了 
   } 
   returnself; 

  
-(void)dealloc 

   [operationQueuerelease]; 
   //正如Alan经常说的,我们是程序的好公民,需要释放内存! 
   [superdealloc]; 

  
@end简单介绍之后,其实可以发现这种方法是非常简单的。很多的时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。 
 
 
/// 
1 在主线程里加入一个loading画面…… 
2 { 
3 [windowaddSubview:view_loading]; 
4[NSThread detachNewThreadSelector:@selector(init_backup:)toTarget:self withObject:nil]; 
5} 
可以通过performSelectorOhMainThread更新UI元素,比如设置进度条等等。最后消除loading画面,载入主View。 
7 -(void)init_backup:(id)sender 
8{ 
9 NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init]; 
10  
11 // ... 
12 int i =status; 
13 [selfperformSelectorOnMainThread:@selector(show_loading:)withObject:[NSNumber numberWithInt:i] waitUntilDone:NO]; 
14  
15 [view_loadingremoveFromSuperview]; 
16 [windowaddSubview:tabcontroller_main.view]; 
17[pool release]; 
18 } 
/// 
利用iphone的多线程实现和线程同步 
 
从接口的定义中可以知道,NSThread和大多数iphone的接口对象一样,有两种方式可以初始化: 
一种使用initWithTarget :(id)targetselector:(SEL)selectorobject:(id)argument,但需要负责在对象的retaincount为0时调用对象的release方法清理对象。 
另一种则使用所谓的convenientmethod,这个方便接口就是detachNewThreadSelector,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。 
#import<UIKit/UIKit.h> 
@interfaceSellTicketsAppDelegate : NSObject<UIApplicationDelegate>{ 
   inttickets; 
   intcount; 
   NSThread*ticketsThreadone; 
   NSThread*ticketsThreadtwo; 
   NSCondition*ticketsCondition; 
   UIWindow *window; 

@property (nonatomic, retain) IBOutlet UIWindow*window; 
@end 
然后在实现中添加如下代码: 
// SellTicketsAppDelegate.m 
// SellTickets 
// 
// Created by sun dfsun2009 on09-11-10. 
// Copyright __MyCompanyName__ 2009. All rightsreserved. 
// 
#import"SellTicketsAppDelegate.h" 
@implementati*****ellTicketsAppDelegate 
@synthesizewindow; 
-(void)applicationDidFinishLaunching:(UIApplication *)application{ 
   tickets =100; 
   count =0; 
   // 锁对象 
   ticketCondition = [[NSCondition alloc]init]; 
   ticketsThreadone = [[NSThread alloc] initWithTarget:selfselector:@selector(run)object:nil]; 
   [ticketsThreadonesetName:@"Thread-1"]; 
   [ticketsThreadonestart]; 
   ticketsThreadtwo = [[NSThread alloc] initWithTarget:selfselector:@selector(run)object:nil]; 
   [ticketsThreadtwosetName:@"Thread-2"]; 
   [ticketsThreadtwostart]; 
   //[NSThread detachNewThreadSelector:@selector(run) toTarget:selfwithObject:nil]; 
   // Override point for customization after applicationlaunch 
   [windowmakeKeyAndVisible]; 

-(void)run{ 
   while (TRUE){ 
       // 上锁 
       [ticketsConditionlock]; 
       if(tickets >0) 
       { 
           [NSThreadsleepForTimeInterval:0.5]; 
           count = 100 -tickets; 
           NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThreadcurrentThread]name]); 
           tickets--; 
       }else 
       { 
           break; 
       } 
       [ticketsConditionunlock]; 
   } 

-        (void)dealloc{ 
   [ticketsThreadonerelease]; 
   [ticketsThreadtworelease]; 
   [ticketsConditionrelease]; 
   [windowrelease]; 
   [superdealloc]; 

@end 

haoxue2011-09-28 09:51
iphone开发中,线程的用法 

haoxue2011-09-28 09:54
多参数selector    
Selector是Objective-C一个非常强大的特性,合理使用Selector可以大大简化实现并避免重复代码。但NSObject提供  的performSelector最多只支持两个参数, 
对于两个以上的参数就没有能力为力了。一番调查后针对NSObject增加了如下扩展,使得 performSelector可以支持传入参数数组。多个参数就不再是问题了。  
 
@interface NSObject (Addition) 
- (id)performSelector:(SEL)selector withObjects:(NSArray *)objects; 
@end 
@implementation NSObject (Addition) 
- (id)performSelector:(SEL)selector withObjects:(NSArray *)objects {  
      NSMethodSignature *signature = [self methodSignatureForSelector:selector];  
      if (signature) {  
          NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];  
          [invocation setTarget:self];  
          [invocation setSelector:selector];  
          for(int i = 0; i < [objects count]; i++){  
              id object = [objects objectAtIndex:i];  
              [invocation setArgument:&object atIndex: (i + 2)];         
          }  
          [invocation invoke];  
          if (signature.methodReturnLength) {  
              id anObject;  
              [invocation getReturnValue:&anObject];  
              return anObject;  
          } else {  
              return nil;  
          }  
      } else {  
          return nil;  
      }  
  }  
  @end
 
 
 

haoxue2011-09-28 09:58
 [self performSelector:@selector(addShrinkImg) withObject:self afterDelay:2.0f];//performSelector: withObject:afterDelay://线程的使用  延迟函数 

kesalin2011-09-28 10:23
哇,我写的文章被收录了,不过没被留名 
深入浅出 Cocoa 之多线程 NSThread 
 
还有一篇关于 block 编程的: 
深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 

haoxue2011-10-12 18:49
线程同步和线程异步有什么区别? 
打个比方,如果你在等一个人, 
同步的时候,你会一直等到她来了之后才做其他事情,这个过程除了等待你啥都不会做, 
异步的时候,你一边在等,可能一边玩游戏或者是看报纸什么的,一直到她到来,你的等待状态才会结束
 
在实现上,同步的过程会阻塞进程的所有其他操作,将同步转换为异步的最常见方法则是 
将会阻塞进程的等待操作放入到一个新的进程中,同时为该等待操作添加一个监视器,在检测到等待操作完成的时候结束等待的进程。
 
 

haoxue2011-10-15 01:37
多线程编程指南(重要理论知识) 
 
1. 关于多线程编程 
多年以来,计算机的性能在很大程度上被单核处理器的速度所限制。在当前技术下,单核处理器的速度已经到达某种极限,因此,芯片制造商们转而专注于多核设计,以使计算机可以同时执行多个任务。Mac OS X 可以利用多核计算,更好的执行系统相关的任务。而开发人员也可以通过线程提高自己程序的性能。 
1) 什么是线程? 
 
线程是在程序内部运行多个流程的轻量单位。在系统级别上,程序使用系统提供的执行时间,各自运行。然而,在每个程序内部,存在着一个或多个被执行的线程,这些线程可以在同一时刻(或将近同时)完成不同的任务。由操作系统本身管理这些线程的执行,例如为线程分配执行时间和执行的内核,或是中断线程来使其他线程有机会执行。 
 
从技术角度来看,线程是“内核级”和“程序级”数据结构的集合,用来管理代码的执行。内核级的数据结构主要处理针对线程的事件,安排线程在可用的内核上执行。而程序级的结构包括调用栈(可以保存函数调用),还有用来管理和计算线程属性和状态的数据。 
 
在非并行程序中,只有一个线程的执行。线程起始于main函数,结束于main函数。在main函数内部,程序一句一句的执行。相比较而言,支持并行的程序起始于一个线程,然后在添加其他线程,从而创建了额外的执行路径。每个这样的路径都拥有自己的执行过程,而和程序中的主线程并行不悖。在程序中加入多线程,会为你带来以下两个非常重要的好处: 
 
1. 多线程可以提高程序的反应速度。 
2. 在多核系统上,多线程可以增强程序的实时处理能力。 
 
如果你的程序只有一个线程,这一个线程就得做所有事情。它必须响应事件、更新程序窗口、执行所有的运算工作。单线程的问题是在特定时刻内,它只能处理一件事。那么如果我们的某些计算工作需要耗费很长时间,会发生什么?当代码忙于计算需要的值时,程序就停止对用户事件的响应,也不会再对窗口进行更新。如果时间很长的话,用户可能以为程序卡住了,强行关闭程序。但是,如果你将需要的计算工作放到一个独立的线程中去,你的主线程就有足够的时间去响应用户事件。 
 
在多核电脑日益普及的今天,线程提高了某些程序的性能。执行多个任务的线程可以使用不同的处理器内核,从而使增加给定时间内完成的工作量成为可能。 
 
但是,线程并不是改善程序性能问题的万金油。伴随着线程提供的好处,更多的潜在问题也随之产生。在程序中加入多个执行路径会大大增加代码的复杂度。每个线程都需要协调各自的行为,来防止弄乱程序的状态。这是因为位于同一程序的所有线程共享同一个内存空间,它们对所有的这些数据结构都有访问权限。如果两个线程同时计算一份数据,其中一个线程可能将另外线程修改的内容覆盖,那么计算结果就乱套了。即使是使用了恰当的保护方式,我们还是必须注意编译器优化选项(它可能导致代码出现很微妙的错误)。 
 
2)线程相关术语 
在深入研究线程和相关技术之前,必须先了解一些基本术语的定义。 
如果你熟悉Carbon框架中的多核处理服务接口(Multiprocessor Services API),或是熟悉Unix系统,你将会发现术语“任务”(task)在本文档中的含义略有不同。在Mac OS的早期版本中,术语“任务”被用来区别使用Multiprocessor Services创建的和使用Carbon Thread Manager创建的线程。而在Unix系统之上,术语“任务”有时指的是运行中的进程(process)。实际情况中,一个Multiprocessor Services的任务和一个抢占式线程是一样的(注:没有深入的用过Carbon,有不妥之处还望指正)。 
 
考虑到Carbon Thread Manager和Multiprocessor Services的API都是在Mac OS系统上的遗留技术,本文档遵循以下的术语规范: 
1. 术语线程(thread)指独立的代码执行路径。 
2. 术语进程(process)指程序的运行,进程可以包括多个线程。 
3. 术语任务(task)指的是一份可以被执行的工作,是个抽象概念。 
 
3)线程备选方案 
自己创建线程时的一个问题是:它们为你的代码增加了不确定因素。线程是让程序支持并行操作的一种相对低层次的方式,而且比较复杂。如果你没有完全理解自己针对多线成设计的初衷,就很容易遇到同步或时间控制的问题。轻者改变程序的行为,严重的会造成程序崩溃或是弄乱用户的数据。 
 
另一个需要考虑的因素是:你是否真的需要并行操作?线程可以解决在同一进程中同时执行多段代码的问题。在某些情况下,一些工作并不能保证被同时执行。线程可能带来更多的负荷,有内存消耗方面的,抑或是占用CPU时间。你需要研究是否值得为某个任务承担这样的负荷,或者使用其他简单的执行方式。 
 
Table 1-1 列出了可供选择的线程实现方式。表中包含了线程的替代技术(操作对象-- operation object 和Grand central Dispatch),还包含了旨在提高效率的单线程技术。 
 
Table 1-1  Alternative technologies to threads 
Technology   Description 
Operation objects| Mac OS X v10.5后被引入,一个操作对象是一项任务的封装,可以被子线程们来执行。使用这样的封装技术,可以忽略线程管理方面的麻烦,使程序员专注于任务本身。通常情况下,我们搭配使用操作队列对象(operation queue object)来使用操作对象。操作队列对象会管理操作对象在多线程中的执行。具体内容参考Concurrency Programming Guide. 
 
Grand Central Dispatch |Mac OS X v10.6后被引入,Grand Central Dispatch是让我们专注于任务本身而非线程管理的另外的选择。使用Grand Central Dispatch,我们定义一项任务,并将其加入到一个工作队列中,队列会负责在合适的线程中执行任务。队列还会查看可用内核的数量和当前任务负载,从而比自己使用线程更高效的执行任务。 
 
Idle-time notificati***** |对于那些相对来说较小的、优先级低的任务,“空闲时间通知”的方式可以让你在程序空闲的时候执行它们。Cocoa用NSNotificationQueue对象提供空闲时通知功能。要请求空闲时通知,就提交通知对象到默认的NSNotificationQueue(通知队列)。队列会延迟提交通知对象,直到run loop空闲。更多信息请查看Notification Programming Topics. 
 
待续。。。每天会持续更新。 
 
Asynchronous functi***** |系统接口包括了很多异步函数,这些函数本身就自动支持并行操作。它们可能使用系统守护进程来创建自己的线程、执行任务并返回结果(具体的实现方式并不重要,因为它和你的代码是分开的)。在设计程序的时候,先查查这一类支持异步操作的函数,而尽量少在自己定义的线程中用等效的同步函数。 
 
Timers |当遇到某些需要定时执行的小任务,而且这些任务并没有动用线程的必要时,可以考虑在主线程中使用定时器。详细信息请查看“Timer Sources.” 
 
Separate processes |尽管进程相对于线程来说有些“重量级”,如果某项任务和程序无直接关系,创建独立的进程还是有必要的。例如:任务需要申请大量的内存空间或者必须使用root权限执行。我们还可能需要使用64位的服务器进程来计算大数据,而用32位的程序显示运行结果。 
 
4)线程的系统支持 
如果你要将线程加入到现有的代码中去,Mac OS和iOS提供了几种创建线程的技术。另外,这两个系统还为管理和同步线程内的工作提供了支持。接下来的内容描述了几种在Mac OS和iOS上使用线程的关键技术。 
 
Listing 2-2列出了在程序中可能用到的线程技术 
 
Table 1-2  Thread technologies 
Technology 
Description 
Cocoa threads |Cocoa使用NSThread类来实现线程。Cocoa还为NSObject类提供了一些方法来在已有线程中生成新的线程并执行代码。更多信息,请参考“Using NSThread” and “Using NSObject to Spawn a Thread.” 
 
POSIX threads |POSIX线程技术提供了一系列基于C的接口来创建线程。如果你不打算编写Cocoa的程序,那么它是最好的选择。POSIX的接口相对简单易用且扩展性良好,便于自定义线程。更多信息,请参考“Using POSIX Threads” 
 
Multiprocessing Services |Multiprocessing Services是老式的C接口。被用于支持Mac OS较早的版本。这项技术在Mac OS X上可用,但在新的开发项目中应该避免使用,而应使用NSThread和POSIX的线程技术。更多信息,请参考Multiprocessing Services Programming Guide。 
 
从程序的层面上来看,所有线程的行为在本质上应该是一样的——无论是任何的平台。启动线程后,线程会有这几种运行状态:正在运行(running),准备就绪(ready)和被阻塞(blocked)。如果一个线程当前未运行,那么它可能是被阻塞,或者是在等待输入,也有可能已经就绪,但未被安排执行(scheduled)。线程会在这几个状态之间来来回回。直到真正执行完毕退出,进入终结(terminated)状态。 
 
当你创建一个新的线程时,你必须为其指定入口函数(在Cocoa中,叫入口方法(method))。入口函数包括你想要在线程中执行的代码。当函数返回,或当你显式的终结线程时,线程就永远结束了,然后会被系统回收。线程在内存和时间片占用方面代价昂贵,正因为如此,建议你在入口函数中完成大量的工作,或者自己设置一个运行回路(run loop)来循环执行工作。 
 
更多线程可用技术的信息,请参考“Thread Management.” 
 
运行回路(Run Loops) 
 
运行回路是线程中管理异步事件获取的基础。它的工作是为线程监视事件源(event sources)。当事件到来之时,系统唤醒线程并将事件发送给运行回路,然后在传递给你指定的处理者。如果没有需要处理的事件到来,运行回路将线程置于休眠状态。 
 
在线程中使用运行回路不是必须的。但是如果这样做的话,会有更好的用户体验。使用运行回路可以创建长期存在的线程,并尽可能少的占用资源。因为运行回路在无事可做的时候让线程休眠。它减少了轮询(polling)的需要,而轮询会浪费CPU循环数,阻止CPU休眠,从而比较耗电。 
 
要设置一个运行回路,你所要做的只是运行你的线程,获得一个运行回路的对象,“安装”你的事件处理者,并且告诉运行回路去运行。不管是Cocoa还是Carbon都在主线程中自动为你提供了默认运行回路的设置。如果你需要创建一个长期存在的子线程,就需要自己来在线程里定义运行回路了。 
 
关于运行回路的详细信息和例子,请参考“Run Loops.”。 
 
 
同步工具 
 
多线程编程中的一个大麻烦是线程间的资源争夺。如果多个线程同时修改一份资源,问题就来了。减少问题的方法之一就是减少共享资源,保证每个线程都有自己的一份资源可以使用。当然,管理完全独立的资源是不可能的,因此,我们要用到锁(locks),条件控制(conditi*****),原子操作(atomic operati*****)等技术自己控制线程对资源同步的访问。 
 
“锁”为代码提供了“暴力”的保护方式,在同一时刻,代码只能被一个线程执行。其中,“互斥锁”是最常用的一种形式,也被叫做互斥体(mutex)。当一个线程试图获取被其他线程占用的互斥体时,该线程会被阻塞,直到互斥体被释放。几个系统框架提供对互斥体的支持,而它们都基于同样的底层技术。Cocoa提供额外的几种互斥体来支持不同的操作,比如递归操作。更多信息,请参考“Locks.” 
 
除了锁以外,系统支持条件控制。它可以保证程序中任务按照正确的顺序执行。条件控制就像一个“门卫”,它会一直阻塞某个线程。直到特定的条件为真,才允许线程继续执行。POSIX层和Foundatoin框架都直接支持条件控制。(如果你使用操作对象(operation object),你可以设置它们之间的依赖关系,从而起到设置任务操作顺序的目的,这和条件控制很类似)。 
 
并行程序设计中,还可以使用“原子操作”(atomic operation)来保护、同步对数据的访问。原子操作提供了一种轻量级的锁定方案,在对某些标量数据进行数学运算或逻辑运算的时候,就可以使用原子操作。原子操作会使用特定的硬件指令来保证对于某个变量的修改完成后其他的线程才能继续访问。 
 
关于同步工具的详细信息,请查看“Synchronization Tools.” 
 
线程间通信 
虽然好的设计会尽可能减少线程间的通信,但是在某些情况下,线程间的通信是必要的(线程的任务就是为程序工作,但是如果线程的运行结果无法被使用,会有什么影响?)线程可能需要执行新的工作请求或是向程序主线程回报工作进度。在这些情况下,你就需要把一个线程的信息传递给另外一个线程。幸运的是,程序中的线程共享同一个进程空间,这意味着你有很多种通信方案。 
 
线程间通信有很多方式,各有优劣,“Configuring Thread-Local Storage” 表中罗列了在Mac OS X上常用到的通信机制(除了消息队列(message queue)和Cocoa分布式对象(Cocoa distributed object)外,其他也可以在iOS上通用)。详细列表如下: 
 
Table 1-3  Communication mechanisms 
Direct messaging|Cocoa程序支持直接在其他线程中执行方法(perform selector)。这个功能意味着一个线程可以直接在另外一个线程中执行方法。因为要在目标线程中执行,通过这种方法发送的消息会在线程中被自动序列化。关于input sources的详细信息,请查看“Cocoa Perform Selector Sources.”(后章会有详细说明)。 
 
Global variables, shared memory, and objects |另外一种通信的方式就是使用全局变量、共享对象,或是共享内存块。虽然共享变量既快又简单,这种方式相比较直接消息方式更加脆弱。必须使用锁或者其他同步机制来“保护”共享变量。否则,可能导致线程间的竞争状态、数据被损坏,或程序崩溃。 
 
Conditi*****|条件空之是控制线程同步的另一个工具。可以把条件控制看做是一个“门卫”,它只会在特定条件符合的时候才允许线程执行。更多信息,请查看“Using Conditi*****.” 
 
Run loop sources |在线程中加入运行回路源可以让你接收到程序的特定消息。由于运行回路是“事件驱动”的,它会在无事可做的时候会自动让线程休眠,从而提高线程运行的效率。更多关于运行回路和运行回路源的信息,请查看“Run Loops.” 
 
Ports and sockets |基于端口的通信是一种更加复杂的方式,但是它非常稳定。更重要的是,端口和套接字可以被在外部实体的访问,比如说其他的进程(process)和服务(service)。为了提高效率,端口使用运行回路源协助执行,因此线程在端口上无等待数据的时候会自动休眠。更多关于运行回路和基于端口的输入源(input source)方面的信息,请查看“Run Loops.” 
 
Message queues| 多进程的服务中定义了一中先进先出(FIFO)队列的抽象模型来管理接收和传出的数据。虽然消息队列简单且方便,但它不像其他通信技术那样高效。关于如何使用消息队列,请查看Multiprocessing Services Programming Guide. 
 
Cocoa distributed objects|分布式对象是Cocoa技术中的一项,它为基于端口通信的实现提供了一种高级的方式。虽然也可以把分布式对象用在线程间通信中,但是不建议这样做,因为会导致很高的系统开销。分布式对象在进程间通信中可以很好的排上用场,因为进程间通信本身就已经有较高的开销了。 

haoxue2011-10-15 01:58
多线程下文件的代码 
 
.h NSString*FDownLoadDefaultFile; //下载异常状态的默认文件 NSString*FDownLoadLocalFilePath; //下载到本地的保存路径 NSIntegerFDownLoadFileCount; //当前下载的文件总数 NSInteger FDownLoadID; //下载序列ID NSMut  
 
.h 
NSString*FDownLoadDefaultFile;   //下载异常状态的默认文件 
NSString*FDownLoadLocalFilePath; //下载到本地的保存路径 
NSIntegerFDownLoadFileCount;     //当前下载的文件总数 
NSInteger FDownLoadID;            //下载序列ID 
NSMutableArray*FDownLoadFileArr;        //下载到本地的文件 
NSMutableArray*FDownLoadNetworkFilePathArr;    //需要下载到本地的网络文件路径 
NSCondition*FDownloadLockCondition;  //线程锁 
 
//下载文件 
//多线程下载文件 
-(void)DownloadFileByThread : (NSArray *)AFileArr                 //需要下载的文件集合数组 
    ADownloadDirPath : (NSString *)ADownloadFilePath       //需要下载到本地的目录路径,结尾为“/” 
ADownloadDefaultFileURL : (NSString *)ADownloadDeaultFileURL; //如果需要下载的文件不存在,则需要下载的默认文件 
//下载主线程 
-(void)DownloadMainThread : (NSArray *)ADataArr;      //传递到线程中的数据集合, 
      //位置0=本地下载目录 
//位置1=下载文件地址 
      //位置2=默认下载文件 
//位置3=文件所属的位置索引号 

.m 
-(void)SetDownloadFileID : (NSInteger)AID { 
FDownLoadID= AID; 

 
-(void)DownloadFileByThread : (NSArray *)AFileArr 
    ADownloadDirPath : (NSString *)ADownloadFilePath  
ADownloadDefaultFileURL : (NSString *)ADownloadDeaultFileURL { 
NSInteger AFileCount = [AFileArr count]; 
NSString *ALocalFile = ADownloadFilePath; 
NSString *AFileName; 
NSString *AFileURL; 
NSString *ADefaultFile; 
NSMutableArray*ADataArr; 
 
[[NSURLCachesharedURLCache] removeAllCachedResp*****es]; 
FDownLoadNetworkFilePathArr= [[NSMutableArrayalloc] initWithArray:AFileArr]; 
FDownLoadFileArr = [[NSMutableArray alloc] initWithCapacity:AFileCount]; 
FDownLoadFileCount= 0; 
FDownloadLockCondition= [[NSConditionalloc] init]; 
 
for (int i = 0; i < AFileCount; i++) { 
[FDownLoadFileArraddObject:@""]; 

 
for (int i = 0; i < AFileCount; i++) { 
AFileURL = [[NSString alloc] initWithString: [AFileArr objectAtIndex : i]]; 
AFileName = [ALocalFile stringByAppendingString:[self GetFileName:AFileURL]]; 
 
ADataArr = [[NSMutableArray alloc] initWithCapacity:3]; 
[ADataArr addObject:ALocalFile]; 
if([[NSFileManagerdefaultManager] fileExistsAtPath:AFileName] == NO) { 
//NSLog(@"%@",AFileURL); 
[ADataArr addObject:AFileURL]; 
[ADataArr addObject:ADownloadDeaultFileURL]; 
[ADataArr addObject:[[NSStringalloc] initWithFormat:@"%d",i]]; 
 
[NSThreaddetachNewThreadSelector:@selector(DownloadMainThread:) toTarget:selfwithObject:ADataArr]; 
[ADataArr release]; 

else { 
//NSLog(@"%@",AFileName); 
if (AFileURL == @"") { 
ADefaultFile = [ALocalFile stringByAppendingString:[self GetFileName:ADownloadDeaultFileURL]]; 
if ([[NSFileManager defaultManager] fileExistsAtPath:ADefaultFile] == NO) { 
[ADataArr addObject:ADownloadDeaultFileURL]; 
[ADataArr addObject:ADownloadDeaultFileURL]; 
[ADataArr addObject:[[NSStringalloc] initWithFormat:@"%d",i]]; 
[NSThreaddetachNewThreadSelector:@selector(DownloadMainThread:) toTarget:selfwithObject:ADataArr]; 
[ADataArr release]; 

else { 
FDownLoadFileCount++; 
[FDownLoadFileArrreplaceObjectAtIndex:i withObject:ADefaultFile]; 
//[FDownLoadFileArr addObject:ADefaultFile]; 


else { 
FDownLoadFileCount++; 
[FDownLoadFileArrreplaceObjectAtIndex:i withObject:AFileName]; 
//[FDownLoadFileArr setValue:AFileName forKey:[[NSString alloc] initWithFormat:@"%d",i]]; 
//[FDownLoadFileArr addObject:AFileName]; 

 

 

if(FDownLoadFileCount== [FDownLoadNetworkFilePathArrcount]) {   //完成全部下载 
[self.delegate DownLoadFileDidFinish:FDownLoadFileArr AID : FDownLoadID]; 
[FDownLoadNetworkFilePathArrrelease]; 
[FDownLoadFileArrrelease]; 
[FDownloadLockConditionrelease]; 
 


 
-(void)DownloadMainThread : (NSArray *)ADataArr { 
NSString *ALocalFile = [ADataArr objectAtIndex:0]; 
//NSLog(@"%@",[ADataArr objectAtIndex:1]); 
NSURL*AURL = [[NSURLalloc] initWithString:[ADataArr objectAtIndex:1]]; 
NSData *AFileData = [[NSData alloc] initWithContentsOfURL:AURL]; 
if (AFileData != nil) { 
ALocalFile = [ALocalFile stringByAppendingString:[self GetFileName:[ADataArr objectAtIndex:1]]]; 
//NSLog(@"%@",ALocalFile); 
[AFileData writeToFile:ALocalFile atomically:YES]; 

else{    //不存在指定路径的文件,使用默认文件 
NSString *ADefaultFile = [ALocalFile stringByAppendingString:[self GetFileName:[ADataArr objectAtIndex:2]]]; 
if ([[NSFileManager defaultManager] fileExistsAtPath:ADefaultFile] == NO) { 
[AURL release]; 
[AFileData release]; 
AURL = [[NSURLalloc] initWithString:[ADataArr objectAtIndex:2]]; 
AFileData = [[NSData alloc] initWithContentsOfURL:AURL]; 
if (AFileData != nil) { 
ALocalFile = [ALocalFile stringByAppendingString:[self GetFileName:[ADataArr objectAtIndex:1]]]; 
[AFileData writeToFile:ALocalFile atomically:YES]; 

else { 
ALocalFile = [DocumentPathstringByAppendingString:@"default.png"]; 



//NSLog(@"ADataArr = %@",[ADataArr objectAtIndex:3]); 
[FDownloadLockConditionlock];    //上锁 
FDownLoadFileCount++; 
[FDownLoadFileArrreplaceObjectAtIndex:[[ADataArr objectAtIndex:3] intValue] withObject:ALocalFile]; 
[FDownloadLockConditionunlock];  //解锁 
 
[AURL release]; 
[ALocalFile release]; 
[AFileData release]; 
 
if(FDownLoadFileCount== [FDownLoadNetworkFilePathArrcount]) {   //完成全部下载 
[self.delegate DownLoadFileDidFinish:FDownLoadFileArr AID : FDownLoadID]; 
[FDownLoadNetworkFilePathArrrelease]; 
[FDownLoadFileArrrelease]; 
[FDownloadLockConditionrelease]; 


 

haoxue2011-10-15 01:59
Apple's OpenCL——多线程同步 
 
OpenCL 即:Open Computing Language,是由苹果公司起草设计的用于大规模并行计算的计算编程语言。OpenCL 教程:多线程同步。 
我们前几章介绍了OpenCL的一些基本概念以及一些基本的用法。我们之前的例子都是线程独立计算的,相互之间没有任何通信。而这样的计算模型也是GPU最最喜欢的,能完全发挥GPU众核并行计算的优势。 
    今天我们将介绍OpenCL多线程同步技巧。我们下面的例子将是以一个简单的求和算法来描述如何同步一个工作组内的线程以及工作组之间如何同步。 
我们之前介绍过变量的地址属性。用__global修饰的变量存放在显示存储器中,特点是容量很大,但访问速度很慢,并且所有工作项都能访问;而用 __local修饰的变量存放在共享存储器,其特点是速度比全局存储要快很多,并且在同一工作组内的工作项能够对其进行访问,而且每个工作组有自己独立的共享存储器;__private修饰或默认状态下定义的变量是私有的,即存放在寄存器中,其特点是访问速度相当快,基本上一次读或写仅需要1个着色器周期,但它是工作项私有的,并且每个工作项只有若干个寄存器可以进行访问。 
    如果我们让在一个工作组内的线程进行同步,那么我们可以借助共享存储变量来帮我们达成这个目标;而如果是工作组之间的通信,则需要全局存储变量。 
    下面看求和的内核代码: 
__kernel void solve_sum( 
                    __global int input[4096], 
                    __global int output[9] 
                    ) 

    __local int localBuffer[512]; 
 
    size_t item_id = get_local_id(0); 
    size_t gid = get_global_id(0); 
 
    localBuffer[item_id] = input[gid]; 
 
    barrier(CLK_LOCAL_MEM_FENCE); 
 
    if((item_id) == 0) 
    { 
        int s = 0; 
        for(int i = 0; i < 512; i++) 
            s += localBuffer; 
        output[get_group_id(0)] = s; 
        output[8] = get_num_groups(0); 
    } 
}
 
    在以上代码中,一共有4096个工作项,共有8个工作组,这样每个工作组就有512个工作项。这个算法很简单,首先将每个工作组内的工作项存放到共享数组中,等到一个工作组内的所有工作项完成这个动作后,让工作项0对共享存储缓存中的数据进行求和,完成后写入到相应的工作组索引的输出缓存。 
    在上述代码中,get_local_id获得的是当前工作组中的当前工作项索引,在上述代码环境中的范围是0到511。因此,我们可以将localBuffer[item_id] = input[gid];这句改为:localBuffer[gid & 511] = input[gid];这两条语句的语义完全等价。 
    这里要着重介绍的线程同步函数是: 
void barrier (cl_mem_fence_flags flags) 
    这个内建函数对应于处理器的一条指令,其作用是同步一个工作组内的所有工作项。我们现在把工作项看作为一个线程。当其中一个线程执行到barrier时,它会被处理器阻塞住,直到该工作组内所有线程都执行到这个barrier,然后这些线程才能继续执行下去。 
    这里有一个参数flags用于指示存储器栅栏是局部的还是全局的,我们这里只需要局部的,因为这里不需要工作组之间的同步。 
我们把每个工作组计算出来的结果写到输出缓存中。由于输出才8个32位数据,因此在CPU中再拿去计算也变成了小菜一碟。 
    下面附上整个工程的代码 OpenCL_Basic.zip (17 K)  
    上述代码是将每个工作组计算好的结果传送给主机端。那么我们是否能让GPU把这8个结果也一起解决掉呢?答案是肯定的。不过我们这里将会用到OpenCL1.0中的原子操作扩展。这些基于int32位的原子操作在OpenCL1.1中将正式归为语言核心,而不是扩展。我们可以通过OpenCL查询获得 
cl_khr_global_int32_base_atomics是否被支持。如果被支持,那么我们可以用下面的方法: 
__kernel void solve_sum( 
                     __global int input[4096], 
                     __global int output[9] 
                     ) 
 { 
     __local int localBuffer[512];      
     size_t item_id = get_local_id(0); 
     size_t gid = get_global_id(0);    
     localBuffer[item_id] = input[gid];     
     barrier(CLK_LOCAL_MEM_FENCE); 
     if(item_id == 0) 
     { 
         int s = 0; 
         for(int i = 0; i < 512; i++) 
             s += localBuffer
         output[get_group_id(0)] = s;     
         int index = atom_inc(&output[8]); 
         if(index == 7) 
         { 
             mem_fence(CLK_GLOBAL_MEM_FENCE); 
             s = 0; 
             for(index = 0; index < 8; index++) 
                 s += output[index]; 
             output[8] = s; 
         } 
     } 
 }
 
    在上述代码中,我们用了原子累积操作: 
int atom_inc (__global int *p) 
    这个函数是先读取p指针所指地址的内容,然后将该内容递增1,最后写回到这个地址中去,并且返回读到的那个值(即更新以前的值)。整个操作都是不被打断的,因此是一个原子操作。 
    我们在上述代码中,用一个索引来获取返回值,如果索引为7,说明当前线程是最后一个写结果的工作组中的第0个线程。于是,我们利用这个线程把8个结果累加,然后写回到输出缓存。 
    如果有两个线程对同一地址同时执行atom_inc,那么GPU将会进行仲裁,它只允许其中一个执行这一操作,而等到这个操作完成之后,其它线程才能继续,否则,其它要执行此操作的线程都将被处理器阻塞。 
    那么这里由于利用了输出缓存作为全局存储的计数器变量,因此它将不象第一份代码那样作为只写参数,而是要设置为可读可写的参数,并且要把初始数据传入给GPU设备端。 
    下面附上相应的工程和代码 OpenCL_Basic.zip (17 K)  
    下面要讲一下关于Local Memory的一些高级话题。 
    其实OpenCL中的local memory对应于CUDA中的shared memory。在访问共享存储器时,如果多个线程写同一个共享存储器段(memory bank),那么会导致段冲突(bank conflict)。 
    什么是共享存储器段呢?一个共享存储器段就是在共享存储器中的一个32位字(当前主流的中低端GPU均是如此,高级点的则可能是64位或更大)。那么,如果一个工作组的共享存储器空间是128KB的话,则共有128KB / 4B = 32 * 1024个段。 
    如果有两个线程(即工作项)对同一个段进行写操作,那么这些写操作将由原来可以并行写而变成串行化的写,也就是说,总线控制器会对这些多个线程的写进行串行 化,它会选择其中一个线程先写,完了之后再挑选下一个。那么这样一来,多个线程的执行也就从原来的并行操作变成了串行操作,这样会受到很大的性能惩罚。 
    因此,我们在设计算法时应该尽量保证每个线程只对自己相应的共享存储器段进行写操作,而避免有多个线程去写同一个共享存储器段。而像上面示例代码中,由于读写的数据元素都是32位,正好是一个存储器段的大小,并且一个工作组内的每个工作项都以自己id作为索引对共享存储器进行写,这样每个工作项所写的段都是相互独立的,因此这里不会发生段冲突。  

haoxue2011-10-15 02:01
使用NSOperation和NSOperationQueue启动多线程的教程 
 
App Store中的很多应用程序非常的笨重,他们有好的界面,但操作性很差。比如说当程序从网上或本地载入数据的时候,界面被冻结了,用户只能等程序完全载入数据之后才能进行操作。 
  当打开一个应用程序时,iPhone会产生一个包含main方法的线程,所用程序中的界面都是运行在这个线程之中的(table views, tab bars, alerts…),有时候我们会用数据填充这些view,现在问题是如何有效的载入数据,并且用户还能自如的操作程序。方法是启动新的线 程,专门用于数据的下载,而主线程不会因为下载数据被阻塞。 
  不管使用任何编程语言,在实现多线程时都是一件很麻烦的事情。更糟糕的是,一旦出错, 这种错误通常相当糟糕。然而,幸运的是Apple从 Mac OS X 10.5 开始在这方面做了很多的改进,NSThread的引入,使得开发多线程应用程序容易多了。除此之外,它们还引入了两个全新的类:NSOperation和NSOperationQueue。 
  接下来我们通过一个实例来剖析如何使用这两个类实现多线程。这里指示展示这两个类的基本用法,当然这不是使用他们的唯一办法。如果你熟悉java或者它的别的变种语言的话 ,你会发现NSOperation对象很像java.lang.Runnable接口,就像java.lang.Runnable接口那 样,NSOperation类也被设计为可扩展的,而且只有一个需要重写的方法。它就是-(void)main。使用NSOperation的最简单的方 式就是把一个NSOperation对象加入到NSOperationQueue队列中,一旦这个对象被加入到队列,队列就开始处理这个对象,直到这个对 象的所有操作完成。然后它被队列释放。 
  下面的例子中,使用一个获取网页,并对其解析程NSXMLDocument,最后将解析得到的NSXMLDocument返回给主线程。  
PageLoadOperation.h@interface PageLoadOperation : NSOperation { 
    NSURL *targetURL;} 
@property(retain) NSURL *targetURL; 
- (id)initWithURL:(NSURL*)url;@end 
 
PageLoadOperation.m 
#import "PageLoadOperation.h"#import "AppDelegate.h"@implementation PageLoadOperation@synthesize targetURL;- (id)initWithURL:(NSURL*)url;{ 
    if (![super init]) return nil; 
    [self setTargetURL:url]; 
    return self;}- (void)dealloc { 
    [targetURL release], targetURL = nil; 
    [super dealloc]; 

- (void)main  

    NSString *webpageString = [[[NSString alloc] 
    initWithContentsOfURL:[self targetURL]] autorelease]; 
    NSError *error = nil; 
    NSXMLDocument *document = [[NSXMLDocument alloc] 
    initWithXMLString:webpageString  
    opti*****:NSXMLDocumentTidyHTML error:&error]; 
    if (!document) { 
        NSLog(@"%s Error loading document (%@): %@",  
        _cmd, [[self targetURL] absoluteString], error); 
         return; 
    } 
    [[AppDelegate shared] 
    performSelectorOnMainThread:@selector(pageLoaded:) 
         withObject:document waitUntilDone:YES]; 
    [document release]; 

@end 
    正如我们所看到的那样,这个类相当的简单,在它的init方法中接受一个url并保存起来,当main函数被调用的时候,它使用这个保存的url创建一个字 符串,并将这个字符串传递给NSXMLDocumentinit方法。如果加载的xml数据没有出错,数据会被传递给AppDelegate,它处于主线 程中。到此,这个线程的任务就完成了。在主线程中注销操作队列的时候,会将这个NSOperation对象释放。 
AppDelegate.h 
@interface AppDelegate : NSObject { 
    NSOperationQueue *queue; 
}+ (id)shared;- (void)pageLoaded:(NSXMLDocument*)document;@endAppDelegate.m        #import "AppDelegate.h"#import "PageLoadOperation.h"@implementation AppDelegate 
static AppDelegate *shared; 
static NSArray *urlArray; 
- (id)init 

    if (shared) 
    { 
        [self autorelease]; 
        return shared; 
    } 
    if (![super init]) return nil;    NSMutableArray *array = [[NSMutableArray alloc] init];[array addObject:@"http://www.google.com"];[array addObject:@"http://www.apple.com"];[array addObject:@"http://www.yahoo.com"];[array addObject:@"http://www.zarrastudios.com"];[array addObject:@"http://www.macosxhints.com"];urlArray = array;    queue = [[NSOperationQueue alloc] init];shared = self;return self; 
    } 
    •    (void)applicationDidFinishLaunching: 
    (NSNotification *)aNotification 

        for (NSString *urlString in urlArray)  
        { 
        NSURL *url =  
        [NSURL URLWithString:urlString];        PageLoadOperation *plo =  
        [[PageLoadOperation alloc] initWithURL:url]; 
        [queue addOperation:plo]; 
        [plo release]; 
        } 

- (void)dealloc 

        [queue release], queue = nil; 
        [super dealloc]; 

+ (id)shared; 

        if (!shared) { 
            [[AppDelegate alloc] init]; 
        } 
        return shared; 

- (void)pageLoaded:(NSXMLDocument*)document; 

        NSLog(@"%s Do something with the XMLDocument: %@", 
             _cmd, document); 

@end 
 
NSOperationQueue的并行控制(NSOperationQueue Concurrency) 
        在上面这个简单的例子中,我们很难看出这些操作是并行运行的,然而,如果你你的操作花费的时间远远比这里的要长,你将会发现,队列是同时执行这些操作的。幸运的是,如果你想要为队列限制同时只能运行几个操作,你可以使用NSOperationQueue的 setMaxConcurrentOperationCount:方法。例如,[queue setMaxConcurrentOperationCount:2]; 

haoxue2011-10-15 08:35
关于TableView中图片的延时加载 
 
经常我们会用tableView显示很多条目, 有时候需要显示图片, 但是一次从服务器上取来所有图片对用户来浪费流量, 对服务器也是负担.最好是按需加载,即当该用户要浏览该条目时再去加载它的图片。 
 
重写如下方法 
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 

    UIImage *image = [self getImageForCellAtIndexPath:indexPath];  //从网上取得图片 
    [cell.imageView setImage:image]; 

 
这虽然解决了延时加载的问题, 但当网速很慢, 或者图片很大时(假设,虽然一般cell中的图很小),你会发现程序可能会失去对用户的响应. 
原因是UIImage *image = [self getImageForCellAtIndexPath:indexPath]; 这个方法可能要花费大量的时间,主线程要处理这个method. 
所以失去了对用户的响应. 
 
所以要将该方法提出来: 
- (void)updateImageForCellAtIndexPath:(NSIndexPath *)indexPath 

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    UIImage *image = [self getImageForCellAtIndexPath:indexPath]; 
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; 
    [cell.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]; 
    [pool release]; 

然后再新开一个线程去做这件事情 
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 

    [NSThread detachNewThreadSelector:@selector(updateImageForCellAtIndexPath:) toTarget:self withObject:indexPath]; 

 
同理当我们需要长时间的计算时,也要新开一个线程 去做这个计算以避免程序处于假死状态 
 
以上代码只是示例, 还可以改进的更多, 比如从网上down下来一次后就将图片缓存起来,再次显示的时候就不用去下载。
 

haoxue2011-10-15 23:57
线程(重要基础知识) 
 1. 创建一个新的线程: 
[NSThread detachNewThreadSelector:@selector(myMethod)  
        toTarget:self  
        withObject:nil];
 
 
 
  2. 创建线程所调用的方法: 
-(void)myMethod { 
    NSAutoreleasePool 
*pool =[[NSAutoreleasePool alloc] init]; 
                     
    
***code that should be run in the newthread goes here ***[pool release]; 
}
 
 
 
  假如我们需要在线程里面调用主线程的方法函数,就可以用performSelectorOnMainThread来实现: 
[self performSelectorOnMainThread:@selector(myMethod)  
    withObject:nil  
    waitUntilDone:
false]; 
 

haoxue2011-10-16 12:04
(线程)互斥锁 
在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象 

haoxue2011-10-20 00:00
iPhone开发资源汇总(重要) 
 
http://www.cocoachina.com/iphonedev/toolthain/2011/1019/3387.html 
 
 

haoxue2011-10-24 00:13
多线程  
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率线程是在同一时间需要完成多项任务的时候实现的。  
使用线程的好处  
使用线程可以把占据长时间的程序中的任务放到后台去处理  
  ·用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度  
  ·程序的运行速度可能加快  
  ·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如
内存占用等等。  
  还有其他很多使用多线程的好处,这里就不一一说明了。  
所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。 

haoxue2011-10-24 00:42
线程同步 
线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。 当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。 
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。 
因此,关于线程同步,需要牢牢记住的第一点是:线程同步就是线程排队同步就是排队。线程同步的目的就是避免线程“同步”执行。 
关于线程同步,需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。  
关于线程同步,需要牢牢记住的第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。  
关于线程同步,需要牢牢记住的第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。 
  
同步锁 
线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。  
确切的说,是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住,同步锁是加在代码段上的。同步锁不是加在共享资源上,而是加在访问共享资源的代码段上。  
同步锁本身也一定是多个线程之间的共享对象。 
synchronized(同步锁) {  
// 访问共享资源,需要同步的代码段  


haoxue2011-10-24 00:46
多线程下载 
线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。现流行的下载软件都支持多线程。  
 

haoxue2011-10-26 14:25
多线程异步有什么作用?  
异步和多线程是两个概念 
多线程至多个任务同时处理(宏观,根据处理器核心数量有关系)异步指的是:例如:A在买东西,B店员需要去拿东西给A看,这个时候可以A继续看东西,而B去拿东西.等待B拿到后通知A.这个过程A是可以继续工作,在B完成后通知A。当然异步的实现还是依赖于多线程因为有异步回调 
 

haoxue2011-10-26 14:28
线程:最常用的是解决页面大数据卡住问题,用另外线程去取数据,等取到后在刷新到页面上,这样就不会卡主界面,影响用户体验 
异步:也可以用到这情况界面先打开,同时请求数据(这在第2线程做),然后等待响应,响应到后,把数据刷新到界面 
 
异步一般是这样:client注册事件到server,当server接受到请求后再通知client

haoxue2011-10-26 14:30
多线程与异步的区别 
 随着拥有多个硬线程CPU(超线程、双核)的普及,多线程和异步操作等并发程序设计方法也受到了更多的关注和讨论。本文主要是想与园中各位高手一同探讨一下如何使用并发来最大化程序的性能。 
  多线程和异步操作的异同 
  多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别。 
  异步操作的本质 
  所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。 
  线程的本质 
  线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度。 
  异步操作的优缺点 
  因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些初入,而且难以调试。 
  多线程的优缺点 
  多线程的优点很明显,线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。 
  适用范围 
  在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.Net Remoting等跨进程的调用。 
  而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤大雅,如果需要处理大量的并发操作时就不合适了。 
 
                                 

haoxue2011-10-26 14:43
解析多线程 
这篇文章主要从线程创建与启动、线程的同步与锁、线程的交互、线程池等等四个方面简单的讲解一下iphone中的多线程编程。  
AD:  
不管是iphone中还是其他的操作系统,多线程在各种编程语言中都是难点,很多语言中实现起来很麻烦,objective-c虽然源于c,但其多线程编程却相当简单,可以与java相媲美。多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。  
 
一、线程创建与启动  
 
线程创建主要有二种方式:  
 
(id)init; // designated initializer   
(id)initWithTarget:(id)target selector:   
(SEL)selector object:(id)argument;  
当然,还有一种比较特殊,就是使用所谓的convenient method,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。这个方法的接口是:  
 
(void)detachNewThreadSelector:   
(SEL)aSelector toTarget:   
(id)aTarget withObject:   
(id)anArgument  
前两种方法创建后,需要手机启动,启动的方法是:  
 
(void)start;  
二、线程的同步与锁  
 
要说明线程的同步与锁,最好的例子可能就是多个窗口同时售票的售票系统了。我们知道在java中,使用synchronized来同步,而iphone虽然没有提供类似java下的synchronized关键字,但提供了NSCondition对象接口。查看NSCondition的接口说明可以看出,NSCondition是iphone下的锁对象,所以我们可以使用NSCondition实现iphone中的线程安全。这是来源于网上的一个例子:  
 
SellTicketsAppDelegate.h 文件  
 
// SellTicketsAppDelegate.h   
import <UIKit/UIKit.h>  
@interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {   
     int tickets;   
     int count;   
     NSThread* ticketsThreadone;   
     NSThread* ticketsThreadtwo;   
     NSCondition* ticketsCondition;   
     UIWindow *window;   
}   
@property (nonatomic, retain) IBOutlet UIWindow *window;   
@end   
SellTicketsAppDelegate.m 文件   
// SellTicketsAppDelegate.m   
import "SellTicketsAppDelegate.h"   
@implementation SellTicketsAppDelegate   
@synthesize window;   
- (void)applicationDidFinishLaunching:(UIApplication *)application {   
     tickets = 100;   
     count = 0;   
     // 锁对象   
     ticketCondition = [[NSCondition alloc] init];   
     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
     [ticketsThreadone setName:@"Thread-1"];   
     [ticketsThreadone start];   
     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];   
     [ticketsThreadtwo setName:@"Thread-2"];   
     [ticketsThreadtwo start];   
     //[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];   
      // Override point for customization after application launch   
     [window makeKeyAndVisible];   
 
}   
 
- (void)run{   
     while (TRUE) {   
     // 上锁   
         [ticketsCondition lock];   
         if(tickets > 0){   
             [NSThread sleepForTimeInterval:0.5];   
             count = 100 - tickets;   
             NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);   
             tickets--;   
         }else{   
             break;   
         }   
         [ticketsCondition unlock];   
     }   
}   
- (void)dealloc {   
[ticketsThreadone release];   
     [ticketsThreadtwo release];   
     [ticketsCondition release];    
     [window release];   
     [super dealloc];   
}   
@end  
三、线程的交互  
 
线程在运行过程中,可能需要与其它线程进行通信,如在主线程中修改界面等等,可以使用如下接口:  
 
(void)performSelectorOnMainThread:   
(SEL)aSelector withObject:   
(id)arg waitUntilDone:   
(BOOL)wait  
由于在本过程中,可能需要释放一些资源,则需要使用NSAutoreleasePool来进行管理,如:  
 
(void)startTheBackgroundJob {        
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];   
    // to do something in your thread job   
    ...   
    [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];   
    [pool release];   
}  
小结:  
 
对于多线程,在一个程序中,一些独立运行的程序片断叫作线程,利用它编程的概念就叫作多线程处理。多线程处理一个常见的例子就是用户界面。利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开始响应 
 

haoxue2011-10-27 00:40
线程间通信

haoxue2011-10-27 00:45
iPhone 多线程 
iPhone 多线程 
  多线程在各种编程语言中都是难点,很多语言中实现起来很麻烦,objective-c虽然源于c,但其多线程编程却相当简单,可以与java相媲美。这篇文章主要从线程创建与启动、线程的同步与锁、线程的交互、线程池等等四个方面简单的讲解一下iphone中的多线程编程。 
  一、线程创建与启动 
  线程创建主要有二种方式: 
  - (id)init; // designated initializer 
  - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument; 
  当然,还有一种比较特殊,就是使用所谓的convenient method,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。这个方法的接口是: 
  + (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument 
  前两种方法创建后,需要手机启动,启动的方法是: 
  - (void)start; 
  二、线程的同步与锁 
  要说明线程的同步与锁,最好的例子可能就是多个窗口同时售票的售票系统了。我们知道在java中,使用synchronized来同步,而iphone虽然没有提供类似java下的synchronized关键字,但提供了NSCondition对象接口。查看NSCondition的接口说明可以看出,NSCondition是iphone下的锁对象,所以我们可以使用NSCondition实现iphone中的线程安全。这是来源于网上的一个例子: 
  SellTicketsAppDelegate.h 文件 
  // SellTicketsAppDelegate.h 
  import  
  @interface SellTicketsAppDelegate : NSObject { 
  int tickets; 
  int count; 
  NSThread* ticketsThreadone; 
  NSThread* ticketsThreadtwo; 
  NSCondition* ticketsCondition; 
  UIWindow *window; 
  } 
  @property (nonatomic, retain) IBOutlet UIWindow *window; 
  @end 
  SellTicketsAppDelegate.m 文件 
  // SellTicketsAppDelegate.m 
  import "SellTicketsAppDelegate.h" 
  @implementation SellTicketsAppDelegate 
  @synthesize window; 
  - (void)applicationDidFinishLaunching:(UIApplication *)application { 
 
〖黑软手机资讯频道〗 
   
  tickets = 100; 
  count = 0; 
  // 锁对象 
  ticketCondition = [[NSCondition alloc] init]; 
  ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; 
  [ticketsThreadone setName:@"Thread-1"]; 
  [ticketsThreadone start]; 
  ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; 
  [ticketsThreadtwo setName:@"Thread-2"]; 
  [ticketsThreadtwo start]; 
  //[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 
  // Override point for customization after application launch 
  [window makeKeyAndVisible]; 
  } 
  - (void)run{ 
  while (TRUE) { 
  // 上锁 
  [ticketsCondition lock]; 
  if(tickets > 0){ 
  [NSThread sleepForTimeInterval:0.5]; 
  count = 100 - tickets; 
  NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]); 
  tickets--; 
  }else{ 
  break; 
  } 
  [ticketsCondition unlock]; 
  } 
  } 
  - (void)dealloc { 
  [ticketsThreadone release]; 
  [ticketsThreadtwo release]; 
  [ticketsCondition release]; 
  [window release]; 
  [super dealloc]; 
  } 
  @end 
  三、线程的交互 
  线程在运行过程中,可能需要与其它线程进行通信,如在主线程中修改界面等等,可以使用如下接口: 
  - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait 
  由于在本过程中,可能需要释放一些资源,则需要使用NSAutoreleasePool来进行管理,如: 
  - (void)startTheBackgroundJob { 
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  // to do something in your thread job 
  ... 
  [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; 
  [pool release]; 
  }  


haoxue2011-10-31 18:56
使用NSOperation实现异步下载 
 
 
在iphone开发中,异步操作是一个永恒的话题,尤其当iphone手机需要和远程服务器进行交互时,使用异步请求是很普遍的做法。 
通常,这需要NSURLConnection和NSOperation结合起来使用。这方面的资料网络上自然有不少的介绍,不过要找一个能运行的代码也并不容易。许多文章介绍的并不全面,或者使用了过时的SDK,在新IOS版本下并不适用(当前最新的ios是4.2了)。这些代码很经典,但仍然很容易使人误入歧途。 
本文总结了众多文档介绍的方法和代码,揭示了异步操作中的实现细节和初学者(包括笔者)易犯的错误,使后来者少走弯路。 
一、使用NSOperation实现异步请求 
1、新建类,继承自NSOperation。 
[table=initial][tr][td][pre][backcolor=transparent]12345678910111213141516171819202122232425[/pre][/td][td][pre][backcolor=transparent]@interface URLOperation [backcolor=transparent]:[backcolor=transparent]NSOperation [backcolor=transparent]{
     [backcolor=transparent]NSURLRequest
[backcolor=transparent]*
  _request;     [backcolor=transparent]NSURLConnection[backcolor=transparent]*_connection;     [backcolor=transparent]NSMutableData[backcolor=transparent]* _data;     [backcolor=transparent]//构建gb2312的encoding     NSStringEncoding enc;   [backcolor=transparent]}   [backcolor=transparent]-[backcolor=transparent]([backcolor=transparent]id[backcolor=transparent])initWithURLString[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSString[backcolor=transparent]*[backcolor=transparent])url; [backcolor=transparent]@property [backcolor=transparent](readonly[backcolor=transparent]) [backcolor=transparent]NSData[backcolor=transparent]*data; [backcolor=transparent]@end[/pre][/td][/tr][/table] 
接口部分不多做介绍,我们来看实现部分。 
首先是带一个NSString参数的构造函数。在其中初始化成员变量。 
其中enc是 NSStringEncoding 类型,因为服务器返回的字符中使用了中文 ,所以我们通过它指定了一个gb2312的字符编码。 
许多资料中说,需要在NSOperation中重载一个叫做isConcurrent的函数并在其中返回YES,否则不支持异步执行。但是实际上,我们在这里注释了这个重载方法,程序也没有报任何错误,其执行方式依然是异步的。 
[table=initial][tr][td][pre]1234567891011121314151617181920212223242526272829303132333435363738394041424344454647[/pre][/td][td][pre][backcolor=transparent]@implementation URLOperation [backcolor=transparent]@synthesizedata[backcolor=transparent]=_data; [backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]id[backcolor=transparent])initWithURLString[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSString[backcolor=transparent]*[backcolor=transparent])url [backcolor=transparent]{     [backcolor=transparent]if [backcolor=transparent](self [backcolor=transparent]= [backcolor=transparent][self init[backcolor=transparent]][backcolor=transparent]) [backcolor=transparent]{         NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"%@",url[backcolor=transparent]);         _request[backcolor=transparent]= [backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSURLRequest alloc[backcolor=transparent]] initWithURL[backcolor=transparent]:[backcolor=transparent][[backcolor=transparent]NSURLURLWithString[backcolor=transparent]:url         [backcolor=transparent]//构建gb2312的encoding         enc [backcolor=transparent]=CFStringConvertEncodingT*****StringEncoding[backcolor=transparent](kCFStringEncodingGB_18030_2000[backcolor=transparent]);           _data [backcolor=transparent]= [backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSMutableData data[backcolor=transparent]]retain[backcolor=transparent]];     [backcolor=transparent]}     [backcolor=transparent]return self; [backcolor=transparent]} [backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])dealloc[backcolor=transparent]{     [backcolor=transparent][_request release[backcolor=transparent]],_request[backcolor=transparent]=[backcolor=transparent]nil;     [backcolor=transparent][_data release[backcolor=transparent]],_data[backcolor=transparent]=[backcolor=transparent]nil;     [backcolor=transparent][_connection release[backcolor=transparent]],_connection[backcolor=transparent]=[backcolor=transparent]nil;     [backcolor=transparent][super dealloc[backcolor=transparent]][backcolor=transparent]}   [backcolor=transparent]// 如果不重载下面的函数,异步方式调用会出错 [backcolor=transparent]//- (BOOL)isConcurrent { [backcolor=transparent]//  return YES;//返回yes表示支持异步调用,否则为支持同步调用 [backcolor=transparent]//}[/pre][/td][/tr][/table] 
整个类中最重要的方法是start方法。Start是NSOperation类的主方法,主方法的叫法充分说明了其重要性,因为这个方法执行完后,该NSOperation的执行线程就结束了(返回调用者的主线程),同时对象实例就会被释放,也就意味着你定义的其他代码(包括delegate方法)也不会被执行。很多资料中的start方法都只有最简单的一句(包括“易飞扬的博客 “的博文): 
[table=initial][tr][td][pre]1[/pre][/td][td][pre][backcolor=transparent][[backcolor=transparent]NSURLConnection connectionWithRequest[backcolor=transparent]:_request delegate[backcolor=transparent]:self[backcolor=transparent]];[/pre][/td][/tr][/table] 
如果这样的话,delegate方法没有执行机会。因为start方法结束后delegate(即self对象)已经被释放了,delegate的方法也就无从执行。 
所以在上面的代码中,还有一个while循环,这个while循环的退出条件是http连接终止(即请求结束)。 当循环结束,我们的工作也就完成了。 
[table=initial][tr][td][pre]12345678910111213141516171819202122232425[/pre][/td][td][pre][backcolor=transparent]// 开始处理-本类的主方法 [backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])start [backcolor=transparent]{     [backcolor=transparent]if [backcolor=transparent]([backcolor=transparent]![backcolor=transparent][self isCancelled[backcolor=transparent]][backcolor=transparent]) [backcolor=transparent]{         NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"start operation"[backcolor=transparent]);         [backcolor=transparent]// 以异步方式处理事件,并设置代理         _connection[backcolor=transparent]=[backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSURLConnectionconnectionWithRequest[backcolor=transparent]:_request delegate[backcolor=transparent]:self[backcolor=transparent]]retain[backcolor=transparent]];         [backcolor=transparent]//下面建立一个循环直到连接终止,使线程不离开主方法,否则connection的delegate方法不会被调用,因为主方法结束对象的生命周期即终止         [backcolor=transparent]//这个问题参考 http://www.cocoabuilder.com/archive/cocoa/279826-nsurlrequest-and-nsoperationqueue.html         [backcolor=transparent]while[backcolor=transparent](_connection [backcolor=transparent]!= [backcolor=transparent]nil[backcolor=transparent]) [backcolor=transparent]{             [backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSRunLoop currentRunLoop[backcolor=transparent]] runMode[backcolor=transparent]:NSDefaultRunLoopMode beforeDate[backcolor=transparent]:[backcolor=transparent][[backcolor=transparent]NSDate distantFuture[backcolor=transparent]][backcolor=transparent]];            [backcolor=transparent]}     [backcolor=transparent]} [backcolor=transparent]}[/pre][/td][/tr][/table] 
接下来,是NSURLConnection的delegate方法,这部分的代码和大部分资料的介绍是一样的,你可以实现全部的delegate方法,但这里我们只实现其中3个就足够了,其余的方法不用理会。如你所见,你可以在其中添加自己想到的任何代码,包括接收数据,进行字符编码或者做xml解析。 
[table=initial][tr][td][pre]12345678910111213141516171819202122232425262728293031323334353637[/pre][/td][td][pre][backcolor=transparent]#pragma mark NSURLConnection delegate Method [backcolor=transparent]// 接收到数据(增量)时 [backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])connection[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSURLConnection[backcolor=transparent]*[backcolor=transparent])connection     didReceiveData[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSData[backcolor=transparent]*[backcolor=transparent])data [backcolor=transparent]{     NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"connection:"[backcolor=transparent]);     NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"%@",[backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSString alloc[backcolor=transparent]] initWithData[backcolor=transparent]:data encoding[backcolor=transparent]:enc[backcolor=transparent]][backcolor=transparent]);     [backcolor=transparent]// 添加数据     [backcolor=transparent][_data appendData[backcolor=transparent]:data[backcolor=transparent]][backcolor=transparent]}   [backcolor=transparent]// HTTP请求结束时 [backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])connectionDidFinishLoading[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSURLConnection[backcolor=transparent]*[backcolor=transparent])connection[backcolor=transparent]{     [backcolor=transparent][_connection release[backcolor=transparent]],_connection[backcolor=transparent]=[backcolor=transparent]nil;     [backcolor=transparent]//NSLog(@"%@",[[NSString alloc] initWithData:_data encoding:enc]); [backcolor=transparent]} [backcolor=transparent]-[backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])connection[backcolor=transparent]: [backcolor=transparent]([backcolor=transparent]NSURLConnection [backcolor=transparent]*[backcolor=transparent]) connection didFailWithError[backcolor=transparent]: [backcolor=transparent]([backcolor=transparent]NSError[backcolor=transparent]*[backcolor=transparent]) error[backcolor=transparent]{     NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"connection error"[backcolor=transparent])[backcolor=transparent]} [backcolor=transparent]@end[/pre][/td][/tr][/table] 
到此,虽然代码还没有完成,但我们已经可以运行它了。你可以看到c*****ole输出的内容,观察程序的运行状态。 
2、调用NSOperation 
我们的NSOperation类可以在ViewController中调用,也可以直接放在AppDelegate中进行。 
在这里,我是通过点击按钮来触发调用代码的: 
[table=initial][tr][td][pre]12345678910111213141516171819[/pre][/td][td][pre][backcolor=transparent]-[backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])loginClicked[backcolor=transparent]{     [backcolor=transparent]//构造登录请求url     [backcolor=transparent]NSString[backcolor=transparent]* url[backcolor=transparent]=@”http[backcolor=transparent]:[backcolor=transparent]//google.com”;     _queue[backcolor=transparent]= [backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSOperationQueue alloc[backcolor=transparent]] init[backcolor=transparent]];       URLOperation[backcolor=transparent]*operation[backcolor=transparent]=[backcolor=transparent][[backcolor=transparent][URLOperation alloc [backcolor=transparent]]initWithURLString[backcolor=transparent]:url[backcolor=transparent]];     [backcolor=transparent]// 开始处理     [backcolor=transparent][_queue addOperation[backcolor=transparent]:operation[backcolor=transparent]];     [backcolor=transparent][operation release[backcolor=transparent]];[backcolor=transparent]//队列已对其retain,可以进行release; [backcolor=transparent]}[/pre][/td][/tr][/table] 
_queue是一个 NSOperationQueue 对象,当往其中添加 NSOperation 对象后, NSOperation 线程会被自动执行(不是立即执行,根据调度情况)。 
3、KVO编程模型 
我们的NSOperation完成了向服务器的请求并将服务器数据下载到成员变量_data中了。现在的问题是,由于这一切是通过异步操作进行的,我们无法取得_data中的数据,因为我们不知道什么时候异步操作完成,以便去访问_data属性(假设我们将_data定义为属性了),取得服务器数据。 
我们需要一种机制,当NSOperation完成所有工作之后,通知调用线程。 
这里我们想到了KVO编程模型(键-值观察模型)。这是cocoa绑定技术中使用的一种设计模式,它可以使一个对象在属性值发生变化时主动通知另一个对象并触发相应的方法。具体请参考cocoa参考库: http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/CocoaBindings/index.html,以及 http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/KeyValueObserving/Concepts/KVOBasics.html#//apple_ref/doc/uid/20002252 两篇文档。 
首先,我们在NSOperation的子类中添加一个BOOL变量,当这个变量变为YES时,标志异步操作已经完成: 
[table=initial][tr][td][pre]1[/pre][/td][td][pre][backcolor=transparent]BOOL _isFinished;[/pre][/td][/tr][/table] 
在实现中加入这个变量的访问方法: 
[table=initial][tr][td][pre]1234567[/pre][/td][td][pre][backcolor=transparent]- [backcolor=transparent]([backcolor=transparent]BOOL[backcolor=transparent])isFinished [backcolor=transparent]{     [backcolor=transparent]return_isFinished; [backcolor=transparent]}[/pre][/td][/tr][/table] 
cocoa的KVO模型中,有两种通知观察者的方式,自动通知和手动通知。顾名思义,自动通知由cocoa在属性值变化时自动通知观察者,而手动通知需要在值变化时调用 willChangeValueForKey:和didChangeValueForKey: 方法通知调用者。 为求简便,我们一般使用自动通知。 
要使用自动通知,需要在 automaticallyNotifiesObserversForKey方法中明确告诉cocoa,哪些键值要使用自动通知: 
[table=initial][tr][td][pre]123456789101112131415161718192021[/pre][/td][td][pre][backcolor=transparent]//重新实现NSObject类中的automaticallyNotifiesObserversForKey:方法,返回yes表示自动通知。 [backcolor=transparent]+[backcolor=transparent]([backcolor=transparent]BOOL[backcolor=transparent])[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSString[backcolor=transparent]*[backcolor=transparent])key [backcolor=transparent]{     [backcolor=transparent]//当这两个值改变时,使用自动通知已注册过的观察者,观察者需要实现observeValueForKeyPath:ofObject:change:context:方法     [backcolor=transparent]if [backcolor=transparent]([backcolor=transparent][key isEqualToString[backcolor=transparent]:[backcolor=transparent]@[backcolor=transparent]"isFinished"[backcolor=transparent]][backcolor=transparent])     [backcolor=transparent]{         [backcolor=transparent]return[backcolor=transparent]YES;     [backcolor=transparent]}       [backcolor=transparent]return [backcolor=transparent][super automaticallyNotifiesObserversForKey[backcolor=transparent]:key[backcolor=transparent]][backcolor=transparent]}[/pre][/td][/tr][/table] 
然后,在需要改变_isFinished变量的地方,使用 
[table=initial][tr][td][pre]1[/pre][/td][td][pre][backcolor=transparent][self setValue[backcolor=transparent]:[backcolor=transparent][[backcolor=transparent]NSNumbernumberWithBool[backcolor=transparent]:[backcolor=transparent]YES[backcolor=transparent]] forKey[backcolor=transparent]:[backcolor=transparent]@[backcolor=transparent]"isFinished"[backcolor=transparent]];[/pre][/td][/tr][/table] 
方法,而不是仅仅使用简单赋值。 
我们需要在3个地方改变isFinished值为YES, 请求结束时、连接出错误,线程被cancel。请在对应的方法代码中加入上面的语句。 
最后,需要在观察者的代码中进行注册。打开ViewController中调用NSOperation子类的地方,加入: 
[table=initial][tr][td][pre]12345[/pre][/td][td][pre]    [backcolor=transparent]//kvo注册     [backcolor=transparent][operation addObserver[backcolor=transparent]:self forKeyPath[backcolor=transparent]:[backcolor=transparent]@[backcolor=transparent]"isFinished"                    opti*****[backcolor=transparent]:[backcolor=transparent](NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld[backcolor=transparent]) context[backcolor=transparent]:operation[backcolor=transparent]];[/pre][/td][/tr][/table] 
并实现 observeValueForKeyPath 方法: 
[table=initial][tr][td][pre]123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051[/pre][/td][td][pre][backcolor=transparent]//接收变更通知 [backcolor=transparent]-[backcolor=transparent]([backcolor=transparent]void[backcolor=transparent])observeValueForKeyPath[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSString[backcolor=transparent]*[backcolor=transparent])keyPath                       ofObject[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]id[backcolor=transparent])object                         change[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]NSDictionary[backcolor=transparent]*[backcolor=transparent])change                        context[backcolor=transparent]:[backcolor=transparent]([backcolor=transparent]void[backcolor=transparent]*[backcolor=transparent])context [backcolor=transparent]{     [backcolor=transparent]if [backcolor=transparent]([backcolor=transparent][keyPath isEqual[backcolor=transparent]:[backcolor=transparent]@[backcolor=transparent]"isFinished"[backcolor=transparent]][backcolor=transparent]) [backcolor=transparent]{         [backcolor=transparent]BOOLisFinished[backcolor=transparent]=[backcolor=transparent][[backcolor=transparent][change objectForKey[backcolor=transparent]:NSKeyValueChangeNewKey[backcolor=transparent]]intValue[backcolor=transparent]];         [backcolor=transparent]if [backcolor=transparent](isFinished[backcolor=transparent]) [backcolor=transparent]{[backcolor=transparent]//如果服务器数据接收完毕             [backcolor=transparent][indicatorView stopAnimating[backcolor=transparent]];             URLOperation[backcolor=transparent]* ctx[backcolor=transparent]=[backcolor=transparent](URLOperation[backcolor=transparent]*[backcolor=transparent])context;             NSStringEncoding enc[backcolor=transparent]=CFStringConvertEncodingT*****StringEncoding[backcolor=transparent](kCFStringEncodingGB_18030_2000[backcolor=transparent]);             NSLog[backcolor=transparent]([backcolor=transparent]@[backcolor=transparent]"%@",[backcolor=transparent][[backcolor=transparent][[backcolor=transparent]NSString alloc[backcolor=transparent]] initWithData[backcolor=transparent]:[backcolor=transparent][ctx data[backcolor=transparent]]encoding[backcolor=transparent]:enc[backcolor=transparent]][backcolor=transparent]);             [backcolor=transparent]//取消kvo注册             [backcolor=transparent][ctx removeObserver[backcolor=transparent]:self                      forKeyPath[backcolor=transparent]:[backcolor=transparent]@[backcolor=transparent]"isFinished"[backcolor=transparent]];         [backcolor=transparent]}           [backcolor=transparent]}[backcolor=transparent]else[backcolor=transparent]{         [backcolor=transparent]// be sure to call the super implementation         [backcolor=transparent]// if the superclass implements it         [backcolor=transparent][super observeValueForKeyPath[backcolor=transparent]:keyPath                              ofObject[backcolor=transparent]:object                                change[backcolor=transparent]:change                               context[backcolor=transparent]:context[backcolor=transparent]];     [backcolor=transparent]} [backcolor=transparent]}
[/pre][/td][/tr][/table] 
运行程序,查看控制台的输出。 
4、libxml的sax解析接口 
iphone和服务器交互通常使用xml数据交换格式,因此本文中也涉及到了xml文件解析的问题。有许多有名气的xml解析器可供我们选择, 如: BXML,TouchXML,KissXML,TinyXML的第三方库和GDataXML。 
Xml解析分为两类,一类是DOM解析,一类为SAX解析。前者如GDataXML,解析过程中需要建立文档树,操作XML元素时通过树形结构进行导航。DOM解析的特点是便于程序员理解xml文档树结构,API 的使用简单;缺点是速度较SAX解析慢,且内存开销较大。在某些情况下, 比如iphone开发,受制于有限的内存空间(一个应用最多可用10几m的内存), DOM解析无法使用(当然,在模拟器上是没有问题的)。 
libxml2的是一个开放源码库,默认情况下iPhone SDK 中已经包括在内。 它是一个基于C的API,所以在使用上比cocoa的 NSXML要麻烦许多(一种类似c函数的使用方式),但是该库同时支持DOM和SAX解析,其解析速度较快,而且占用内存小,是最适合使用在iphone上的解析器。 从性能上讲,所有知名的解析器中,TBXML最快,但在内存占用上,libxml使用的内存开销是最小的。因此,我们决定使用libxml的sax接口。 
首先,我们需要在project中导入framework:libxml2.dylib。 
虽然libxml是sdk中自带的,但它的头文件却未放在默认的地方,因此还需要我们设置project的build选项:HEADER_SEARCH_PATHS = /usr/include/libxml2,否则libxml库不可用。 
然后,我们就可以在源代码中 #import  了。 
假设我们要实现这样的功能:有一个登录按钮,点击后将用户密码帐号发送http请求到服务器(用上文中介绍的异步请求技术),服务器进行验证后以xml文件方式返回验证结果。我们要用libxml的sax方式将这个xml文件解析出来。 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值