NSOrderedSet tarafından oluşturulan erişimcilere kural dışı durum


364

Lion uygulamamda şu veri modeli var:

resim açıklamasını buraya girin

İlişki subitemsiçine Item sıralanır .

Xcode 4.1 (build 4B110) benim için dosyayı yarattı Item.h, Item.m, SubItem.hve SubItem.h.

İşte içeriği (otomatik olarak oluşturulmuş) Item.h:

#import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

@class SubItem;

@interface Item : NSManagedObject {
@private
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSOrderedSet *subitems;
@end

@interface Item (CoreDataGeneratedAccessors)

- (void)insertObject:(SubItem *)value inSubitemsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromSubitemsAtIndex:(NSUInteger)idx;
- (void)insertSubitems:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeSubitemsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInSubitemsAtIndex:(NSUInteger)idx withObject:(SubItem *)value;
- (void)replaceSubitemsAtIndexes:(NSIndexSet *)indexes withSubitems:(NSArray *)values;
- (void)addSubitemsObject:(SubItem *)value;
- (void)removeSubitemsObject:(SubItem *)value;
- (void)addSubitems:(NSOrderedSet *)values;
- (void)removeSubitems:(NSOrderedSet *)values;

@end

Ve işte içeriği (otomatik olarak oluşturulmuş) Item.m:

#import "Item.h"
#import "SubItem.h"

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Gördüğünüz gibi, sınıf Itemdenilen bir yöntem sunuyor addSubitemsObject:. Ne yazık ki, bu şekilde kullanmaya çalışırken:

Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

[item addSubitemsObject:subItem];

bu hata görünür:

2011-09-12 10:28:45.236 Test[2002:707] *** -[NSSet intersectsSet:]: set argument is not an NSSet

Bana yardımcı olabilir misiniz?

Güncelleme:

Hata raporumdan sadece 1.787 gün sonra, bugün (1 Ağustos 2016) Apple bana şunu yazdı: "Lütfen bu sorunu en son iOS 10 beta derlemesi ile doğrulayın ve bugreport.apple.com'daki hata raporunuzu sonuçlarınızla güncelleyin." . Umarım doğru zaman budur :)


5
Aynı sorunu görüyorum. Umarım yakında sabitlenir. Değişken sipariş setini doğrudan kullanmak şimdilik şimdilik kolay bir çözümdür. Not: Mogenerator kullanıyorum, ancak üretilen kodun bu kısmı için dahili olarak aynı Apple jeneratörünü kullandığını varsayıyorum.
Chad Podoski

12
Neredeyse 2 yıl! Apple, iOS 7'de düzeltir misiniz? —— Sadece bu hatanın hala orada olup olmadığını merak edenlerle paylaşmak istiyorum: “Evet, öyle.”
an0

1
İki yıla çok yakın, bu hala tüm xcode 5 geliştirici önizlemelerinde bir sorun.
Korvin Szanto

2
Uygun KVC erişimcisini kullanıyorsanız sorunu hala görüyor musunuz? (yani mutableOrderedSetValueForKey:)
quellish

3
Mavericks ile ilgili hala bir sorun var gibi görünüyor.
Tim

Yanıtlar:


263

Kurulumunuzu hem veri modelinizle hem de kendi adımla farklı adlarla yeniden ürettim. Her iki durumda da aynı hatayı aldım.

Apple'ın otomatik oluşturulan kodunda bir hata gibi görünüyor.


60
Hata kimliği 10114310'dur. 13 Eylül 2011'de rapor edilmiştir, ancak bugün (15 Ocak 2012) hala "açık" dır. Aynı sorunu yaşayan insanların sayısı göz önüne alındığında inanılmaz.
Dev

14
Güncelleme: bugün (11 Mayıs 2012) # 10114310 hatası, raporumdan (13 Eylül 2011) 241 gün sonra hala açık. Inanılmaz.
Dev

23
WWDC'deki CoreData Lab oturumlarından birinde bir Apple mühendisi ile bu konuyu gündeme getirdim. Sorunu ve bunun gerçek bir hata olduğunu ve gördüklerimden "kritik" statüye sahip olduğunu kabul ediyorlar, ancak elbette ne zaman çözeceklerine dair bir söz yok. İOS6 / Mountain Lion'da düzeltileceğini sanmıyorum. Bu radarı daha da çoğaltmanın iyi olacağını düşünüyorum. Şu anda yaklaşık 25 dup var, daha iyi!
DaGaMs

