Objective-C'de bir nesne nasıl kopyalanır


112

Kendine ait nesneleri olan özel bir nesneyi derinlemesine kopyalamam gerekiyor. NSCopying'i nasıl miras alacağımı ve NSCopyObject'i nasıl kullanacağımı okudum ve biraz kafam karıştı.


1
Büyük ÖĞRETICISI anlayış kopya, mutableCopy ve copyWithZone için
horkavlna

Yanıtlar:


192

Referans türlerinde her zaman olduğu gibi, iki "kopya" kavramı vardır. Eminim onları biliyorsunuzdur, ama bütünlük açısından.

  1. Bitsel bir kopya. Burada, sadece bellek bitini bit kopyalıyoruz - NSCopyObject bunu yapıyor. Neredeyse her zaman, istediğin bu değil. Nesnelerin dahili durumu, diğer nesneler vb. Vardır ve genellikle bu verilere referans tutan tek nesnelerin kendileri olduğu varsayımlarında bulunur. Bitsel kopyalar bu varsayımı bozar.
  2. Derin, mantıklı bir kopya. Bunda, nesnenin bir kopyasını oluşturuyoruz, ancak aslında onu parça parça yapmadan - tüm amaç ve amaçlar için aynı şekilde davranan, ancak (zorunlu olarak) orijinalin bellekle özdeş bir klonu olmayan bir nesne istiyoruz - Objective C kılavuzu, böyle bir nesneyi orijinalinden "işlevsel olarak bağımsız" olarak adlandırır. Bu "akıllı" kopyaları yapma mekanizmaları sınıftan sınıfa değiştiğinden, nesnelerin kendilerinden bunları gerçekleştirmelerini isteriz. Bu, NSCopying protokolüdür.

İkincisini istiyorsun. Bu kendi nesnelerinizden biriyse, NSCopying protokolünü benimsemeniz ve - (id) copyWithZone: (NSZone *) bölgesini uygulamanız yeterlidir. Ne istersen yapmakta özgürsün; Ancak fikir, kendinizin gerçek bir kopyasını yapıp geri vermenizdir. Derin bir kopya oluşturmak için tüm alanlarınızda copyWithZone'u çağırırsınız. Basit bir örnek

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  // We'll ignore the zone for now
  YourClass *another = [[YourClass alloc] init];
  another.obj = [obj copyWithZone: zone];

  return another;
}

Ama kopyalanan nesnenin alıcısını onu serbest bırakmakla sorumlu tutuyorsunuz! Sen yapmamalısın autorelease, yoksa burada bir şey mi özlüyorum?
bobobobo

30
@bobobo: Hayır, Objective-C bellek yönetiminin temel kuralı: Bir nesneyi, adı “tahsis” veya “yeni” ile başlayan veya “kopya” içeren bir yöntem kullanarak oluşturursanız, nesnenin sahipliğini alırsınız. copyWithZone:bu kriteri karşıladığından, tutma sayısı +1 olan bir nesne döndürmelidir.
Steve Madsen

1
@Adam Bölgeye geçildiğinden beri kullanmak allocyerine kullanmak için bir neden var mı allocWithZone:?
Richard

3
Pekala, bölgeler modern OS X tabanlı çalışma zamanlarında etkili bir şekilde kullanılmıyor (yani, kelimenin tam anlamıyla hiç kullanılmadıklarını düşünüyorum). Ama evet, arayabilirsin allocWithZone.
Adam Wright


25

Apple belgeleri diyor

CopyWithZone: yönteminin bir alt sınıf sürümü, alt sınıf doğrudan NSObject'ten gelmedikçe, uygulamasını dahil etmek için mesajı süper'e göndermelidir.

mevcut cevaba eklemek için

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  YourClass *another = [super copyWithZone:zone];
  another.obj = [obj copyWithZone: zone];

  return another;
}

