【问题标题】:iPhone (iOS): copying files from main bundle to documents folder causes crashiPhone (iOS):将文件从主包复制到文档文件夹会导致崩溃
【发布时间】:2011-03-15 08:48:57
【问题描述】:

我正在尝试设置我的应用程序,以便在首次启动时,将位于主包中“Populator”文件夹中的一系列文件复制到文档目录中。

我目前的实现如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Populator"];
  NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:@"Files"];
  NSLog(@"Source Path: %@\n Documents Path: %@ \n Folder Path: %@", sourcePath, documentsDirectory, folderPath);

  NSError *error;

  [[NSFileManager defaultManager] copyItemAtPath:sourcePath 
                                        toPath:folderPath
                                         error:&error];

  NSLog(@"Error description-%@ \n", [error localizedDescription]);
  NSLog(@"Error reason-%@", [error localizedFailureReason]);
  ....
  return YES;
}

但是,它在第​​一次运行时会崩溃,并显示以下控制台消息(但文件已被复制)。下次打开应用时,它不会崩溃。

    2010-07-13 15:14:26.418 AppName[5201:207] Source Path: /Users/jack/Library/Application Support/iPhone Simulator/3.2/Applications/1076C1FA-60B0-4AC7-8CD4-74F81472DAE6/AppName.app/Populator
 Documents Path: /Users/jack/Library/Application Support/iPhone Simulator/3.2/Applications/1076C1FA-60B0-4AC7-8CD4-74F81472DAE6/Documents 
 Folder Path: /Users/jack/Library/Application Support/iPhone Simulator/3.2/Applications/1076C1FA-60B0-4AC7-8CD4-74F81472DAE6/Documents/Files
2010-07-13 15:14:26.466 AppName[5201:207] *** +[AppNameAppDelegate localizedDescription]: unrecognized selector sent to class 0xa79c
2010-07-13 15:14:26.475 AppName[5201:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[AppNameAppDelegate localizedDescription]: unrecognized selector sent to class 0xa79c'
2010-07-13 15:14:26.495 AppName[5201:207] Stack: (
    40911435,
    2569270537,
    41183227,
    40645910,
    40642578,
    9142,
    2815466,
    2819475,
    2844680,
    2826401,
    2858055,
    49271164,
    40452156,
    40448072,
    2817668,
    2850273,
    8776,
    8630
)

有人对出了什么问题有任何建议吗?我已经设置了一些代码来实现“仅在首次启动时”功能,但为了清楚起见,这里没有包含它。

谢谢

【问题讨论】:

    标签: iphone ios4 nsfilemanager


    【解决方案1】:
       NSError *error;
    

    您正在声明一个局部变量而不对其进行初始化。因此,它会被垃圾填满。

      [[NSFileManager defaultManager] copyItemAtPath:sourcePath 
                                            toPath:folderPath
                                             error:&error];
    

    如果这一行没有发生错误,error 的垃圾状态仍将保持。

      NSLog(@"Error description-%@ \n", [error localizedDescription]);
    

    现在您将消息发送到某个随机的、未初始化的位置。这是崩溃的根源。


    为避免这种情况,请将error 初始化为nil

    NSError* error = nil;
    //             ^^^^^
    

    或仅当 -copyItemAtPath:… 返回 NO(其中正确填充了 error)时才打印错误。

    if (![[NSFileManager defaultManager] copyItemAtPath:sourcePath ...]) {
      NSLog(...); 
    }
    

    【讨论】:

    • 这是错误的。 Apple 的 API 不承诺在没有错误的情况下保持错误参数不变——它可能是垃圾。正如其他发帖者所提到的,正确的方法是如果返回成功则忽略错误参数。
    • Kenny,如果发生错误,错误将指向新分配的错误对象。这是一个输出参数。指针的地址 (!) 被赋予该方法。不应初始化错误。请参阅 Apple 文档中的错误处理。
    【解决方案2】:

    我刚刚通读了我的代码并发现了问题。正如肖恩·爱德华兹(Sean Edwards)在上面指出的那样,如果成功就没有错误 - 因此会崩溃。

    这是我给感兴趣的人的新代码:

    if([[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:folderPath error:&error]){
        NSLog(@"File successfully copied");
    } else {
        NSLog(@"Error description-%@ \n", [error localizedDescription]);
        NSLog(@"Error reason-%@", [error localizedFailureReason]);
    }
    

    【讨论】:

    • 出于某种原因,localizedDescription 没有提供信息让我知道该文件已经存在(而且我看不到它是如何本地化的,它给了我英语)。我不得不使用[error description](仍然是英文)。
    【解决方案3】:

    我对 iPhone 编程或目标 C 了解不多,但出于好奇,如果复制操作确实成功,那么在这种情况下会出现什么错误?如果没有错误,会不会是日志行崩溃了?

    [编辑] 另外,你可以像那样复制子目录的全部内容吗? (同样,我不熟悉 iOS API,只是根据我对其他语言/API 的了解来识别可能的错误来源)

    【讨论】:

      【解决方案4】:

      你应该检查文件是否已经存在!然后复制。

      + (BOOL) getFileExistence: (NSString *) filename
      {
          BOOL IsFileExists = NO;
      
          NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *documentsDir = [documentPaths objectAtIndex:0];
          NSString *favsFilePath = [documentsDir stringByAppendingPathComponent:filename];
      
          NSFileManager *fileManager = [NSFileManager defaultManager];
      
          // Check if the database has already been created in the users filesystem
          if ([fileManager fileExistsAtPath:favsFilePath])
          {
              IsFileExists = YES;
          }
          return IsFileExists;
      }
      
      + (NSString *)dataFilePath:(NSString *)filename {
      
          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *docDirectory = [paths objectAtIndex:0];
          return [docDirectory stringByAppendingPathComponent:filename];
      }
      
      - (void)copyFileToLocal:(NSString *)filename
      {
      
          if (![AppDelegate getFileExistence:filename])
          {
              NSError *error;
              NSString *file = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
      
              if (file)
              {
                  if([[NSFileManager defaultManager] copyItemAtPath:file toPath:[AppDelegate dataFilePath:filename] error:&error]){
                      NSLog(@"File successfully copied");
                  } else {
      
                      [[[UIAlertView alloc]initWithTitle:NSLocalizedString(@"error", nil) message: NSLocalizedString(@"failedcopydb", nil)  delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", nil)  otherButtonTitles:nil] show];
                      NSLog(@"Error description-%@ \n", [error localizedDescription]);
                      NSLog(@"Error reason-%@", [error localizedFailureReason]);
                  }
                  file = nil;
              }
          }
      }
      

      NSLocalizedString 是应用程序的本地化字符串。

      【讨论】:

        【解决方案5】:

        在知道有错误之前就记录了错误

        将代码放入 if 块中

        if(error)
        {
         NSLog(@"Error description-%@ \n", [error localizedDescription]);
         NSLog(@"Error reason-%@", [error localizedFailureReason]);
        }
        

        更详细地描述您的问题:错误点的指针 ANYWHERE 并且该对象无法识别该消息。因此你得到一个例外

        【讨论】:

          【解决方案6】:

          作为一般编程实践,最好使用默认值初始化变量:

          NSError *error = nil;
          

          在 Objective-C 中,向nil 发送消息是有效的。因此,在您的情况下,如果将错误变量初始化为 nil,则不会导致崩溃。

          有关该主题的更多信息,请查看Sending Messages to nilhttps://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html 部分

          【讨论】:

            猜你喜欢
            • 2011-03-15
            • 1970-01-01
            • 2011-05-02
            • 2013-09-03
            • 1970-01-01
            • 2018-03-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多