40
Bugün kontrol edildi, hala iOS 7 GM / OMG'de var! İnanamıyorum ...
an0

79
Güncelleme: # 10114310 hatasını doldurduğumdan beri 797 gün, 2 yeni büyük iOS sürümü ve sayısız Xcode sürümü geçti. Ve hala "açık". Inanılmaz.
Dev

244

Burada bir hata olabileceğini kabul ediyorum. Bir NSMutableOrderedSet doğru eklemek için ekleme nesne ayarlayıcı uygulamasını değiştirdim.

- (void)addSubitemsObject:(SubItem *)value {
    NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
    [tempSet addObject:value];
    self.subitems = tempSet;
}

Kümeyi self.subitems olarak yeniden atamak Will / DidChangeValue bildirimlerinin gönderilmesini sağlar.


Kod snippet'iniz beni bu soruna getirmem için gereken şeydi. Umarım Apple nihayetinde sorunu çözer, ancak şu ana kadar yaklaşımınızı kullanmayla ilgili herhangi bir sorun görmedim.
Christopher Hujanen

Bu geçici çözüm uygulamaya çalıştığımda bu hatayı alıyorum .. [__NSArrayI isEqualToSet:]: tanınmayan seçici örneğe gönderildi ... Bu genellikle serbest bırakılan bir öğeden, ancak nerede çalıştığını bulamayan, herkes çalıştırıyor bunun içine?
DerekH

@DerekH isEqualToSet yalnızca NSSet'in sahip olduğu bir yöntemdir, bu yüzden tahminim, setin gerekip gerekmediğini belirlemek için isEqualToOrderedSet'i çağırmadan önce bir işaretçi NSArray olarak dönüştürüldüğünüz, oluşturduğunuz veya bir NSArray olarak işlem görüyorsunuz. hatta değiştirmek ya da olduğu gibi bırakmak.
InitJason

3
@MarkAmery Test edildi. Doğrulanmış. Dinamik ayarlayıcı self.subitems bildirimleri gönderir. Yani JLust çözümü doğrudur.
bernstein

3
Bu iyi bir cevap ama verimsiz. Sipariş edilen tüm seti kopyalar, değiştirir ve sonra tekrar kopyalarsınız. Etki sadece sipariş edilen sete bir isabet değildir, aynı zamanda sipariş edilen setin her değiştiğinde tüm içeriğinin değiştiğine dair bildirimler gönderilir! Bu sıralı set, örneğin UITable için kullanılıyorsa, güncelleme için ciddi sonuçlar doğurabilir. Çözümümde hatanın tam olarak nereden geldiğini belirttim ve hatayı atlamak için daha verimli bir yöntem gösterdim.
Owen Godfrey

111

Gerekli tüm yöntemleri uygulayarak çözümü geliştirmeye karar verdim:

static NSString *const kItemsKey = @"<#property#>";

- (void)insertObject:(<#Type#> *)value in<#Property#>AtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet insertObject:value atIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)removeObjectFrom<#Property#>AtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet removeObjectAtIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)insert<#Property#>:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet insertObjects:values atIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>AtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet removeObjectsAtIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replaceObjectIn<#Property#>AtIndex:(NSUInteger)idx withObject:(<#Type#> *)value {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet replaceObjectAtIndex:idx withObject:value];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replace<#Property#>AtIndexes:(NSIndexSet *)indexes with<#Property#>:(NSArray *)values {
    [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet replaceObjectsAtIndexes:indexes withObjects:values];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)add<#Property#>Object:(<#Type#> *)value {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSUInteger idx = [tmpOrderedSet count];
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    [tmpOrderedSet addObject:value];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>Object:(<#Type#> *)value {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSUInteger idx = [tmpOrderedSet indexOfObject:value];
    if (idx != NSNotFound) {
        NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet removeObject:value];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    }
}

- (void)add<#Property#>:(NSOrderedSet *)values {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
    NSUInteger valuesCount = [values count];
    NSUInteger objectsCount = [tmpOrderedSet count];
    for (NSUInteger i = 0; i < valuesCount; ++i) {
        [indexes addIndex:(objectsCount + i)];
    }
    if (valuesCount > 0) {
        [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet addObjectsFromArray:[values array]];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    }
}

- (void)remove<#Property#>:(NSOrderedSet *)values {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
    for (id value in values) {
        NSUInteger idx = [tmpOrderedSet indexOfObject:value];
        if (idx != NSNotFound) {
            [indexes addIndex:idx];
        }
    }
    if ([indexes count] > 0) {
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet removeObjectsAtIndexes:indexes];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    }
}

