3

Documentドキュメントの名前に関するクエリに従って、クラスのオブジェクトを返すメソッドを作成することに興味があります。

DocumentサブクラスNSManagedObjectに起こりますが、消費者はそれを本当に気にしません。Documentオブジェクトはメソッド内で正常に障害が発生し、そのプロパティにアクセスできます。ただし、メソッドがNSManagedObjectContext終了し、フェッチに使用されたものの割り当てが解除されると、オブジェクトは再び障害になります。

をコンテキストから切り離してNSManagedObject、障害にならないようにする方法はありますか?もちろん、これではオブジェクトへの変更を保存することはできませんが、とにかく私はそれに興味がありません。

私はどちらかに頼ることを嫌います:

  1. NonManagedDocumentクラスを反映したクラスを作成しDocumentます。
  2. NSManagedObjectContextドキュメントに興味がある限り、と一緒にタグ付けします。消費者は気にせず、Documentサブクラスであるという事実を意識する必要はありませんNSManagedObject

ノート:

保持できたとしても、コンシューマーサブクラスとしてsインターフェースにアクセスできるため、これは優れたソリューションではないことを付け加えておきます。これは、予期しないバグにつながる可能性があるため、望ましくありません。しかし、DBに格納されているモデルオブジェクトごとにフリーズドライラッパーを手動で作成するという選択肢は、非常に魅力的ではありません。DocumentNSManagedObjectContextDocumentNSManagedObjectContext

4

6 に答える 6

3

NSManagedObjectsはCoreDataスタックに緊密にバインドされています。エンティティは必ずしもメモリに完全にロードされているわけではなく、関係は確実に完全にロードされていません。まあ、私が知る限り、あなたが求めていることを行うためのサポートされた方法はありません。

あなたはあなたが持っている選択肢をかなりよく説明しました。あなたがそれらを嫌うと聞いて申し訳ありません。;)

とはいえ、実行時にミラーリングクラスを生成することは可能であり、すべてのエンティティを手動で記述して維持する必要はありません。ObjCランタイムを使用すると、いくつかのすばらしいことができます。


編集:NSDictionaryスナップショットに向けたいくつかのスニペット

NSManagedObject* any; // let's imagine you have an instance at hand
NSAttributeDescription* entityDescription = any.entity;

// Attributes
NSDictionary* attributeDescriptions = entityDescription.attributesByName;
for ( NSAttributeDescription* attributeDescription in attributeDescriptions.allValues )
{
    NSString* attributeName = attributeDescription.name;
    NSAttributeType attributeType = attributeDescription.attributeType;
    Class attributeClass = NSClassFromString( attributeDescription.attributeValueClassName );

    …
    id attributePrimeValue = [any primitiveValueForKey: attributeName];
    id attributeValue = [any valueForKey: attributeName];
    …

}

// Relationships
NSDictionary* relationshipDescriptions = entityDescription.relationshipsByName;
for ( NSRelationshipDescription* relationshipDescription in relationshipDescriptions.allValues )
{
    …
}

これを開始点として、属性を反復処理し、属性の名前、タイプ、および値を取得して、NSDictionaryベースのスナップショットを作成できるはずです。関係は同じように繰り返されますが、もちろん、関係で行うことはあなたのニーズにしっかりと結びついています。

于 2012-07-21T08:54:56.443 に答える
3

私はあなたの問題を本当に理解していません。NSManagedObjectから継承するクラスを公開する場合、そのクラスのユーザーには、付随する問題とともにその面付けがあります。

クラスをコンポジションとして公開する場合、管理対象オブジェクトの詳細はクラス内にあり、それらの詳細は簡単に非表示にできます。

必要なときにMOCを作成/破棄できますが、かなり安価です。しかし、それはあまり有益ではないようです。

