Triển khai NSCopying


84

Tôi đã đọc NSCopyingtài liệu nhưng vẫn không chắc chắn về cách triển khai những gì được yêu cầu.

Lớp tôi 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

Các Vendorlớp học có một mảng của các đối tượng được gọi là Car.

CarĐối tượng của tôi :

@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

Vì vậy, Vendorgiữ một mảng các Carđối tượng. Cargiữ 2 mảng của các đối tượng tùy chỉnh khác.

Cả hai VendorCarđều được lấy từ một từ điển. Tôi sẽ thêm một trong những phương pháp này, chúng có thể có liên quan hoặc không.

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

Vì vậy, để tóm tắt vấn đề đáng sợ.

Tôi cần sao chép một mảng các Vendorđối tượng. Tôi tin rằng tôi cần triển khai NSCopyinggiao thức trên Vendor, có thể có nghĩa là tôi cũng cần triển khai nó CarVendorcó một mảng Cars. Điều đó có nghĩa là tôi cũng cần triển khai nó trên các lớp được tổ chức trong 2 mảng thuộc Carđối tượng.

Tôi thực sự đánh giá cao nếu tôi có thể nhận được một số hướng dẫn về triển khai NSCopyinggiao thức Vendor, tôi không thể tìm thấy bất kỳ hướng dẫn nào về điều này ở bất kỳ đâu.


Bạn đã đọc tài liệu của NSCopying chưa? Tôi thấy nó khá rõ ràng khi cần thiết.
jv42

4
Vâng, đọc và nó đọc lại nó. Tôi hiếm khi thấy tài liệu của apple dễ học, mặc dù chúng rất tốt để tìm các phương pháp, v.v. trong khi lập trình. Cảm ơn -Mã

Câu trả lời:


186

Để thực hiện NSCopying , đối tượng của bạn phải phản hồi với -copyWithZone:bộ chọn. Đây là cách bạn tuyên bố rằng bạn tuân thủ nó:

@interface MyObject : NSObject <NSCopying> {

Sau đó, trong triển khai đối tượng của bạn ( .mtệp của bạn ):

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

Mã của bạn nên làm gì? Đầu tiên, tạo một thể hiện mới của đối tượng — bạn có thể gọi [[[self class] alloc] init]để nhận một obejct đã khởi tạo của lớp hiện tại, hoạt động tốt cho phân lớp. Sau đó, đối với bất kỳ biến cá thể nào là lớp con NSObjecthỗ trợ sao chép, bạn có thể gọi [thatObject copyWithZone:zone]đối tượng mới. Đối với các loại nguyên thủy ( int, char, BOOLvà bạn bè) chỉ cần thiết lập các biến để được bình đẳng. Vì vậy, đối với Nhà cung cấp trực tiếp của bạn, nó sẽ giống như sau:

- (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: copythường được triển khai dưới dạng một bản sao cạn như Jeff đã trình bày. Điều bất thường - mặc dù không phải là không thể tưởng tượng được - mà bạn muốn có một bản sao sâu hoàn chỉnh (nơi mọi thứ từ đầu đến cuối đều được sao chép). Bản sao sâu cũng rắc rối hơn nhiều, vì vậy bạn thường muốn chắc chắn rằng đó thực sự là những gì bạn muốn.
Chuck

3
Có sự cố trong mã của bạn khi bạn sao chép các lớp con của mình, vì copyWithZone:trả về một đối tượng có số tham chiếu là 1 và không có chế độ tự động khôi phục, điều này sẽ gây ra rò rỉ. Bạn cần thêm ít nhất một autorelease.
Marius

22
Không nên [[self class] alloc]sử dụng allocWithZonethay thế? Xin lỗi vì đã đưa ra điều này.
jweyrich

1
Mọi người, tôi cho rằng bằng cách sử dụng ARC (vì IOS được hỗ trợ tối thiểu cho bất kỳ ứng dụng nào là 4.3), bạn không cần lo lắng về việc phát hành và tự động phát hành.
rishabh

1
@GeneralMike: Đây có lẽ nên là một câu hỏi riêng, nhưng nói chung (xem tôi đã làm gì ở đó?), Bạn muốn đảm bảo sao chép mọi đối tượng từ bản gốc trong quá trình sao chép sâu — và đảm bảo rằng các -copyphương pháp của họ cũng tạo bản sao sâu .
Jeff Kelley

6

Câu trả lời này tương tự như câu trả lời được chấp nhận, nhưng sử dụng allocWithZone:và được cập nhật cho ARC. NSZone là lớp nền tảng để cấp phát bộ nhớ. Mặc dù bỏ qua NSZonecó thể hiệu quả đối với hầu hết các trường hợp, nhưng nó vẫn không chính xác.

Để triển khai chính xác, NSCopyingbạn phải triển khai một phương thức giao thức phân bổ một bản sao mới của đối tượng, với các thuộc tính khớp với các giá trị của bản gốc.

Trong phần khai báo giao diện ở tiêu đề, hãy chỉ định rằng lớp của bạn triển khai NSCopyinggiao thức:

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

Trong triển khai .m, hãy thêm một -(id)copyWithZonephương thức trông giống như sau:

- (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

Phiên bản Swift

Chỉ cần gọi object.copy()để tạo bản sao.

Tôi đã không sử dụng copy()cho các loại giá trị vì chúng được sao chép "tự động". Nhưng tôi đã phải sử dụng copy()cho classcác loại.

Tôi đã bỏ qua NSZonetham số vì tài liệu nói rằng nó không được dùng nữa:

Tham số này bị bỏ qua. Objective-C không còn sử dụng vùng bộ nhớ nữa.

Ngoài ra, xin lưu ý rằng đây là một triển khai đơn giản hóa. Nếu bạn có các lớp con nó được một tricker chút và bạn nên sử dụng loại động: 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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.