1
Çökme türü nedir? 'removeObjectFromSubitemsAtIndex' bu alt öğeleri silmez, yine de depolama alanınızda bulunurlar, yalnızca nesneler arasındaki ilişkiyi kaldırmanın yoludur.
Dmitry Makarenko

2
kItemsKey sadece KVO yöntem çağrılarında kolaylık sağlamak için eklenen bir sabittir. Bu, yöntemlerinizi yazdığınız düzenli bir ilişkinin adıdır.
Dmitry Makarenko

1
Ben öyle düşünüyorum. Teşekkürler. Ama benim sorunum bu yöntemler kullanılarak verilerin veritabanına kaydedilmemesidir.
Bagusflyer

4
!!!!!!!!! Sadece kodu kopyalamak ve yöntem adlarını değiştirmek, mükemmel çalışıyor !!! Bu en hızlı cevap.
flypig

1
Bu müthiş, ancak sipariş edilen setin geçici kopyasını oluşturmak gereksizdir. Suçlu, willChangeValueForKey:withSetMutation:usingObjectsbaşarıyla kaçındığınız. Bundan sonra, sadece [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]veya [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]uygun şekilde kullanın . Ayrıntılar için cevabıma bakın.
Owen Godfrey

38

Evet, bu kesinlikle bir Temel Veri hatasıdır. Bir süre önce ObjC-Runtime tabanlı bir düzeltme yazdım, ancak o zaman yakında düzeltileceğini düşündüm. Her neyse, böyle bir şans yok, bu yüzden GitHub'da KCOrderedAccessorFix olarak yayınladım . Tüm varlıklarınızda bu soruna geçici bir çözüm bulun:

[managedObjectModel kc_generateOrderedSetAccessors];

Özellikle bir varlık:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Veya sadece bir ilişki için:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

Bunun Apple'ın gerçek düzeltmesiyle çakışıp çakışmadığını merak ediyorum.
tia

3
Bu, Apple'ın düzeltmesiyle çelişmemelidir, çünkü amacı ne olursa olsun Apple'ın uygulamasını geçersiz kılmaktır. Bu aslında Apple tarafından düzeltildiğinde / düzeltilmişse, belki - (BOOL)kc_needsOrderedSetAccessorFix;Foundation / iOS sürümünü kontrol eden bir şey eklerim .
Sterling Archer

2
CocoaPods ana deposunda zaten bir KCOrderedAccessorFix.podspec var. Bunu projelerinize bağlamak için Podfile'nize "pod 'KCOrderedAccessorFix'" yazabilirsiniz
Anton Matosov

Bunun iOS 8 ile ilgili bazı sorunları vardı (yanlış yöntem imzaları için objc_msg_send)
NSTJ

İOS9'da işe yarıyor, güzel iş! Bu şimdiye kadarki en iyi çözüm, kodunuzda herhangi bir değişiklik yapmaya gerek yok!
Borzh

32

Bir kopya yapmak yerine, ilişkilerin NSMutableOrderedSet'ine erişmek için NSObject içindeki erişimciyi kullanmanızı öneririm.

- (void)addSubitemsObject:(SubItem *)value {
      NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
 }

örneğin iOS v5.0 için Temel Veri Sürüm Notları buna atıfta bulunur.

Kısa bir testte uygulamamda çalıştı.


1
Değişmez dizeleri kolayca yeniden düzenleyemezsiniz. Kod kullanırsanız derleyici check self.subitems yazabilir.
logancautrell

1
@logancautrell evet bu doğru. Belirli kullanım durumunun önceliğine bağlıdır. Genel olarak, özellikle bu durumda kaynakları kurtarmaya odaklanıyorum çünkü bu sadece bir çözümdü.
Stephan

2
Değişmez dize NSStringFromSelector(@selector(subitems))olsa değiştirilebilir :)
Ja͢ck

17

Hatayı izledim. İçinde oluşur willChangeValueForKey:withSetMutation:usingObjects:.

Bu çağrı, izlenmesi zor olabilecek bir bildirim zincirini başlatır ve elbette bir yanıtlayıcıda yapılan değişikliklerin başka biri için etkileri olabilir, ki Apple'ın hiçbir şey yapmadığından şüpheleniyorum.

