3

複数の NSManagedObjectModel をマージしたい理由はいくつかあります。Web を検索すると、すべての応答は、それは不可能であるか、1 つ以上の関係を共有する 2 つの無関係なエンティティに対してのみ可能であるというものです。たとえば、これこのリンクを参照してください。

ただし、エンティティが(親子のように)関連している場合や、属性が複数のモデルに分散している場合でも、もう少し作業を行うと、NSManagedObjectModelsをマージすることが可能です(私は思います)。

ただし、Xcode モデル エディタではすぐには表示されず、すぐに使用できるトランジションは (おそらく) 機能しません。

以下の回答では、コアデータに関する私の観察と、複数のモデルのマージに関する私のコードを示しています。バグを見つけた場合、または改善のための提案がある場合は、ここに返信してください。

4

2 に答える 2

3

私が気づいたいくつかのこと:

  1. NSPropertyDescription (属性、関係) をコピーすると、そのすべての値がコピーされますが、それが属するエンティティはコピーされません。destinationEntity と inverseRelationship についても同様です。

  2. したがって、コピーされた NSPropertyDescription をエンティティに追加する必要があります。その結果、そのエンティティのすべての子エンティティも自動的にプロパティを取得します。

  3. NSEntityDescription のコピーには、親エンティティは含まれません。そのため、(NSManagedObjectEntity の) ツリーは手動で再構築する必要があります。

  4. エンティティの親を設定すると、その (子) エンティティは、すべての親プロパティを即座に自動的に継承します。つまり、エンティティにその属性を尋ねると、このエンティティはすでにすべての属性を知っています。最初にその親を照会しません。(合理的な仮定)

  5. エンティティをモデルに追加すると、宛先エンティティと、追加されたエンティティの関係の説明の逆関係の説明が入力されます。

  6. 使用する前にエンティティまたはプロパティの名前を設定しないと、コア データからエラーが発生します。これは、値の側面ではなく、名前によるコピーです。

  7. 同じ名前のプロパティを既に持っているエンティティにプロパティを追加すると (それ自体から、またはその祖先から継承されたもの)、コア データが不平を言うようになります。

これは次のコードに変換されます。

extension NSPropertyDescription
{
   var isPlaceholder : Bool { return self.userInfo?["isPlaceholder"] != nil }
}

extension NSEntityDescription
{
   var isPlaceholder : Bool { return self.userInfo?["isPlaceholder"] != nil }
}

func mergeModels(models: [NSManagedObjectModel]) -> NSManagedObjectModel?
{
    var entities : [String : NSEntityDescription] = [:]

    //support functions
    let makeEntity : String -> NSEntityDescription = { entityName in
        let newEntity = NSEntityDescription()
        entities[entityName] = newEntity
        newEntity.name = entityName
        return newEntity
    }

    let setParent : (String, NSEntityDescription) -> () = { parentName, child in
        if let parent = entities[parentName]
        {
            parent.subentities.append(child)
        }
        else //parent has not yet been encountered, so generate it
        {
            let newParentEntity = makeEntity(parentName)
            newParentEntity.subentities.append(child)
        }
    }


    //rebuild model: generate new description for each entity and add non-placeholder properties
    for model in models
    {
        for entity in model.entities
        {
            guard let entityName = entity.name else { fatalError() }
            let mergedEntity = entities[entityName] ?? makeEntity(entityName)

            //set entity properties
            if !entity.isPlaceholder
            {
                mergedEntity.abstract = entity.abstract
                mergedEntity.managedObjectClassName = entity.managedObjectClassName
            }

            //set parent, if any
            if mergedEntity.superentity == nil, //no parent set
                let parentName = entity.superentity?.name //but parent is required
            {
                setParent(parentName, mergedEntity)
            }

            //set properties
            for property in entity.properties
            {
                if property.isPlaceholder ||
                    mergedEntity.properties.contains({$0.name == property.name})
                { continue }

                let newProperty = property.copy() as! NSPropertyDescription
                mergedEntity.properties.append(newProperty)

            }
        }
    }

    //generate final model
    let mergedModel = NSManagedObjectModel()
    mergedModel.entities = Array(entities.values) //sets the destination entity and inverse relationship descriptions
    return mergedModel
}

managedObjectModel (xcode エディター) で、エンティティおよび/またはプロパティのユーザー情報辞書に「プレースホルダー」フラグを設定します。

ユーザー情報ディクショナリに追加のキーを設定して、主要なエンティティ/属性/関係 (設定) を持つモデルを指定し、このコード フラグメントを適切に調整することで、モデル生成を改良できます。

ただし、複数のモデルの使用を避けることができる場合は、使用しないでください。標準的な単一モデルのアプローチに固執することで、あなたの人生はずっとシンプルになります。

[免責事項: 私の知る限り、このコードは機能するはずです。ただし、保証はありません。]

于 2015-12-13T12:02:54.813 に答える