วิธีคัดลอกวัตถุใน Objective-C


112

ฉันต้องการคัดลอกวัตถุแบบกำหนดเองที่มีวัตถุเป็นของตัวเอง ฉันอ่านมาเรื่อย ๆ และสับสนเล็กน้อยเกี่ยวกับวิธีการสืบทอด NSCopying และวิธีใช้ NSCopyObject


1
ดีสอนสำหรับการคัดลอกความเข้าใจ mutableCopy และ copyWithZone
horkavlna

คำตอบ:


192

เช่นเคยกับประเภทการอ้างอิงมีแนวคิดเกี่ยวกับ "สำเนา" อยู่สองประการ ฉันแน่ใจว่าคุณรู้จักพวกเขา แต่เพื่อความสมบูรณ์

  1. สำเนาบิต ในสิ่งนี้เราเพียงแค่คัดลอกบิตหน่วยความจำเป็นบิต - นี่คือสิ่งที่ NSCopyObject ทำ เกือบตลอดเวลาไม่ใช่สิ่งที่คุณต้องการ ออบเจ็กต์มีสถานะภายในออบเจ็กต์อื่น ๆ ฯลฯ และมักจะตั้งสมมติฐานว่าเป็นสิ่งเดียวที่มีการอ้างอิงถึงข้อมูลนั้น สำเนา Bitwise ทำลายข้อสันนิษฐานนี้
  2. สำเนาที่มีเหตุผลเชิงลึก ในสิ่งนี้เราทำสำเนาของวัตถุ แต่ไม่ได้ทำจริงทีละนิด - เราต้องการให้วัตถุที่ทำงานเหมือนกันสำหรับเจตนาและวัตถุประสงค์ทั้งหมด แต่ไม่ใช่ (จำเป็น) โคลนต้นฉบับที่เหมือนหน่วยความจำ - คู่มือ Objective C เรียกอ็อบเจ็กต์ดังกล่าวว่า "ไม่ขึ้นกับฟังก์ชัน" จากของเดิม เนื่องจากกลไกในการสร้างสำเนา "อัจฉริยะ" เหล่านี้แตกต่างกันไปในแต่ละคลาสเราจึงขอให้ออบเจ็กต์ดำเนินการเอง นี่คือโปรโตคอล NSCopying

คุณต้องการอย่างหลัง หากนี่เป็นหนึ่งในออบเจ็กต์ของคุณเองคุณต้องใช้โปรโตคอล NSCopying และใช้งาน - (id) copyWithZone: (NSZone *) zone คุณมีอิสระที่จะทำสิ่งที่คุณต้องการ แม้ว่าแนวคิดนี้คือคุณทำสำเนาตัวเองจริงและส่งคืน คุณเรียก copyWithZone ในทุกฟิลด์ของคุณเพื่อทำสำเนาแบบลึก ตัวอย่างง่ายๆคือ

@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;
}

แต่คุณกำลังทำให้ผู้รับวัตถุที่คัดลอกต้องรับผิดชอบในการปลดปล่อยมัน! คุณไม่ควรautoreleaseหรือฉันขาดอะไรที่นี่?
bobobobo

30
@bobobobo: ไม่กฎพื้นฐานของการจัดการหน่วยความจำ Objective-C คือ: คุณเป็นเจ้าของวัตถุถ้าคุณสร้างโดยใช้วิธีที่ชื่อขึ้นต้นด้วย "จัดสรร" หรือ "ใหม่" หรือประกอบด้วย "สำเนา" copyWithZone:ตรงตามเกณฑ์นี้ดังนั้นจึงต้องส่งคืนวัตถุที่มีจำนวนคงที่ +1
Steve Madsen

1
@ อดัมมีเหตุผลอะไรที่ต้องใช้allocแทนallocWithZone:ตั้งแต่ผ่านโซนมา?
Richard

3
โซนต่างๆไม่ได้ใช้อย่างมีประสิทธิภาพในช่วงเวลาที่ใช้ OS X สมัยใหม่ (เช่นฉันคิดว่ามันไม่เคยใช้เลย) แต่ใช่คุณสามารถโทรallocWithZone.
Adam Wright


25

เอกสารของ Apple กล่าวว่า