Ancak, Set'te ve bu arızalı bir OrderedSet'teki Set işlemlerinde sorun yoktur. Bu, değiştirilmesi gereken sadece dört yöntem olduğu anlamına gelir. Bu nedenle, tek yaptığım Set işlemlerini eşdeğer Array işlemlerine dönüştürmekti. Bunlar mükemmel ve minimum (ama gerekli) genel giderler olarak çalışır.

Kritik düzeyde, bu çözüm bir kritik kusurdan muzdariptir; nesne ekliyorsanız ve nesnelerden biri zaten varsa, o zaman eklenmez veya sıralı listenin arkasına taşınmaz (hangisini bilmiyorum). Her iki durumda da, ulaştığımız zamana kadar nesnenin beklenen sıralı endeksi, beklenenden didChangefarklıdır. Bu, bazı kişilerin uygulamalarını bozabilir, ancak benimkini etkilemez, çünkü yalnızca yeni nesneler ekliyorum veya eklemeden önce son konumlarını onaylıyorum.

- (void)addChildrenObject:(BAFinancialItem *)value {
    if ([self.children containsObject:value]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:self.children.count];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] addObject:value];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
    if (![self.children containsObject:value]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.children indexOfObject:value]];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] removeObject:value];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)addChildren:(NSOrderedSet *)values {
    if ([values isSubsetOfOrderedSet:self.children]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildren:(NSOrderedSet *)values {
    if (![self.children intersectsOrderedSet:values]) {
        return;
    }
    NSIndexSet * indexSet = [self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return [values containsObject:obj];
    }];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

Tabii ki, daha kolay bir çözüm var. şu şekildedir;

- (void)addChildrenObject:(BAFinancialItem *)value {
    if ([self.children containsObject:value]) {
        return;
    }
    [self insertObject:value inChildrenAtIndex:self.children.count];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
    if (![self.children containsObject:value]) {
        return;
    }
    [self removeObjectFromChildrenAtIndex:[self.children indexOfObject:value]];
}

- (void)addChildren:(NSOrderedSet *)values {
    if ([values isSubsetOfOrderedSet:self.children]) {
        return;
    }
    [self insertChildren:values atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)]];
}

- (void)removeChildren:(NSOrderedSet *)values {
    if (![self.children intersectsOrderedSet:values]) {
        return;
    }
    [self removeChildrenAtIndexes:[self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return [values containsObject:obj];
    }]];
}

Herkesin bu cevabı göz ardı ettiği çok kötü görünüyor, kesinlikle en iyi çözüm gibi görünüyor.
George

Bu çözüm, yerel bir set yapmak için orderedSetWithOrderedSet kullananlardan çok daha iyi bir performansa sahiptir. Büyük veri kümeleriniz olduğunda bunun büyük bir yükü vardır. Daha kolay çözüm, sadece gösterilmeyen yöntemlerle ilkinin yeniden düzenlenmiş bir versiyonu gibi görünüyor.
David Pettigrew

1
Hala addChildren'de bir kilitlenme görüyorum: *** Yakalanmayan istisna 'NSInvalidArgumentException' nedeniyle uygulamayı sonlandırıyorum, nedeni: '- [TrackHistory insertTrackpoints: atIndexes:]: tanınmayan seçici 0x1702b1b20'
Victor Bogdan

@OwenGodfrey Daha kolay çözüm için bu yöntemleri nerede uyguluyorsunuz? Bir istisna alıyorum: [Üst insertObject: inChildrenAtIndex:] tanınmayan seçici 0x6180000ac480 örneğine gönderildi.
Dalmazio

değişkeniniz büyük "P" ile "Ebeveyn" midir? Bu, "Üst öğe" sınıfını çağırdığınız anlamına mı geliyor yoksa "Üst öğe" adında bir örnek değişkeniniz mi var? Sınıfım Üst öğe ise, bu yöntemi Üst öğenin en altına uyguladım, ancak sınıf yöntemi olmadığı için büyük olasılıkla küçük "p" ile "üst" olarak adlandırılan bir örnek üzerinde çağırmanız gerekir. .
Owen Godfrey

10

Birçok İlişkiler için Elma dokümanlar diyor ki: Eğer vekil değişken kümesi erişmek gerekir ya kullanılarak sıralı dizi

NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];

Bu kümede değişiklik yapılması, yönetilen nesnenize ilişki ekler veya kaldırır. Değiştirilebilir sıralı sete [] veya. gösterim yanlıştır ve başarısız olur.