2
YourClass doğrudan NSObject'ten geldiği için burada gerekli olduğunu düşünmüyorum
Mike

2
İyi bir nokta, ancak uzun bir sınıf hiyerarşisi olması durumunda bu genel bir kural.
Saqib Saud

8
Bir hata var: No visible @interface for 'NSObject' declares the selector 'copyWithZone:'. Sanırım bu sadece uygulayan başka bir özel sınıftan miras alırken gerekliydicopyWithZone
Sam

1
another.obj = [[obj copyWithZone: zone] autorelease]; NSObject'in tüm alt sınıfları için. Ve ilkel veri türleri için onları atarsınız -> another.someBOOL = self.someBOOL;
hariszaman

@Sam "NSObject, NSCopying protokolünü kendi başına desteklemez. Alt sınıflar protokolü desteklemeli ve copyWithZone: yöntemini uygulamalıdır. CopyWithZone: yönteminin bir alt sınıf sürümü, alt sınıf doğrudan alçalmadıkça, uygulamasını dahil etmek için iletiyi ilk önce süper'e göndermelidir NSObject'ten. " developer.apple.com/documentation/objectivec/nsobject/…
s4mt6

21

Bu kodla benimki arasındaki farkı bilmiyorum, ancak bu çözümle ilgili sorunlarım var, bu yüzden biraz daha okudum ve nesneyi iade etmeden önce ayarlamamız gerektiğini gördüm. Şöyle bir şey demek istiyorum:

#import <Foundation/Foundation.h>

@interface YourObject : NSObject <NSCopying>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *line;
@property (strong, nonatomic) NSMutableString *tags;
@property (strong, nonatomic) NSString *htmlSource;
@property (strong, nonatomic) NSMutableString *obj;

-(id) copyWithZone: (NSZone *) zone;

@end


@implementation YourObject


-(id) copyWithZone: (NSZone *) zone
{
    YourObject *copy = [[YourObject allocWithZone: zone] init];

    [copy setNombre: self.name];
    [copy setLinea: self.line];
    [copy setTags: self.tags];
    [copy setHtmlSource: self.htmlSource];

    return copy;
}

Bu yanıtı ekledim çünkü bu sorunla ilgili çok sorunum var ve neden olduğu konusunda hiçbir fikrim yok. Farkı bilmiyorum ama benim için çalışıyor ve belki başkaları için de faydalı olabilir :)


3
another.obj = [obj copyWithZone: zone];

Sanırım, bu satır bellek sızıntısına neden oluyor, çünkü obj(sanırım) olarak ilan edilen özelliğe erişiyorsunuz retain. Yani, alıkoyma sayısı mülkiyet tarafından artırılacak ve copyWithZone.

Bunun olması gerektiğine inanıyorum:

another.obj = [[obj copyWithZone: zone] autorelease];

veya:

SomeOtherObject *temp = [obj copyWithZone: zone];
another.obj = temp;
[temp release]; 

Hayır, yöntemler tahsis, kopya, mutableCopy, new, otomatik olmayan nesneleri döndürmelidir.
kovpas

@kovpas, beni anladığına emin misin? Döndürülen nesneden bahsetmiyorum, veri alanlarından bahsediyorum.
Szuwar_Jr

evet, benim hatam, üzgünüm. Lütfen cevabınızı bir şekilde düzenler misiniz, böylece eksiyi kaldırabilirim? :))
kovpas

0

Kopyalama için -> operatörünün kullanımı da vardır. Örneğin:

-(id)copyWithZone:(NSZone*)zone
{
    MYClass* copy = [MYClass new];
    copy->_property1 = self->_property1;
    ...
    copy->_propertyN = self->_propertyN;
    return copy;
}

Buradaki mantık, kopyalanan nesnenin orijinal nesnenin durumunu yansıtması gerektiğidir. "." Bu, alıcıları aradığından operatör yan etkiler getirebilir ve bu da mantık içerebilir.

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.