4

NSDocumentiCloudドキュメントストレージを実装しようとしている単純なベースのMac OS Xアプリがあります。10.7 SDK でビルドしています。

iCloud ドキュメント ストレージ用にアプリをプロビジョニングし、必要な権利を含めました (AFAICT)。アプリは、ローカル ユビキタス コンテナーの Documents ディレクトリを正しくビルド、実行、および作成します (これにはしばらく時間がかかりましたが、すべて機能しているようです)。NSFileCoordinatorAppleが推奨するAPIを使用しています。UbiquityIdentifierAppleが推奨する正しいものを使用していると確信しています(以下では編集されています)。

私は、この WWDC 2011 ビデオの Apple の iCloud ドキュメント ストレージのデモ手順に厳密に従いました。

セッション 107 自動保存と Lion のバージョン

私のコードは、そのデモのコードとほとんど同じに見えます。

ただし、アクションを呼び出して現在のドキュメントをクラウドに移動すると、メソッドを呼び出すときに活性の問題が発生します。-[NSFileManager setUbiquitous:itemAtURL:destinationURL:error:]それは二度と戻りません。

これが私のNSDocumentサブクラスの関連コードです。これは、Apple の WWDC デモ コードとほぼ同じです。これはactionであるため、(Apple のデモ コードが示したように) メイン スレッドで呼び出されます。-setUbiquitous:itemAtURL:destinationURL:error:メソッドが呼び出されると、最後にデッドロックが発生します。バックグラウンド スレッドに移動しようとしましたが、それでも戻りません。

決して到着しないシグナルを待っている間、セマフォがブロックしているようです。

このコードをデバッガーで実行すると、ソース URL と宛先 URL が正しいように見えるので、それらが正しく計算されていることはほぼ確実であり、ディレクトリがディスク上に存在することを確認しました。

私は明らかに間違ったことをしていて、-setUbiquitous二度と戻ってこないのでしょうか?

- (IBAction)moveToOrFromCloud:(id)sender {
    NSURL *fileURL = [self fileURL];
    if (!fileURL) return;

    NSString *bundleID = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSString *appID = [NSString stringWithFormat:@"XXXXXXX.%@.macosx", bundleID];

    BOOL makeUbiquitous = 1 == [sender tag];

    NSURL *destURL = nil;
    NSFileManager *mgr = [NSFileManager defaultManager];
    if (makeUbiquitous) {
        // get path to local ubiquity container Documents dir
        NSURL *dirURL = [[mgr URLForUbiquityContainerIdentifier:appID] URLByAppendingPathComponent:@"Documents"];
        if (!dirURL) {
            NSLog(@"cannot find URLForUbiquityContainerIdentifier %@", appID);
            return;
        }

        // create it if necessary
        [mgr createDirectoryAtURL:dirURL withIntermediateDirectories:NO attributes:nil error:nil];

        // ensure it exists
        BOOL exists, isDir;
        exists = [mgr fileExistsAtPath:[dirURL relativePath] isDirectory:&isDir];
        if (!(exists && isDir)) {
            NSLog(@"can't create local icloud dir");
            return;
        }

        // append this doc's filename
        destURL = [dirURL URLByAppendingPathComponent:[fileURL lastPathComponent]];
    } else {
        // get path to local Documents folder
        NSArray *dirs = [mgr URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
        if (![dirs count]) return;

        // append this doc's filename
        destURL = [[dirs objectAtIndex:0] URLByAppendingPathComponent:[fileURL lastPathComponent]];
    }

    NSFileCoordinator *fc = [[[NSFileCoordinator alloc] initWithFilePresenter:self] autorelease];
    [fc coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForMoving writingItemAtURL:destURL options:NSFileCoordinatorWritingForReplacing error:nil byAccessor:^(NSURL *fileURL, NSURL *destURL) {

        NSError *err = nil;
        if ([mgr setUbiquitous:makeUbiquitous itemAtURL:fileURL destinationURL:destURL error:&err]) {
            [self setFileURL:destURL];
            [self setFileModificationDate:nil];
            [fc itemAtURL:fileURL didMoveToURL:destURL];
        } else {
            NSWindow *win = ... // get my window
            [self presentError:err modalForWindow:win delegate:nil didPresentSelector:nil contextInfo:NULL];
        }
    }];
}
4

5 に答える 5

5

これらが問題の原因であるかどうかはわかりませんが、次のような問題が発生しています。

  • -[NSFileManager URLForUbiquityContainerIdentifier:]しばらく時間がかかる場合があるため、メイン スレッドで呼び出すべきではありません。 このブログ投稿の「Ubiquity Container の検索」セクションを参照してください。

  • NSFileManagerこれをグローバル キューで行うということは、おそらく+defaultManager.

  • 調整された書き込みの部分に渡されたブロックはbyAccessor、特定のスレッドで呼び出されることが保証されていないため、NSWindowsそのブロック内からモーダル ダイアログや何かを操作したり表示したりしないでください (メイン キューにディスパッチした場合を除く)。 )。

  • NSFileManager物事が完了するまで、ほぼすべての iCloud メソッドがブロックされると思います。適切に構成されていないため、メソッドがブロックされて返されない可能性があります。設定を 2 回、3 回確認します。再現ケースを単純化してみてください。それでも機能しない場合は、バグを報告するか、DTS に連絡してみてください。