3
Adil olmak gerekirse, dokümanlar ayrıca şunları söylüyor: "veya otomatik olarak oluşturulan ilişki mutator yöntemlerinden biri (bkz. Dinamik Olarak Oluşturulmuş Erişimci Yöntemleri):"
Matt

Tamam tamam ... haklısın. Öyleyse, bunun en basit çalışma yolu olduğunu söyleyelim ...
Nicolas Manzini

9

Aynı hatayı aldı, @LeeIII çözümü benim için çalıştı (teşekkürler!). Biraz değiştirmenizi öneririm:

  • Yeni yöntemi saklamak için objektif-c kategorisini kullanın (bu nedenle Öğe yeniden oluşturulursa yöntemimizi kaybetmeyeceğiz)
  • Değişken setimiz olup olmadığını kontrol edin

İçeriği Item+category.m:

#import "Item+category.h"

@implementation Item (category)

- (void)addSubitemsObject:(SubItem *)value {
    if ([self.subitems isKindOfClass:[NSMutableOrderedSet class]]) {
        [(NSMutableOrderedSet *)self.subitems addObject:value];
    } else {
        NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
        [tempSet addObject:value];
        self.subitems = tempSet;
    }
}

@end

Bu kodu kategoriye taşımak için iyi bir nokta. Ama yine de will / setPrimitiveValue / didChange çağrıları ile @Dmitry Makarenko yanıtında olduğu gibi gerçek add / remove'i kucaklamamız gerekiyor.
Vladimir Shutyuk

8

Mogenerator kullanıyorsanız, bunun yerine

[parentObject add<Child>sObject:childObject];

sadece kullanın:

[[parent object <child>sSet] addObject:childObject];

Mogenerator ekstra kodla ilgileneceğinden, aksi takdirde yazmak zorunda kalacaksınız ve sadece temeldeki set nesnesine erişime izin vereceksiniz.
καrτhικ

Görünüşe göre, mojeneratör düzeltilmiş cisimler üretecek anlamına gelen bir düzeltme yapıldı ... github.com/dmakarenko/mogenerator/commit/…
kombinasyonel

1
Kullanıyorum mogeneratorama hala hata var.
Colas

7

Şahsen sadece @Stephan tarafından başka bir çözümde özetlenen yöntem doğrudan çağrıları ile CoreData oluşturulan yöntemleri çağrıları değiştirdik:

NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
[tempSet addObject:value];

Bu, daha sonra hata düzeltildiğinde Apple'dan oluşturulan koda bir çözümle çakışabilecek kategorilere olan ihtiyacı ortadan kaldırır.

Bu, bunu yapmanın resmi yolu olmanın artılarını da ekledi!


Bu şu hatayı verir: '[<CLASS 0x20886d10> valueForUndefinedKey:]: bu sınıf, anahtar alt öğeleri için kod değeri uyumlu anahtar değer değil.'
jmstone617

Hala bunun Apple'ın bilinen sorunlarında listelenmediğine dair bir sonuç vermese de (görünüşte nafile bir jest için bir radar açtım), bu çözüm benim için kusursuz bir şekilde çalıştı.
Scott Corscadden

Keşke bu cevabı daha önce görmüş olsaydım; Son zamanlarda biraz kazma yapana ve nihayet burada tam olarak ne yaptığını uygulayana kadar en yüksek oyu verdim :)
Ja͢ck

Neden addObject:iki kez çağrılıyor?
Jason Moore

5

Öyle görünüyor ki, ebeveyni çocuğa bağlayarak ebeveyni çocuğa bağlarsanız, bunun tersi değil, çökmeden çalışır.

Yani eğer yaparsanız:

[child setParent:parent]

onun yerine

[parent setChildObects:child]

Çalışmalı, en azından iOS 7'de çalışıyor ve ilişki ile ilgili herhangi bir sorun yaşamadım.


1
Her iki taraf da çok olduğunda iyi olmaz. O zaman net bir ebeveyn-çocuk ilişkisi yoktur.
fatuhoku

3

Aynı sorunu yaşadım, ama sadece yaptığımdan farklı bir şey denediğimde. SubItem kodunu göremiyorum, ancak öğeye ters bir bağlantı olduğunu varsayacağım. Bu reveres link "parentItem" diyelim, o zaman en kolay çözüm şudur:

Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

//[item addSubitemsObject:subItem];
subItem.parentItem = item;