インターフェイスに使用する単一の管理対象オブジェクトコンテキストを保持することをお勧めします。複数のスレッドがMOCを呼び出すことを心配しているとおっしゃいました。これは有効な懸念事項ですが、NSPrivateQueueConcurrencyTypeでMOCを使用することにより、簡単かつ効率的に克服できます。

そうすれば、オブジェクトをフェッチする必要があるときはいつでも、

[moc performBlockAndWait:^{
    // Fetch the object from the MOC
}];

デッドロックが発生する可能性があるため、同期バージョンの呼び出しには注意してください。

または、フェッチごとに一意のコンテキストを作成するオプションを引き続き使用できます...

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
moc.parentContext = myPrivateQueueContext;
// Now, you can fetch from this MOC, and do what you want with the object
// and when you are done, you just let the MOC dealloc.

NSFetchRequestのオプション、およびMOC自体の設定(具体的には)を確認することをお勧めします。

refreshObject:mergeChanges:
retainsRegisteredObjects

myPrivateQueueContextにオブジェクトを保持するように指示すると、オブジェクトはローカルMOCに保持されます。次に、フェッチを実行するときに、MOCにキャッシュされたバージョンを優先するようにフェッチに指示できます。これにより、ディスクに移動することもありません。

管理対象オブジェクトのobjectIDを保存し、objectRegisteredForID:を呼び出すだけで、さらに利点を得ることができます。これは、すでに登録されているオブジェクトを検索するだけです。答えに反対票を投じた人は誰でも手動でキャッシュをクリーンアップできるので、必要に応じてキャッシュが大きくなりすぎないようにします。

または、existingObjectWithID:error:を呼び出すことができます。これにより、障害が返されることはありません。結果は常に実現されたオブジェクトまたはnilになります。

とにかく、やりたいことはいろいろな方法で簡単に解決できると思います。ただし、ソリューションを過度に設計しないように注意します。

公開するインターフェースを見つけて、それを実装します。後でパフォーマンスの問題を発見した場合は、その時点で対処してください。

** 編集 **

具体的には、ダンラのコメント(および回答に反対票を投じた人)に対処するためです。私はあなたがプライベートMOCから直接データにアクセスすることを意図していませんでした。プライベートMOCにアクセスする場合は、performBlockを使用します。

ただし、任意の同時実行タイプの子を必要な数だけ作成し、それらを心の希望に合わせて使用​​することができます。データベースと通信する必要がある場合は、(適切なコンテキストで)親と直接通信します。

私が言ったことは、プライベートMOCで何かをすることを示唆していませんが、performBlockを呼び出すか、別のMOCの親にします。

于 2012-07-29T21:39:33.073 に答える
2

なぜ隠さないのNSManagedObjectContextですか?あなたの消費者はそれがであるという事実を気にしないかもしれませんNSManagedObjectが、彼らはを使用する必要はありませんNSManagedObjectContext、それはあなたの実装の詳細でしょう。を公​​開する必要はなくNSManagedObjectContext、すべてのドキュメントに1つだけ使用する必要があります。

于 2012-07-26T14:12:02.653 に答える
2

管理対象オブジェクトのコンテキストの割り当てを解除する理由がわかりません。プログラムのどこからでもNSManagedObjectsにアクセスできるように、アプリデリゲートでCore Dataスタックをセットアップしてみませんか?

バックグラウンドタスクを実行する必要がある場合は、メインスレッド(アプリデリゲート内)に1つのNSManagedObjectContextを作成し、バックグラウンドスレッドに別のNSManagedObjectContextを作成してください。

