NSString özelliği: kopyala veya sakla?


331

Diyelim ki SomeClassbir stringözellik adıyla adlandırılan bir sınıfım var :

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

Adın atanmış olabileceğini anlıyorum, NSMutableStringbu durumda bu durum hatalı davranışlara yol açabilir.

  • Genel olarak dizeler için, bu özelliği kullanmak her zaman iyi bir fikir midir?copyretain
  • "Kopyalanan" bir özellik, bu tür "alıkonulmuş" bir mülkten daha az verimli mi?

6
Takip eden soru: nameSerbest bırakılmalı mı bırakılmamalı deallocmı?
Chetan

7
@chetan Evet olmalı!
Jon

Yanıtlar:


440

Kimin tipi uygundur o değişmez bir değer sınıftır öznitelikler için NSCopyingprotokol, hemen hemen her zaman belirtmelidir copysenin içinde @propertybildiriminde. Belirtmek retain, böyle bir durumda neredeyse hiç istemediğiniz bir şeydir.

İşte bunu neden yapmak istiyorsunuz:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

Mevcut değeri Person.namemülkiyet mülkiyet ilan olup olmamasına bağlı olarak farklı olacaktır retainya copyda olacak - @"Debajit"özellik işaretlenirse retain, ancak @"Chris"mülkiyet işaretlenmiş ise copy.

Hemen hemen her durumda, bir nesnenin özniteliklerini arkasından değiştirmeyi önlemek istediğiniz için , bunları temsil eden özellikleri işaretlemelisiniz copy. (Ve kullanmak yerine pasör kendiniz yazarsanız @synthesizeaslında kullanmayı unutmayın gerekir copyyerine retaino.)