Etkisi, elmanın kendi kodunu kullanması ve basit ve temiz olmasıdır. Ayrıca, küme otomatik olarak eklenir ve tüm gözlemciler güncellenir. Sorun değil.


Bu çok güzel. Tüm problemi çözer ve aynı zamanda düzenli tutar. Hala hata hala mevcut deli. Bu yanıtın bir diğer yararı da, temel veri modellerinizi yeniden oluşturursanız, hata düzeltmelerinizi yeniden yazmak zorunda kalmamanızdır. Teşekkürler!
Johan S

Diğer cevabımı gör. Hatayı daha ayrıntılı olarak izledim. Bu hala en kolay yoldur, ancak diğer yöntem en iyisidir çünkü daha fazla olasılık açar.
Owen Godfrey

Vaov! En sonunda!!! Teşekkürler! (Diğer kodunuzu denedim, ancak hatalar aldı, bu yanlış tür hakkında bir şey [self didChange: NSKeyValueChangeInsertion değerleriAtIndexes: indexSet forKey: ChildrenKey];)
Leonard Pauli

3

Ben sadece bu sorunu faul düştü ve burada özetlenen diğerlerinden çok daha basit bir uygulama kullanarak çözdüm. NSManagedObjectAlt sınıfları kullanmadığınızda ilişkilerle başa çıkmak için mevcut yöntemleri kullanırım.

Bir varlığı bir NSOrderedSetilişkiye eklemek için örnek bir uygulama şöyle görünür:

- (void)addAddress:(Address *)address
{
    if ([self.addresses containsObject:address]) {
        return;
    }
    // Use NSManagedObject's methods for inserting an object
    [[self mutableOrderedSetValueForKey:@"addresses"] addObject:address];
}

Bu mükemmel çalışıyor ve NSManagedObjectalt sınıflara geçmeden önce kullandığım şey bu .


3

Amaç-C'den bir proje göç ederken bu sorun aklıma geldi Swift 2 ileXCode 7 . Bu proje eskiden işe yaradı ve iyi bir nedenden dolayı: Bu hatayı düzeltmek için değiştirme yöntemleri olan MOGenerator kullanıyordum. Ancak tüm yöntemlerin değiştirilmesi gerekmez.

Burada, mümkün olduğunca varsayılan erişimcilere dayanan örnek bir sınıfla eksiksiz bir çözüm.

Diyelim ki sipariş edilen ürünler içeren bir listemiz var

Öncelikle bir / çok ilişkiniz varsa hızlı bir kazanç , en kolayı sadece yapmaktır:

item.list = list

onun yerine

list.addItemsObject(item)

Şimdi, bu bir seçenek değilse , şunları yapabilirsiniz:

// Extension created from your DataModel by selecting it and
// clicking on "Editor > Create NSManagedObject subclass…"

extension List {
  @NSManaged var items: NSOrderedSet?
}

class List

  // Those two methods work out of the box for free, relying on
  // Core Data's KVC accessors, you just have to declare them
  // See release note 17583057 https://developer.apple.com/library/prerelease/tvos/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc7_release_notes.html
  @NSManaged func removeItemsObject(item: Item)
  @NSManaged func removeItems(items: NSOrderedSet)

  // The following two methods usually work too, but not for NSOrderedSet
  // @NSManaged func addItemsObject(item: Item)
  // @NSManaged func addItems(items: NSOrderedSet)

  // So we'll replace them with theses

  // A mutable computed property
  var itemsSet: NSMutableOrderedSet {
    willAccessValueForKey("items")
    let result = mutableOrderedSetValueForKey("items")
    didAccessValueForKey("items")
    return result
  }

  func addItemsObject(value: Item) {
    itemsSet.addObject(value)
  }

  func addItems(value: NSOrderedSet) {
    itemsSet.unionOrderedSet(value)
  }
end

Tabii ki, eğer Objective-C kullanıyorsanız, aynı şeyi yapabilirsiniz çünkü ilk etapta fikri burada buldum :)


3

Burada bir hata olabileceğini kabul ediyorum. Bir NSMutableOrderedSet doğru eklemek için ekleme nesnesi> ayarlayıcı uygulamasını değiştirdim.

- (void)addSubitemsObject:(SubItem *)value {
     NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
     [tempSet addObject:value];
     self.subitems = tempSet;
}

Kümeyi self.subitems olarak yeniden atamak Will / DidChangeValue bildirimleri> öğesinin gönderilmesini sağlar.

