การใช้ NSCopying


84

ฉันได้อ่านNSCopyingเอกสารแล้ว แต่ยังไม่แน่ใจว่าจะนำสิ่งที่จำเป็นไปใช้ได้อย่างไร

ชั้นเรียนของฉันVendor:

@interface Vendor : NSObject 
{
    NSString        *vendorID;
    NSMutableArray  *availableCars;
    BOOL            atAirport;
}

@property (nonatomic, copy) NSString *vendorID;
@property (nonatomic, retain) NSMutableArray *availableCars;
@property (nonatomic, assign) BOOL atAirport;

- (id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails;

@end

ชั้นมีอาร์เรย์ของวัตถุที่เรียกว่าVendorCar

Carวัตถุของฉัน:

@interface Car : NSObject 
{
    BOOL            isAvailable;
    NSString        *transmissionType;
    NSMutableArray  *vehicleCharges; 
    NSMutableArray  *fees; 
}

@property (nonatomic, assign) BOOL isAvailable;
@property (nonatomic, copy) NSString *transmissionType;
@property (nonatomic, retain) NSMutableArray *vehicleCharges;
@property (nonatomic, retain) NSMutableArray *fees;

- (id) initFromVehicleDictionary:(NSDictionary *)vehicleDictionary;

@end

ดังนั้นVendorถืออาร์เรย์ของCarวัตถุ Carมี 2 ​​อาร์เรย์ของวัตถุที่กำหนดเองอื่น ๆ

ทั้งสองVendorและCarเริ่มต้นจากพจนานุกรม ฉันจะเพิ่มหนึ่งในวิธีต่อไปนี้อาจเกี่ยวข้องหรือไม่ก็ได้

-(id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails {

    self.vendorCode      = [[vehVendorAvails objectForKey:@"Vendor"] 
                           objectForKey:@"@Code"];

    self.vendorName      = [[vehVendorAvails objectForKey:@"Vendor"] 
                           objectForKey:@"@CompanyShortName"];

    self.vendorDivision  = [[vehVendorAvails objectForKey:@"Vendor"]   
                           objectForKey:@"@Division"];

    self.locationCode    = [[[vehVendorAvails objectForKey:@"Info"] 
                           objectForKey:@"LocationDetails"] 
                           objectForKey:@"@Code"];

    self.atAirport       = [[[[vehVendorAvails objectForKey:@"Info"] 
                           objectForKey:@"LocationDetails"] 
                           objectForKey:@"@AtAirport"] boolValue];

    self.venLocationName = [[[vehVendorAvails objectForKey:@"Info"] 
                           objectForKey:@"LocationDetails"] 
                           objectForKey:@"@Name"];

    self.venAddress      = [[[[vehVendorAvails objectForKey:@"Info"] 
                           objectForKey:@"LocationDetails"] 
                           objectForKey:@"Address"] 
                           objectForKey:@"AddressLine"];

    self.venCountryCode  = [[[[[vehVendorAvails objectForKey:@"Info"]  
                           objectForKey:@"LocationDetails"] 
                           objectForKey:@"Address"] 
                           objectForKey:@"CountryName"]
                           objectForKey:@"@Code"];

    self.venPhone        = [[[[vehVendorAvails objectForKey:@"Info"]  
                           objectForKey:@"LocationDetails"]        
                           objectForKey:@"Telephone"] 
                           objectForKey:@"@PhoneNumber"];

    availableCars        = [[NSMutableArray alloc] init];

    NSMutableArray *cars = (NSMutableArray *)[vehVendorAvails objectForKey:@"VehAvails"];

    for (int i = 0; i < [cars count]; i++) {

        Car *car = [[Car alloc] initFromVehicleDictionary:[cars objectAtIndex:i]];
        [availableCars addObject:car];
        [car release];
    }

    self.venLogo = [[[vehVendorAvails objectForKey:@"Info"] 
                   objectForKey:@"TPA_Extensions"] 
                   objectForKey:@"VendorPictureURL"];

    return self;
}

ดังนั้นเพื่อสรุปปัญหาที่น่ากลัว

ฉันต้องการคัดลอกอาร์เรย์ของVendorวัตถุ ฉันเชื่อว่าฉันจำเป็นต้องใช้NSCopyingโปรโตคอลVendorซึ่งอาจหมายความว่าฉันต้องใช้มันด้วยCarเนื่องจากVendorมีอาร์เรย์ของCars นั่นหมายความว่าฉันต้องนำไปใช้กับคลาสที่จัดขึ้นใน 2 อาร์เรย์ที่เป็นของCarออบเจ็กต์

ฉันจะขอบคุณมากถ้าฉันได้รับคำแนะนำในการใช้งานNSCopyingโปรโตคอลVendorฉันไม่พบบทช่วยสอนใด ๆ เกี่ยวกับเรื่องนี้เลย


คุณอ่านเอกสารของ NSCopying หรือยัง? ฉันพบว่ามันค่อนข้างชัดเจนเมื่อจำเป็น
jv42

4
ใช่อ่านและอ่านซ้ำ ฉันไม่ค่อยพบว่า apple docs ง่ายต่อการเรียนรู้แม้ว่ามันจะยอดเยี่ยมสำหรับการค้นหาวิธีการ ฯลฯ ในขณะที่เขียนโปรแกรม ขอบคุณ -Code

คำตอบ:


186

ในการใช้NSCopyingวัตถุของคุณต้องตอบสนองต่อ-copyWithZone:ตัวเลือก นี่คือวิธีที่คุณประกาศว่าคุณปฏิบัติตาม:

@interface MyObject : NSObject <NSCopying> {

จากนั้นในการใช้งานวัตถุของคุณ ( .mไฟล์ของคุณ):

- (id)copyWithZone:(NSZone *)zone
{
    // Copying code here.
}

โค้ดของคุณควรทำอย่างไร? ขั้นแรกให้สร้างอินสแตนซ์ใหม่ของออบเจ็กต์ - คุณสามารถเรียก[[[self class] alloc] init]เพื่อเริ่มต้น obejct ของคลาสปัจจุบันซึ่งทำงานได้ดีสำหรับคลาสย่อย จากนั้นสำหรับตัวแปรอินสแตนซ์ใด ๆ ที่เป็นคลาสย่อยNSObjectที่รองรับการคัดลอกคุณสามารถเรียก[thatObject copyWithZone:zone]ใช้อ็อบเจ็กต์ใหม่ สำหรับรูปแบบดั้งเดิม ( int, char, BOOLและเพื่อน ๆ ) เพียงแค่ตั้งค่าตัวแปรที่จะเท่าเทียมกัน ดังนั้นสำหรับผู้ขายที่เชื่อฟังของคุณจะมีลักษณะดังนี้:

- (id)copyWithZone:(NSZone *)zone
{
    id copy = [[[self class] alloc] init];

    if (copy) {
        // Copy NSObject subclasses
        [copy setVendorID:[[self.vendorID copyWithZone:zone] autorelease]];
        [copy setAvailableCars:[[self.availableCars copyWithZone:zone] autorelease]];

        // Set primitives
        [copy setAtAirport:self.atAirport];
    }

    return copy;
}

2
@Code: copyโดยทั่วไปจะใช้เป็นสำเนาตื้น ๆ เหมือนที่เจฟฟ์แสดง เป็นเรื่องผิดปกติ - แม้ว่าจะนึกไม่ถึง - ที่คุณต้องการสำเนาลึกแบบเต็มรูปแบบ การทำสำเนาลึก ๆ ก็เป็นปัญหามากกว่าเช่นกันดังนั้นโดยทั่วไปคุณต้องแน่ใจว่านั่นคือสิ่งที่คุณต้องการจริงๆ
Chuck

3
มีปัญหาในโค้ดของคุณที่คุณคัดลอกคลาสย่อยของคุณเนื่องจากcopyWithZone:ส่งคืนอ็อบเจ็กต์ที่มีจำนวนอ้างอิงเป็น 1 และไม่มีการปล่อยอัตโนมัติซึ่งจะทำให้เกิดการรั่วไหล คุณต้องเพิ่มการปล่อยอัตโนมัติอย่างน้อย
Marius

22
ไม่ควร[[self class] alloc]ใช้allocWithZoneแทน? ขออภัยที่นำเรื่องนี้ขึ้น
jweyrich

1
ฉันคิดว่าโดยใช้ ARC (เนื่องจาก IOS ที่รองรับขั้นต่ำสำหรับแอปใด ๆ คือ 4.3) คุณไม่จำเป็นต้องกังวลเกี่ยวกับการเปิดตัวและการปล่อยอัตโนมัติ
rishabh

1
@GeneralMike: นี่น่าจะเป็นคำถามแยกกัน แต่โดยทั่วไปแล้ว (ดูว่าฉันทำอะไรที่นั่น?) คุณต้องแน่ใจว่าได้คัดลอกทุกออบเจ็กต์จากต้นฉบับในระหว่างการคัดลอกแบบลึก - และตรวจสอบให้แน่ใจว่า-copyวิธีการของพวกเขาทำสำเนาลึกด้วย .
Jeff Kelley

6

คำตอบนี้คล้ายกับคำตอบที่ยอมรับ แต่ใช้allocWithZone:และได้รับการอัปเดตสำหรับ ARC NSZone เป็นคลาสพื้นฐานสำหรับการจัดสรรหน่วยความจำ แม้ว่าการละเว้นNSZoneอาจได้ผลในกรณีส่วนใหญ่ แต่ก็ยังไม่ถูกต้อง

ในการนำไปใช้อย่างถูกต้องNSCopyingคุณต้องใช้วิธีการโปรโตคอลที่จัดสรรสำเนาใหม่ของวัตถุพร้อมคุณสมบัติที่ตรงกับค่าของต้นฉบับ

ในการประกาศอินเทอร์เฟซในส่วนหัวระบุว่าคลาสของคุณใช้NSCopyingโปรโตคอล:

@interface Car : NSObject<NSCopying>
{
 ...
}

ในการใช้งาน. m ให้เพิ่ม-(id)copyWithZoneวิธีการซึ่งมีลักษณะดังนี้:

- (id)copyWithZone:(NSZone*)zone
{
    Car* carCopy = [[[self class] allocWithZone:zone] init];

    if (carCopy)
    {
        carCopy.isAvailable = _isAvailable;
        carCopy.transmissionType = _transmissionType;
        ... // assign all other properties.
    }

    return carCopy;
}

2

เวอร์ชัน Swift

เพียงแค่เรียกobject.copy()สร้างสำเนา

ฉันไม่ได้ใช้copy()สำหรับประเภทค่าเนื่องจากจะถูกคัดลอก "โดยอัตโนมัติ" แต่ฉันต้องใช้copy()สำหรับclassประเภท

ฉันไม่สนใจNSZoneพารามิเตอร์เพราะเอกสารบอกว่าเลิกใช้แล้ว:

พารามิเตอร์นี้ถูกละเว้น Objective-C ไม่ได้ใช้โซนหน่วยความจำอีกต่อไป

นอกจากนี้โปรดทราบว่านี่เป็นการใช้งานที่เรียบง่าย ถ้าคุณมี subclassesจะได้รับ tricker type(of: self).init(transmissionType: transmissionType)บิตและคุณควรใช้ชนิดแบบไดนามิก:

class Vendor {
    let vendorId: String
    var availableCars: [Car] = []

    init(vendorId: String) {
        self.vendorId = vendorId
    }
}

extension Vendor: NSCopying {
    func copy(with zone: NSZone? = nil) -> Any {
        let copy = Vendor(vendorId: vendorId)
        if let availableCarsCopy = availableCars.map({$0.copy()}) as? [Car] {
            copy.availableCars = availableCarsCopy
        }
        return copy
    }
}

class Car {
    let transmissionType: String
    var isAvailable: Bool = false
    var fees: [Double] = []

    init(transmissionType: String) {
        self.transmissionType = transmissionType
    }
}

extension Car: NSCopying {
    func copy(with zone: NSZone? = nil) -> Any {
        let copy = Car(transmissionType: transmissionType)
        copy.isAvailable = isAvailable
        copy.fees = fees
        return copy
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.