2010-03-06 19 views
20

NS (Mutable) sözlüklerinde anahtar olarak kullanılan tüm nesneler, NSCopying protokolünü desteklemeli ve söz konusu nesneler sözlükte kullanıldığında kopyalanmalıdır.Cocoa's NSDictionary: Anahtarlar neden kopyalanıyor?

Sıklıkla bir nesneyi diğerine eşlemek için, daha ağır nesneleri anahtar olarak kullanmak isterim. Ne Bunu yaparken gerçekten demek etkin bir geçerli:

[dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]]; 

("Geri gelip tekrar bu aynı anahtar nesne örneğini teslim ederken, aynı değere çıkar beni.")

... Bu tasarım etrafında bazen yapmak için yaptığım şey tam olarak bu. (Evet, masaüstü Cocoa NSMapTable hakkında bilmek; ama örneğin iPhone bu desteklemez.)

Ama Bana düşmez gerçekten anahtar ilk etapta gerekli veya arzu edilir kopyalama neden olsun olduğunu. Uygulamayı veya arayanı ne alır?

cevap

25

Kopyalama, anahtar olarak kullanılan değerlerin anahtar olarak kullanıldığında "alttan" değiştirilmemesini sağlar. Bir değişken dize örneği inceleyelim:

NSMutableString* key = ... 
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; 

[dict setObject: ... forKey: key]; 

en sözlüğü anahtarı kopyalamak olmadığını varsayalım, ama bunun yerine sadece retain bunu ed. Şimdi, daha sonra bir noktada, orijinal dize değiştirilirse, o zaman aynı anahtar nesneyi (yani, bir key noktaları kullansanız bile sözlükte saklı değerinizi bulamayacaksınız demektir) Yukarıdaki örnekte).

Böyle bir hataya karşı kendinizi korumak için sözlük tüm anahtarları kopyalar.

Bu arada, -copyWithZone:'u tanımlamak için return [self retain] numarasını belirtmek yeterli. Bu izin ve iyi kod nesne değişmez ise ve NSCopying sözleşme özellikle değişmez döndü nesne (tür, sayılır) olmak zorundadır şekilde dizayn edilir:

orijinal istinat yerine oluşturarak NSCopying Uygulamak sınıf ve içeriği değişmez olduğunda yeni kopya. (NSCopying Reference den)

ve

göz “kesilebilir genel değişmez” alıcı nesnesi için geçerli olup olmadığını geri kopya değişmez olduğu; aksi halde kopyanın kesin doğası sınıf tarafından belirlenir. Yalnızca hiç etkilenmez, yani uygulamalar kimlik tabanlı eşitliği/karma uygulamaları kullanmak eğer nesneler değişmez olmasa bile

(-copyWithZone: Reference itibaren)

, o uygulamaya kaçabilirsiniz nesnenin dahili durumu tarafından herhangi bir şekilde.

+0

Dirk: Bunun için teşekkürler. Yanlış anlama olduğum kritik gerçeğin (cevabın beni yönlendirdiğini), anahtarın semantiğinin anahtar nesne ref * ile ilgili olmadığını *; anahtarın içeriği ile ilgilidirler (böylece anahtar "foo" dizgesidir, ister harflerden, isterse neyse), bir literalden oluşur - bu, semantik bir karşılaştırmayı işaret eder, bir işaretçi değil. Ancak, tuşlar * sonra * kopyalanmalıdır, çünkü değiştirilebilir tuşlar değişebilir. Bu durum beni nereye bıraktığında, gerçekten bir anahtar için bir nesneyi kullanmak istediğimde ve referans eşitliğinden çok sonra, benim yaklaşımım bir kırılma değil; tam olarak doğru. –

+0

Ben: Oldukça değil. 'KeyObject''ün korunmasını istiyorsanız, bu kod bunu yapmayacaktır, çünkü keyObject’i bir nesneye değil, bir nesneye bir işaretçi olarak gösterdiği için, onu tutabileceğini bilmenin bir yolu yoktur. Ben emin değilim: withObjCType: '@encode (id)' geçse bile, koruyacaktır. Anahtarlarınızı elde ettiğinden kesinlikle emin olmak için, ya NSMapTable'ı kullanmanız gerekecek (iPhone için geliştirdiğiniz için bunu yapamayacağınızı ima ediyorsunuz), NSDictionary yerine CFDictionary kullanın (her yerde sadece kendi yaratımı sırasında) veya kendi NSValue alt sınıfınızı oluşturun. –

+0

İyi nokta. Ekstra bilgi için teşekkürler. Bunu düşündüğümde, bu modeli kullandığımda saklanacak anahtarlara asla ihtiyacım yok. Genellikle bazı diğer koleksiyonlar aslında anahtar olarak kullandığım nesneleri "sahiplenir"; Ben sadece onları diğer ilgili nesneye verimli bir şekilde eşlemek için sözlük kullanıyorum. En kötü durum, anahtar nesnenin, sözlükte bir türkçe giriş bırakarak dealloc olacağıdır. Dikkatli olmak zorunda olduğum tek şey, gerçek anahtarları sıralamak ve işaretçileri tekrar id olarak göstermektir. Ama bu kod, tehlikeli bir şey yaptığını bilmek için yeterince kötü kokuyor ... –

7

İşaretçileri anahtar olarak kaydetmek istiyorsanız, bunları nesnesine +valueWithPointer: ile sarmanız gerekir.

+0

Bu iş beni çok zaman kurtardı. –

+0

Yanılıyorsam düzeltin, ancak uygulamayı denediğimde işe yaramadı çünkü işaretçime atıfta bulunulan nesne, erişmek istediğim zaman yayımlandı. Bunun yerine + valueWithNonretainedObject: 'kullandım. – rizzes

3

sen NSMapTable nesneyi kullanabilirsiniz tuş olarak işaretçileri kullanmak istiyorsanız iOS 6 beri belirtebilirsiniz http://nshipster.com/nshashtable-and-nsmaptable/

bkz tuşları ve/veya değerler stongly veya zayıf tutulan olup olmadığını:

NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory 
             valueOptions:NSMapTableWeakMemory]; 

Bazen uygun olabilecek başka bir seçenek de, tuşları güçlü bir şekilde tutan ve aslında dişli olarak güvenli olan NSCache kullanmaktır.