Leelll, bu sette saklanan NSMutableOrderedSet değerlerinin bu özel kurulumundan sonra CoreData tarafından veritabanına doğru şekilde kaydedileceğinden emin misiniz? Bunu kontrol etmedim, ancak CoreData NSOrderedSet hakkında hiçbir şey bilmiyor ve NSSet'i çok sayıda ilişki konteyneri olarak bekliyor gibi görünüyor.


CoreData'nın bir NSOrderedSet nesnesini döndürmesi veya alması için, bu başlatılan soru gösterildiği gibi birden çok koşulun karşılanması gerekir. Kodumu paylaşan insanlar Lion çalıştırmayan bir geliştirici olduğunda gördüğüm en yaygın hatalar. NSOrderedSets çerçevesi kar leoparında mevcut değildir. Ama evet, bunun başarısız olduğunu görmedim, ancak bunun performansta en iyisi olduğundan emin değilim. Sanırım bu tüm seti alıyor ve sadece istenen kaydı eklemek yerine onun yerini alıyor.
InitJason

2

Bence herkes asıl problemi kaçırıyor. Erişimci yöntemlerinde değil, aslındaNSOrderedSet bir alt sınıf değildir NSSet. Dolayısıyla -interSectsSet:, argüman olarak sıralı bir kümeyle çağrıldığında başarısız olur.

NSOrderedSet* setA = [NSOrderedSet orderedSetWithObjects:@"A",@"B",@"C",nil];
NSSet* setB = [NSSet setWithObjects:@"C",@"D", nil];

 [setB intersectsSet:setA];

ile başarısız *** -[NSSet intersectsSet:]: set argument is not an NSSet

Düzeltme, set operatörlerinin uygulamasını değiştirmek ve böylece türleri şeffaf bir şekilde ele almak gibi görünüyor. -intersectsSet:A'nın sıralı veya sırasız bir kümeyle çalışmasının bir nedeni yoktur .

Değişiklik bildiriminde istisna oluşur. Muhtemelen ters ilişkiyi işleyen kodda. Çünkü sadece ters bir ilişki kurarsam olur.

Aşağıdakiler benim için hile yaptı

@implementation MF_NSOrderedSetFixes

+ (void) fixSetMethods
{
    NSArray* classes = [NSArray arrayWithObjects:@"NSSet", @"NSMutableSet", @"NSOrderedSet", @"NSMutableOrderedSet",nil];

    [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* name = obj;
        Class aClass = objc_lookUpClass([name UTF8String]);
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(intersectsSet:) forClass:aClass];
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(isSubsetOfSet:) forClass:aClass];
    }];
}

typedef BOOL (*BoolNSetIMP)(id _s,SEL sel, NSSet*);

/*
    Works for all methods of type - (BOOL) method:(NSSet*) aSet
*/
+ (void) fixMethodWithSetArgument:(SEL) aSel forClass:(Class) aClass 
{
    /* Check that class actually implements method first */
    /* can't use get_classInstanceMethod() since it checks superclass */
    unsigned int count,i;
    Method method = NULL;
    Method* methods = class_copyMethodList(aClass, &count);
    if(methods) {
        for(i=0;i<count;i++) {
            if(method_getName(methods[i])==aSel) {
                method = methods[i];
            }
        }
        free(methods);
    }
    if(!method) {
        return;
    }

   // Get old implementation
   BoolNSetIMP originalImp  = (BoolNSetIMP) method_getImplementation(method);
   IMP newImp = imp_implementationWithBlock(^BOOL(NSSet *_s, NSSet *otherSet) {
        if([otherSet isKindOfClass:[NSOrderedSet class]]) {
            otherSet = [(NSOrderedSet*)otherSet set];
        }
        // Call original implementation
        return originalImp(_s,aSel,otherSet);
    });
    method_setImplementation(method, newImp);
}
@end

2

Swift'te (Xcode 6.1.1) problemi yeni aldım.

Cevap, NSManagedObject alt sınıflarınızda HERHANGİ BİR YÖNTEM VEYA İLAVE ŞEYLERİ KODLAMAYIN. Bence bu bir derleyici hatası. Çok garip bir böcek ..

Umarım yardımcı olur ..


3
Diğer düzeltmeleri uygulayamazsam, bunu düzeltmek için ne yapmalıyım?
Ben Leggiero

2

Tersi No Inverse olarak ayarlayarak bu sorunu çözdüm, neden bilmiyorum, Belki Apple Bug var.resim açıklamasını buraya girin


