2012-03-28 55 views
3

我有一個nsthread,在這個之內的while循環。它從主方法的「線程安全」隊列中獲取對象。當我離開包含此nsthread對象的UIViewController時,我調用nsthread cancel方法,但它不會停止,因爲它被「queueLock」NSCondition鎖定。當我回到這個UIViewController時,會創建一個新的nsthread,並將這些對象組成隊列,但是前一個線程仍然退出,並且它們都嘗試使用隊列中的同一個對象,這會導致內存管理問題。我的問題是當我離開UIViewController時應該如何阻止這個線程。iOS - Objective-C - 如何在等待時停止NSThread?

NSThread主要方法:

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
while ([self isCancelled] == NO) { 
    RenderRequest *renderRequest = [queue get]; 
    [self doRender:renderRequest]; 
    [renderRequest release];  
} 

[pool drain]; 

這是隊列類的get方法:

- (id) get { 
    id toRet = nil; 
    [queueLock lock]; 
    @try { 
     while ([queueContents count] == 0) { 
      [queueLock wait]; 
     } 

     toRet = [queueContents lastObject]; 
     [queueContents removeLastObject]; 
    } 

    @finally { 
     [queueLock unlock]; 
     return toRet; 
    } 
} 

謝謝!

回答

1

我寫了一個簡單的演示,希望這可以幫助你:)

演示.h

#import <Foundation/Foundation.h> 

@interface test : NSObject 
{ 
    NSCondition *queueCondition; 
    NSThread *queueThread; 

    NSMutableArray *queueTask; 

    NSTimer *timer; 
} 
- (id)init; 
@end 

demo.m

#import "demo.h" 

@interface demo (PrivateMethods) 
- (void)threadTest; 
- (void)cancelThread; 
- (void)addTask; 
@end 


@implementation demo 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     if (!queueThread) { 
      if (!queueCondition) { 
       queueCondition = [[NSCondition alloc] init]; 
      } 

      if (!queueTask) { 
       queueTask = [[NSMutableArray alloc] initWithCapacity:5]; 
      } 

      queueThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil]; 
      [queueThread start]; 

      [self performSelector:@selector(cancelThread) withObject:nil afterDelay:10]; 

      if (!timer) { 
       timer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addTask) userInfo:nil repeats:YES] retain]; 
      } 
     } 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [queueThread release]; 
    [queueCondition release]; 
    [queueTask release]; 
    [timer invalidate]; 
    [timer release]; 
    [super dealloc]; 
} 

- (void)threadTest 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    while (![[NSThread currentThread] isCancelled]) { 
     [queueCondition lock]; 
     [queueCondition wait]; 

     if ([queueTask count] == 0) { 
      [queueCondition unlock]; 
      continue; 
     } 

     NSString *str = nil; 
     while ((str = [queueTask lastObject])) { 
      NSLog(@"getTask: %@", [queueTask lastObject]); 
      [queueTask removeLastObject]; 

     } 

     [queueCondition unlock]; 
    } 
    NSLog(@"threadTest end"); 
    [pool drain]; 
} 

- (void)addTask 
{ 
    [queueCondition lock]; 
    if (!queueTask) { 
     queueTask = [[NSMutableArray alloc] initWithCapacity:5]; 
    } 
    [queueTask addObject:@"new task"]; 
    [queueCondition signal]; 
    NSLog(@"add: new task"); 
    [queueCondition unlock]; 
} 

- (void)cancelThread 
{ 
    [timer invalidate]; 

    [queueThread cancel]; 

    [queueCondition lock]; 
    [queueCondition signal]; 
    [queueCondition unlock]; 
} 
@end 
+0

謝謝,我會試一試。 – 2012-03-29 20:29:33

+1

感謝您的寫作!我用這個很好的演示來了解NSThread以及如何排隊數據。雖然現在每個人都指向GCD,但我認爲記住NSThread'親手做'選項很重要。 – LEO 2013-09-15 01:58:34

0
- (id) get 
{ 
    id toRet = nil; 
    [queueLock lock]; 
    @try 
    { 
     while ([queueContents count] == 0) 
     { 
      [queueLock wait]; 
      if ([self isCancelled]) return nil; // stop waiting 
     } 

     toRet = [queueContents lastObject]; 
     [queueContents removeLastObject]; 
    } 
    @finally 
    { 
     [queueLock unlock]; 
     return toRet; 
    } 
} 

線程主要

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 

while ([self isCancelled] == NO) 
{ 
    RenderRequest *renderRequest = [queue get]; 
    if (renderRequest == nil) break; // should stop 
    [self doRender:renderRequest]; 
    [renderRequest release];  
} 

[pool drain]; 

那麼你就可以取消該線程,並通知queueLock停止等待

+0

謝謝。我嘗試了這一點,但第二次打開UIViewController我得到一個「[不是類型發佈]:消息發送到釋放」錯誤在[pool drain]行,並且第一個線程仍然存在。我在UIViewController的viewDidDisappear方法中調用線程cancel方法。 – 2012-03-29 13:39:44