NSThread与iPhone应用程序的问题

时间:2010-12-03 20:16:02

标签: iphone objective-c multithreading

我在https://github.com/BigBadOwl/iStreamer-Download-Manager有一个相当复杂的应用程序,它使用Rtmpdump和Ffmpeg库来下载和转换Flash流。我希望释放实际转换的线程,以便用户在下载过程中再次访问屏幕(原因很明显)。

无论如何,只要它在主线程中就可以正常运行,但只要我尝试将它放在一个新线程上,Rtmpdump代码就会崩溃。这是一个记忆的事情,但我看不出我错在哪里。有谁介意告诉我发生了什么事?

有问题的代码部分是https://github.com/BigBadOwl/iStreamer-Download-Manager/blob/master/iplayerFetch.m

- (void)startTheBackgroundJob:(NSArray *) args {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 NSString *thePid = [args objectAtIndex:0];
 NSString *flvPath = [args objectAtIndex:1];
 NSString *mp4Path = [args objectAtIndex:2];
 [self performSelectorOnMainThread:@selector(updateProgressBar:) withObject:flvPath waitUntilDone:NO]; 
 [self getFlashFile:thePid withFlvPathName: flvPath];

 NSFileManager *fileManager = [NSFileManager defaultManager];
 if([fileManager fileExistsAtPath:flvPath]){
  NSNumber *filesize = 0;
  NSDictionary *fileAttributes = [[NSFileManager defaultManager]     fileAttributesAtPath:flvPath traverseLink:NO];
  if(fileAttributes != nil){
   filesize = [fileAttributes objectForKey:NSFileSize];
  }

  if([filesize longLongValue] > 1024){
   [self removeFlvWrapper:flvPath withMp4PathName:mp4Path];
  }
  [fileManager removeItemAtPath:flvPath error:NULL];
 }

 [fileManager release];
 [pool drain];}

    - (void)beginDownload:(NSString *) thePid withDocumentsFolder:(NSString *)         documentsDirectory andTempFolder:(NSString *) tempDirectory {
     NSString *flvPath = [NSString stringWithFormat:@"%@/%@.flv", tempDirectory, thePid];
     NSString *mp4Path = [NSString stringWithFormat:@"%@/%@.mp4", documentsDirectory, thePid];

     NSArray *extraArgs = [[NSArray alloc] initWithObjects:thePid, flvPath, mp4Path, nil];
     [NSThread detachNewThreadSelector:@selector(startTheBackgroundJob:) toTarget:self withObject:extraArgs];}

堆栈跟踪:

#0  getStream (argc=16, argv=0x65a0140) at /Users/colinb/Documents/iStreamer-Download-Manager/rtmpdump.c:746
#1  0x0003c297 in -[iplayerFetch getFlashFile:withFlvPathName:] (self=0x658ced0, _cmd=0x58da66, thePid=0x793e8b0, flvPath=0x65994c0) at /Users/colinb/Documents/iStreamer-Download-Manager/iplayerFetch.m:168
#2  0x0003b776 in -[iplayerFetch startTheBackgroundJob:] (self=0x658ced0, _cmd=0x58daca, args=0x6599490) at /Users/bigbadowl/Documents/iStreamer-Download-Manager/iplayerFetch.m:33
#3  0x019a4d4c in -[NSThread main] ()
#4  0x019a4cd8 in __NSThread__main__ ()
#5  0x97ca385d in _pthread_start ()
#6  0x97ca36e2 in thread_start ()
Program received signal:  “EXC_BAD_ACCESS”.

alt text

2 个答案:

答案 0 :(得分:0)

发生崩溃时,会有回溯。

发布。

您的程序将在调试器中断,并且调用堆栈将在调试器UI中(或者您可以键入'bt

有了这个,崩溃的原因通常很明显。如果没有这个,我们就会批评代码。


线程之间的内存管理在发布的代码中是非常偶然的。 extraArgs被泄露,很有可能。你需要在接收线程中release它(或者一旦你知道接收线程完成它就在主线程中)。请注意,autorelease不能用于将对象的所有权从一个线程转移到另一个线程。

鉴于没有崩溃并声称代码在主线程上工作正常,但在线程时没有,我最好的猜测是你滥用来自多个线程的非线程安全API,可能还会从外部触发UI更新主线。

removeFlvWrapper:*线程安全吗?请记住,Foundation集合类不是线程安全的


您的[self getFlashFile:thePid withFlvPathName: flvPath];方法线程是否安全?它调用的功能怎么样?这只是被激发一次还是你有多个线程?

答案 1 :(得分:0)

通常最好使用排除过程来查找这些类型的错误。

在主线程中运行时,-updateProgressBar:将无法运行,直到下载完成。你应该删除它,直到你发现问题为止。

此外,NSFileManager个实例不是线程安全的,因此您无法使用后台线程中的[NSFileManger defaultManager]。相反,您必须创建一个NSFileManager的新实例,以便从后台线程中使用。例如,[[NSFileManager alloc] init];

您还应尝试在单步执行代码时捕获崩溃。这可以更好地了解问题所在。 callstack中报告的行号(rtmpdump.c:746)没有多大意义。您应该尝试更好地了解崩溃发生的位置。它每次都出现在同一条线上,还是会发生变化?哪条线?

另外,为什么这些声明为静态?

static char **rt_argv;
static int    rt_argc;

在这里将它们声明为静态会使它们成为全局变量,当您尝试运行多个下载线程时,这绝对会导致致命的问题。