1

Ben "alt öğeler" yerine "sinyalleri" adlı bir öğe ile aynı durum var. Tempset ile çözüm benim test çalışır. Ayrıca, removeSignals: yöntemiyle ilgili bir sorunum vardı. Bu geçersiz kılma işe yarıyor gibi görünüyor:

- (void)removeSignals:(NSOrderedSet *)values {
    NSMutableOrderedSet* tempset = [NSMutableOrderedSet orderedSetWithOrderedSet:self.signals];
    for (Signal* aSignal in values) {
        [tempset removeObject:aSignal];
    }
    self.signals = tempset;
}

Bunu yapmanın daha iyi bir yolu varsa, lütfen bana bildirin. Değerlerim girdisi hiçbir zaman 10-20 öğeden fazla değildir, bu nedenle performans endişe verici değildir - yine de lütfen alakalı bir şeye dikkat edin.

Teşekkürler,

Damien


1

Bu soruyu hata mesajı için googling yaparak buldum ve sadece bu hatayı biraz farklı bir şekilde (sıralı kümeler kullanmadan) çalıştırdığımı belirtmek istedim. Bu verilen soruya bir cevap değil, ancak arama yaparken bu soruya rastlayan herkes için yararlı olması durumunda burada gönderiyorum.

Yeni bir model sürümü ekliyordum ve mevcut modellere bazı ilişkiler ekledim ve başlık dosyasındaki add * Object yöntemlerini kendim tanımladım. Onları aramaya çalıştığımda, yukarıdaki hatayı aldım.

Modellerimi inceledikten sonra, "Çok Sayıda İlişki" onay kutusunu işaretlemeyi aptalca unuttuğumu fark ettim.

Dolayısıyla, bununla karşılaşıyorsanız ve sipariş edilen setleri kullanmıyorsanız, modelinizi iki kez kontrol edin.


1

Benim için çalışan bu hata için bir düzeltme buldum. Ben sadece bunun yerine:

[item addSubitemsObject:subItem];

Bununla:

item.subitemsObject = subItem;

1

SWIFT'te doğru cevabın daha iyi versiyonu

var tempSet = NSMutableOrderedSet()
if parent!.subItems != nil {
    tempSet = NSMutableOrderedSet(orderedSet: parent!.subItems!)
}

tempSet.add(newItem)
parent!.subItems = tempSet

0

LeeIII'ın çalıştığı yöntemi kullanarak buldum, ancak profillemede büyük ölçüde yavaş olduğunu gördüm. 1000 öğenin ayrıştırılması 15 saniye sürdü. İlişki eklemek için kodun yorumlanması 15 saniyeyi 2 saniyeye çevirdi.

Geçici çözümüm (daha hızlı ancak çok daha çirkin) geçici bir değişken dizi oluşturmayı ve ardından tüm ayrıştırma tamamlandığında sıralı kümeye kopyalamayı içerir . (bu yalnızca birçok ilişki ekleyecekseniz performans kazancıdır).

@property (nonatomic, retain) NSMutableArray* tempItems;
 ....
@synthesize tempItems = _tempItems;
 ....

- (void) addItemsObject:(KDItem *)value 
{
    if (!_tempItems) {
        self.tempItems = [NSMutableArray arrayWithCapacity:500];
    }
    [_tempItems addObject:value];
}

// Call this when you have added all the relationships
- (void) commitRelationships 
{
    if (_tempItems) {
        self.items = [NSOrderedSet orderedSetWithArray:self.tempItems];
        self.tempItems = nil;
    }
}

Umarım bu başka birine yardım eder!


0

Robert

Cevabınızın bunun için çalışacağını kabul ediyorum, ancak bir ilişkiye bir dizi değer eklemek için otomatik olarak oluşturulan bir yöntem olduğunu unutmayın. Apple'ın Dokümantasyonu ( burada " Çok Sayıda İlişkiler " bölümünde veya burada "Çok Sayıda İlişkiler İçin Özel Erişim Yöntemleri" bölümünde görüldüğü gibi ) bunları şu şekilde uygular:

- (void)addEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
[[self primitiveEmployees] unionSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
}

- (void)removeEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
[[self primitiveEmployees] minusSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
}

İlişkilerinizi temel verilerin dışında kolayca derleyebilir ve ardından bu yöntemi kullanarak hepsini bir kerede ekleyebilirsiniz. Önerdiğiniz yöntemden daha az çirkin olabilir ;)


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.