Magical Record(https://github.com/magicalpanda/MagicalRecord/)にチェックインして、Core Dataの操作を大幅に簡素化し、複数のスレッドでコンテキストを適切に管理できるようにすることをお勧めします。

于 2012-07-26T14:35:55.267 に答える
2

@hypercryptの答えに基づいて構築するには:APIが管理対象オブジェクトに基づいているという事実を公開しないのはなぜですか?これは@jodyが提案するものと似ています。

NSManagedObjectContextのカテゴリを使用して、スレッドごとの取得を処理できますNSManagedObjectContext。何かのようなもの:

@implementation NSManagedObjectContext (PerThreadContext)

+(NSManagedObjectContext*)contextForCurrentThread
{
    static OSSpinLock lock = OS_SPINLOCK_INIT ;
    @try 
    {
        OSSpinLockLock( & lock ) ;

        NSDictionary * info = [ NSThread currentThread ].threadDictionary ;
        NSManagedObjectContext * context = [ info valueForKey:@"managedObjectContext" ] ;
        if ( !context )
        {
            context = [ [ NSManagedObjectContext alloc ] initWithConcurrencyType:NSConfinementConcurrencyType ] ;
            context.persistentStoreCoordinator = [ NSPersistentStoreCoordinator sharedCoordinator ] ; // perhaps a category we added to NSPersistentStoreCoordinator? Or use your app delegate, etc...
            [ info setObject:context forKey:@"managedObjectContext" ] ;
        }
    }
    @finally 
    {
        OSSpinLockUnlock( & lock ) ;
    }
    return context ;
}

@end

APIメソッドの1つが呼び出されたとき:

-(NSManagedObject*)getSomeObject
{
    NSManagedObjectContext * context = [ NSManagedObjectContext contextForCurrentThread ] ;

    NSManagedObject * result = ....code...
    return result ;
}

これで、不変性を強制したい場合は、NSManagedObjectにカテゴリを追加できます(またはオブジェクトグラフインスタンスの基本クラスを作成できます)

カテゴリとして:

@interface NSManagedObject (Freezing)
@property ( nonatomic, readonly ) BOOL frozen ;
-(void)freeze ;
@end

@implementation NSManagedObject (Freezing)

-(void)setFrozen:(BOOL)f
{
    objc_setAssociatedObject( self, "_frozen", [ NSNumber numberWithBool:f ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}

-(BOOL)frozen
{
   return [ objc_getAssociatedObject( self, "_frozen" ) boolValue ] ;
}

-(void)freeze
{
    [ self setFrozen:YES ] ;
}

-(BOOL)validateForUpdate:(NSError**)error
{
    if ( self.frozen ) 
    { 
        if ( error ) { *error = [ NSError ... ] ; }
        return NO ; 
    }

    return [ super validateForUpdate:error ] ;
}

-(BOOL)validateForDelete:(NSError**)error
{
    if ( self.frozen ) 
    { 
        if ( error ) { *error = [ NSError ... ] ; }
        return NO ; 
    }

    return [ super validateForDelete:error ] ;
}

@end

最後に、コアデータスタック-(NSDictionary*)dictionaryRepresentationを破棄することに完全に取り掛かっている場合は、NSManagedObjectにカテゴリを追加します。これを使用して、管理対象オブジェクトをコンシューマーに渡す辞書に変換します。(あなたは私がカテゴリーが好きであることがわかりますか?)

コアデータフェッチから返される結果の場合NSDictionaryResultType、フェッチリクエストで結果タイプを使用します。これにより、結果が辞書として返されます。

HTH

于 2012-07-31T20:00:21.993 に答える
2

NSManagedObjectsコアデータスタック内のコンテキストに常にバインドされたままにすることはできません。これは理にかなっています。

考えられる解決策の1つは、 mogeneratorに似たものを作成することですが、これは非管理スナップショットクラスと、これらのコピーを作成する管理クラスのメソッドを自動的に作成します。

NSManagedObjectsあるいは、コンテキストを保持するサブクラスを生成し、それらがサブクラスであるという事実を隠すためのobj -Cトリックがあるかもしれません。そのNSManagedObjectため、コンシューマーはスナップショットのプロパティのみに公開され、NSManagedObject機能全体には公開されません。

于 2012-08-01T17:51:37.063 に答える