รุ่น subclass ของวิธี copyWithZone: ควรส่งข้อความไปยัง super ก่อนเพื่อรวมการนำไปใช้งานเว้นแต่ว่าคลาสย่อยจะลงมาจาก NSObject โดยตรง

เพื่อเพิ่มคำตอบที่มีอยู่

@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 ลงจาก NSObject โดยตรงฉันไม่คิดว่ามันจำเป็นที่นี่
Mike

2
จุดดี แต่เป็นกฎทั่วไปในกรณีที่มีลำดับชั้นของคลาสยาว
Saqib Saud

8
ฉันได้รับข้อผิดพลาด: No visible @interface for 'NSObject' declares the selector 'copyWithZone:'. ฉันเดาว่าสิ่งนี้จำเป็นเฉพาะเมื่อเรารับช่วงจากคลาสที่กำหนดเองอื่น ๆ ที่ใช้copyWithZone
แซม

1
another.obj = [[obj copyWithZone: zone] autorelease]; สำหรับคลาสย่อยทั้งหมดของ NSObject และสำหรับประเภทข้อมูลดั้งเดิมคุณเพียงแค่กำหนด -> another.someBOOL = self.someBOOL;
hariszaman

@Sam "NSObject เองไม่สนับสนุนโปรโตคอล NSCopying คลาสย่อยต้องสนับสนุนโปรโตคอลและใช้เมธอด copyWithZone: เมธอด copyWithZone รุ่นย่อยควรส่งข้อความไปยัง super ก่อนเพื่อรวมการใช้งานเว้นแต่คลาสย่อยจะลงมาโดยตรง จาก NSObject” developer.apple.com/documentation/objectivec/nsobject/…
s4mt6

21

ฉันไม่รู้ความแตกต่างระหว่างรหัสนั้นกับของฉัน แต่ฉันมีปัญหากับวิธีแก้ปัญหานั้นดังนั้นฉันจึงอ่านอีกเล็กน้อยและพบว่าเราต้องตั้งค่าวัตถุก่อนที่จะส่งคืน ฉันหมายถึงสิ่งที่ชอบ:

#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;
}

ฉันเพิ่มคำตอบนี้เนื่องจากฉันมีปัญหามากมายเกี่ยวกับปัญหานี้และฉันไม่รู้ว่าทำไมจึงเกิดขึ้น ฉันไม่รู้ความแตกต่าง แต่มันใช้ได้ผลสำหรับฉันและอาจเป็นประโยชน์สำหรับคนอื่น ๆ ด้วย :)


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

ฉันคิดว่าบรรทัดนี้ทำให้เกิดการรั่วไหลของหน่วยความจำเนื่องจากคุณเข้าถึงobjผ่านคุณสมบัติซึ่ง (ฉันถือว่า) ประกาศเป็นretain. ดังนั้นเก็บนับcopyWithZoneจะเพิ่มขึ้นโดยทรัพย์สินและ

ฉันเชื่อว่ามันควรจะเป็น:

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

หรือ:

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

ไม่วิธีการจัดสรรคัดลอก mutableCopy ใหม่ควรส่งคืนอ็อบเจ็กต์ที่ไม่ได้ปล่อยอัตโนมัติ
kovpas

@kovpas คุณแน่ใจหรือว่าคุณเข้าใจฉันถูกต้อง ฉันไม่ได้พูดถึงวัตถุที่ส่งคืนฉันกำลังพูดถึงช่องข้อมูล
Szuwar_Jr

ใช่ฉันไม่ดีขอโทษ คุณช่วยแก้ไขคำตอบของคุณเพื่อให้ฉันสามารถลบลบได้ไหม :))
kovpas

0

นอกจากนี้ยังมีการใช้ตัวดำเนินการ -> ในการคัดลอก ตัวอย่างเช่น:

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

การให้เหตุผลในที่นี้คืออ็อบเจ็กต์ที่คัดลอกผลลัพธ์ควรสะท้อนถึงสถานะของอ็อบเจ็กต์ดั้งเดิม "." ผู้ประกอบการสามารถแนะนำผลข้างเคียงเนื่องจากสิ่งนี้เรียก getters ซึ่งอาจมีตรรกะ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.