于 2011-11-12T04:14:10.333 に答える
3

これを Twitter で共有しましたが、NSDocument を使用する場合、NSFileCoordinator のようなことをする必要はないと思います。ドキュメントをユビキタスにして保存するだけです。

于 2011-11-16T22:28:28.353 に答える
1

わかりました、それで、ダンクのアドバイスを使用して、最終的に問題を解決することができました. 私が抱えていた問題は次のとおりであると確信しています。

  • 私がガイドとして使用していた WWDC ビデオが作成された後、Apple はユビキタス API を完成させ、サブクラスNSFileCoordinator内から保存する際にオブジェクトを使用する必要性を取り除きました。NSDocument

したがって、キーは、の作成NSFileCoordinatorと呼び出しの両方を削除することでした-[NSFileCoordinator coordinateWritingItemAtURL:options:writingItemAtURL:options:error:byAccessor:]

また、この作業をバックグラウンド スレッドに移動しましたが、問題を修正するために絶対に必要というわけではありませんでした (ただし、それは確かに良い考えでした)。

将来の勇敢な Xcoder を支援することを期待して、完成したコードを Google の Web クローラーに送信します。

動作する私の完全なソリューションは次のとおりです。

- (IBAction)moveToOrFromCloud:(id)sender {
    NSURL *fileURL = [self fileURL];
    if (!fileURL) {
        NSBeep();
        return;
    }

    BOOL makeUbiquitous = 1 == [sender tag];

    if (makeUbiquitous) {
        [self displayMoveToCloudDialog];
    } else {
        [self displayMoveFromCloudDialog];
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self doMoveToOrFromCloud:makeUbiquitous];
    });
}


- (void)doMoveToOrFromCloud:(BOOL)makeUbiquitous {
    NSURL *fileURL = [self fileURL];
    if (!fileURL) return;

    NSURL *destURL = nil;
    NSFileManager *mgr = [[[NSFileManager alloc] init] autorelease];
    if (makeUbiquitous) {
        NSURL *dirURL = [[MyDocumentController instance] ubiquitousDocumentsDirURL];
        if (!dirURL) return;

        destURL = [dirURL URLByAppendingPathComponent:[fileURL lastPathComponent]];
    } else {
        // move to local Documentss folder
        NSArray *dirs = [mgr URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
        if (![dirs count]) return;

        destURL = [[dirs firstObject] URLByAppendingPathComponent:[fileURL lastPathComponent]];
    }

    NSError *err = nil;
    void (^completion)(void) = nil;

    if ([mgr setUbiquitous:makeUbiquitous itemAtURL:fileURL destinationURL:destURL error:&err]) {
        [self setFileURL:destURL];
        [self setFileModificationDate:nil];

        completion = ^{
            [self hideMoveToFromCloudDialog];
        };

    } else {
        completion = ^{
            [self hideMoveToFromCloudDialog];
            NSWindow *win = [[self canvasWindowController] window];
            [self presentError:err modalForWindow:win delegate:nil didPresentSelector:nil contextInfo:NULL];
        };
    }

    dispatch_async(dispatch_get_main_queue(), completion);
}
于 2012-01-23T02:11:08.083 に答える
1

うーん、

コードでユビキタス コンテナ識別子を使用しないようにしましたか (申し訳ありませんが、プロジェクトから切り取ったため、一部を疑似コーディングしました):

NSFileManager *fm = [NSFileManager defaultManager];
NSURL *iCloudDocumentsURL = [[fm URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents"];
NSURL *iCloudFileURL = [iCloudDocumentsURL URLByAppendingPathComponent:[doc.fileURL lastPathComponent]];
ok = [fm setUbiquitous:YES itemAtURL:doc.fileURL destinationURL:iCloudRecipeURL error:&err];
NSLog(@"doc moved to iCloud, result: %d (%@)",ok,doc.fileURL.fileURL);

そして、資格ファイルで:

<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
    <string>[devID].com.yourcompany.appname</string>
</array>

それ以外は、あなたのコードは私のものとほとんど同じに見えます(これは動作します-私は NSDocument を使用していないが、すべて自分でローリングしています)。

于 2011-11-16T22:16:15.010 に答える
1

これが、iCloud にアクセスしているコードの最初の場所である場合、Console.app で次のようなメッセージを探します。

taskgated: com.apple.developer.ubiquity-container-identifiers エンタイトルメントの使用が許可されていないため、yourAppID [pid 13532] を強制終了しました

このメッセージが表示されるたびに、アプリ コンテナーを削除してください。~/Library/Containers/<yourAppID> Console.app には、この問題の解決に役立つその他の有用なメッセージが含まれている場合もあります。アプリ コンテナーを削除することが、iCloud を使用する場合の新しいクリーン プロジェクトであることがわかりました。

于 2011-11-18T21:35:01.873 に答える