61
Bu cevap biraz karışıklığa neden olmuş olabilir (bkz. Robnapier.net/blog/implementing-nscopying-439#comment-1312 ). NSString konusunda kesinlikle haklısın, ama bu konuyu biraz genelleştirdiğine inanıyorum. NSString öğesinin kopyalanmasının nedeni, ortak değiştirilebilir bir alt sınıfa (NSMutableString) sahip olmasıdır. Değişken bir alt sınıfa sahip olmayan sınıflar için (özellikle kendiniz yazdığınız sınıflar), zaman ve hafızayı boşa harcamaktan kaçınmak için genellikle kopyalamak yerine onları tutmak daha iyidir.
Rob Napier

63
Gerekçeniz yanlış. Zamana / belleğe dayalı olarak kopyalanıp kopyalanmayacağına veya saklanmayacağınıza karar vermemelisiniz, bunu istediğiniz semantiğe göre belirlemelisiniz. Bu yüzden cevabımda özellikle "değişmez değer sınıfı" terimini kullandım. Ayrıca, bir sınıfın değiştirilebilir alt sınıfları olup olmadığı ya da kendisi değiştirilebilir olup olmadığı meselesi değildir.
Chris Hanson

10
Obj-C'nin türüne göre değişmezliği zorlayamaması utanç verici. Bu, C ++ 'ın geçişsel sabit eksikliğiyle aynıdır. Şahsen ben ipler hep değişmezmiş gibi çalışırım . Değişken bir dize kullanmam gerekirse, daha sonra mutasyona uğrarsam asla değişmez bir referans veremeyeceğim. Farklı bir şeyin kod kokusu olduğunu düşünürdüm. Sonuç olarak - kodumda (yalnız üzerinde çalıştığım) tüm dizelerimi saklıyorum. Bir ekibin parçası olarak çalışsaydım olaylara farklı bakabilirdim.
philsquared

5
@Phil Nash: Yalnız üzerinde çalıştığınız ve başkalarıyla paylaştığınız projeler için farklı stiller kullanmanın bir kod kokusu olduğunu düşünürdüm. Her Dil / Çerçeve içerisinde geliştiricilerin üzerinde anlaştığı ortak kurallar veya stiller vardır. Özel projelerde onları dikkate almamak yanlış görünüyor. Ve mantığınız için "Kodumda değişebilir dizeler döndürmüyorum": Bu kendi dizeleriniz için işe yarayabilir, ancak çerçevelerden aldığınız dizeleri asla bilemezsiniz.
Nikolai Ruhe

7
@Nikolai Ben NSMutableStringgeçici bir "dize oluşturucu" türü hariç (ben hemen değişmez bir kopyasını almak) dışında kullanmıyorum . Onları gizli türler olarak tercih ederim - ama orijinal dize değişmezse kopyanın bir saklamak için ücretsiz olmasına izin vereceğim, endişemin çoğunu hafifletir.
philsquared

120

Kopyalama , NSString için kullanılmalıdır. Değişken ise, kopyalanır. Eğer değilse, o zaman korunur. Tam olarak bir uygulamada istediğiniz anlambilim (yazının en iyisini yapmasına izin verin).


1
Ben hala değişebilir ve değişmez formlar sağduyulu olmasını tercih ederdim, ama bu kopya sadece orijinal dize değişmez ise saklanabilir olabilir fark etmemişti - ki orada en yolu. Teşekkürler.
philsquared

25
+1 NSStringolarak ilan edilen mülkün yine de copyalacağını belirtmek için retain(elbette değişmezse). Aklıma gelen başka bir örnek NSNumber.
matm

@GBY tarafından verilen bu yanıtla oylanan cevap arasındaki fark nedir?
Gary Lyn

67

Genel olarak dizeler için, saklamak yerine copy niteliğini kullanmak her zaman iyi bir fikir midir?

Evet - genel olarak her zaman copy niteliğini kullanır.

Bunun nedeni, NSString özelliğinizin bir NSString örneğini veya NSMutableString örneğini geçirebilmesidir ve bu nedenle iletilen değerin değişmez veya değişken bir nesne olup olmadığını gerçekten belirleyemeyiz.

"Kopyalanan" bir özellik, bu tür "alıkonulmuş" bir mülkten daha az verimli mi?

  • Mülkünüze bir NSString örneği geçiliyorsa , yanıt " Hayır " dır - kopyalama alıkoymaktan daha az etkili değildir.
    (Daha az verimli değildir, çünkü NSString aslında bir kopya gerçekleştirmeyecek kadar akıllıdır.)

  • Mülkünüz bir NSMutableString örneğinden geçirilirse , yanıt " Evet " olur - kopyalama alıkoymaktan daha az verimlidir.
    (Daha az verimlidir, çünkü gerçek bir bellek ayırma ve kopyalama gerçekleşmelidir, ancak bu muhtemelen istenen bir şeydir.)

  • Genel olarak "kopyalanmış" bir özellikten bahsetmek daha az verimli olma potansiyeline sahiptir - ancak NSCopyingprotokolün kullanılmasıyla, kopyalamak için olduğu kadar kopyalamak için de "verimli" bir sınıf uygulamak mümkündür. NSString örnekleri bunun bir örneğidir.

Genel olarak (yalnızca NSString için değil), ne zaman "sakla" yerine "kopya" kullanmalıyım?

copyUyarı vermeden mülkün dahili durumunun değişmesini istemediğinizde her zaman kullanmalısınız . Değiştirilemeyen nesneler için bile - uygun şekilde yazılan değiştirilemeyen nesneler kopyalamayı verimli bir şekilde idare eder (değişmezlik ve ilgili bir sonraki bölüme bakın NSCopying).

retainNesnelerin performans nedenleri olabilir , ancak bir bakım yükü ile birlikte gelir - dahili durumun kodunuzun dışında değişme olasılığını yönetmelisiniz. Dedikleri gibi - en son optimize edin.

Ama sınıfımı değişmez olarak yazdım - sadece "tutamaz mıyım?"

Kullanmıyorum copy. Sınıfınız gerçekten değişmezse , kullanıldığında NSCopyingsınıfınızın kendisini geri döndürmesi için protokolü uygulamak en iyi uygulamadır copy. Eğer bunu yaparsan:

  • Sınıfınızın diğer kullanıcıları, kullandıklarında performans avantajlarından yararlanır copy.
  • copyEk açıklama Kendi kod daha sürdürülebilir kılan - copyaçıklama gerçekten başka bir yerde devlet değişen bu nesne hakkında endişe gerekmez gösterir.

39

Bu basit kuralı izlemeye çalışıyorum:

  • Tutunacak istiyor musunuz değerin nesnenin bunu atama am zaman zaman içinde bir noktada benim mülke? Kopya kullanın .

  • Nesneye tutunmak ister miyim ve şu anda iç değerlerinin ne olacağını veya gelecekte olacağını umursamıyorum ? Güçlü kullanın (saklayın).

Göstermek için: "Lisa Miller" ( kopya ) ismini tutmak mı yoksa Lisa Miller ( güçlü ) kişiye tutmak mı istersiniz ? Adı daha sonra "Lisa Smith" olarak değişebilir, ancak yine de aynı kişi olacaktır.


14

Bu örnek aracılığıyla kopyalama ve saklama aşağıdaki gibi açıklanabilir:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

özellik copy türündeyse,

[Person name]dizenin içeriğini tutacak dize için yeni bir kopya oluşturulur someName. Artık someNamedize üzerindeki herhangi bir işlemin hiçbir etkisi olmayacaktır [Person name].

[Person name]ve someNamedizelerin farklı bellek adresleri olacaktır.

Ancak alıkonulması halinde,

her ikisi de [Person name]somename dizesiyle aynı bellek adresini tutacaktır, sadece somename dizesinin alıkonma sayısı 1 artacaktır.

Böylece somename dizesindeki herhangi bir değişiklik dizeye yansıtılacaktır [Person name].


3

Kuşkusuz bir özellik bildirimi üzerine 'kopya' koymak, öbek üzerindeki nesnelerin referans olarak iletildiği nesne yönelimli bir ortam kullanma karşısında uçar - burada elde ettiğiniz avantajlardan biri, bir nesneyi değiştirirken, o nesneye yapılan tüm referanslardır. son değişiklikleri görün. Birçok dil, değer türlerinin (örn. Yığındaki yapılar) aynı davranıştan yararlanmasına izin vermek için 'ref' veya benzer anahtar kelimeler sağlar. Şahsen, kopyayı idareli olarak kullanırdım ve bir özellik değerinin atandığı nesnede yapılan değişikliklere karşı korunması gerektiğini hissettiysem, atama sırasında bu nesnenin kopyalama yöntemini çağırabilirim, örneğin:

p.name = [someName copy];

Tabii ki, bu özelliği içeren nesneyi tasarlarken, tasarımın atamaların kopya aldığı bir desenden faydalanıp faydalanmadığını yalnızca siz anlayacaksınız - Cocoawithlove.com şunları söyleyecektir:

"Setter parametresi değiştirilebilirken bir uyarının dahili durumunun uyarı yapmadan değiştirilememesi durumunda bir kopya erişimcisi kullanmalısınız " - böylece beklenmedik bir şekilde değişecek değere dayanıp dayanamayacağınıza dair karar tamamen size aittir. Bu senaryoyu düşünün:

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

Bu durumda, kopya kullanmadan, iletişim nesnemiz yeni değeri otomatik olarak alır; eğer bunu kullanırsak, değişikliklerin tespit edildiğinden ve senkronize edildiğinden emin olmak zorundayız. Bu durumda, semantiği korumak arzu edilebilir; bir başkasında, kopya daha uygun olabilir.


1
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660

0

NSString özelliğini bildirmek için her zaman kopya kullanmalısınız

@property (nonatomic, copy) NSString* name;

Değişmez dizge döndürüp döndürmediği (değişebilir dize geçirilmişse) veya korunan bir dizge döndürüp döndürmediği (değiştirilemez dize geçirilmişse) hakkında daha fazla bilgi için bunları okumalısınız.

NSCopying Protokol Referansı

Sınıf ve içeriği değiştirilemezken yeni bir kopya oluşturmak yerine orijinali koruyarak NSCopying'i uygulayın

Değer Nesneleri

Yani, değişmez versiyonumuz için bunu yapabiliriz:

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

-1

Ad (değiştirilemez) olduğundan NSString, başka bir NSStringad verdiğinizde kopyala veya sakla fark etmez . Başka bir deyişle, kopya tıpkı alıkoyma gibi davranarak referans sayısını bir arttırır. Değişmez sınıflar için otomatik bir optimizasyon olduğunu düşünüyorum, çünkü bunlar değişmez ve klonlanmaya gerek yok. Ancak a NSMutalbeString mstrisimlendirildiğinde, mstrdoğruluğu için içeriği kopyalanacaktır.


1
Bildirilen türü gerçek türle karıştırıyorsunuz. Bir "retain" özelliği kullanır ve bir NSMutableString atarsanız, bu NSMutableString korunur, ancak yine de değiştirilebilir. "Copy" kullanırsanız, bir NSMutableString atadığınızda değiştirilemez bir kopya oluşturulur; bundan sonra özellik üzerindeki "kopya" sadece korunacaktır, çünkü değişebilen dizenin kopyası değişmezdir.
gnasher729

1
Burada bazı önemli gerçekleri kaçırıyorsunuz, korunmuş bir değişkenden gelen bir nesneyi kullanırsanız, bu değişken değiştirildiğinde, nesneniz de kopyalanır bir değişkenden geldiyse, nesneniz değişkenin geçerli değerine sahip olur. değişmeyecek
Bryan P

-1

Dize çok büyükse, kopyalama performansı etkiler ve büyük dizenin iki kopyası daha fazla bellek kullanır.

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.