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

Vendor클래스라는 객체의 배열이있다 Car.

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하는데, s 배열을 보유하고 Car있기 때문에 구현해야 할 수도 있습니다 . 즉, 객체에 속하는 2 개의 배열에있는 클래스에서도이를 구현해야 합니다.VendorCarCar

에서 NSCopying프로토콜을 구현하는 방법에 대한 지침을 얻을 수 있다면 정말 감사하겠습니다. 여기에 Vendor대한 튜토리얼은 어디에서도 찾을 수 없습니다.


NSCopying 문서를 읽었습니까? 나는 필요할 때 그것을 아주 명확하게 알았다.
jv42 2010

4
예, 읽고 다시 읽습니다. 프로그래밍하는 동안 방법 등을 찾는 데는 좋지만 애플 문서를 배우기 쉬운 경우는 거의 없습니다. 감사합니다 -Code

답변:


186

NSCopying 을 구현하려면 객체가 -copyWithZone:선택자에 응답해야합니다 . 준수를 선언하는 방법은 다음과 같습니다.

@interface MyObject : NSObject <NSCopying> {

그런 다음 개체의 구현 ( .m파일)에서 :

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

코드는 무엇을해야합니까? 먼저 개체의 새 인스턴스를 만듭니다. 호출 [[[self class] alloc] init]하여 현재 클래스의 초기화 된 개체 를 가져올 수 있습니다 . 이는 하위 클래스에 적합합니다. 그런 다음 NSObject복사를 지원 하는의 하위 클래스 인 인스턴스 변수 [thatObject copyWithZone:zone]에 대해 새 개체를 호출 할 수 있습니다 . 프리미티브 유형 ( int, char, BOOL친구)는 단지 동일하게 변수를 설정. 따라서 obejct Vendor의 경우 다음과 같이 표시됩니다.

- (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는 일반적으로 Jeff가 보여준 것처럼 얕은 복사본으로 구현됩니다. 상상할 수는 없지만 전체적으로 딥 카피를 원한다는 것은 드문 일입니다 (모든 것이 아래로 모두 복사되는). 딥 카피는 훨씬 더 많은 문제가되기 때문에 일반적으로 그것이 실제로 원하는 것인지 확인하고자합니다.
Chuck

3
copyWithZone:참조 횟수가 1 인 객체를 반환하고 자동 릴리스가 없으면 누수가 발생하므로 하위 클래스를 복사하는 코드에 문제 가 있습니다. 적어도 하나의 자동 릴리스를 추가해야합니다.
Marius

22
[[self class] alloc]사용하는 allocWithZone대신? 이 문제를 제기해서 죄송합니다.
jweyrich

1
여러분, ARC (모든 앱에 대해 지원되는 최소 IOS가 4.3이기 때문에)를 사용하면 릴리스 및 자동 릴리스에 대해 걱정할 필요가 없습니다.
rishabh 2012

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

스위프트 버전

object.copy()사본을 만들려면 전화 하십시오.

copy()값 유형은 "자동으로"복사되므로 사용하지 않았습니다 . 그러나 나는 유형 에 사용해야 copy()했습니다 class.

문서에서 더 이상 사용되지 않는다고 말했기NSZone 때문에 매개 변수를 무시 했습니다.

이 매개 변수는 무시됩니다. Objective-C는 메모리 영역을 더 이상 사용하지 않습니다.

또한 이것은 단순화 된 구현입니다. 하위 클래스 가있는 경우 약간 까다로워지고 dynamic type :을 사용해야합니다 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.