web-dev-qa-db-ja.com

NSCopyingの実装

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

したがって、VendorCarオブジェクトの配列を保持します。 Carは、他のカスタムオブジェクトの2つの配列を保持します。

VendorCarは両方とも辞書からの初期化です。これらのメソッドの1つを追加します。関連する場合と関連しない場合があります。

-(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オブジェクトの配列をコピーする必要があります。 NSCopyingVendorプロトコルを実装する必要があると思います。つまり、CarにはVendorの配列が保持されているため、Carにも実装する必要があります。 [$ var] _s。つまり、Carオブジェクトに属する2つの配列に保持されているクラスにも実装する必要があります。

NSCopyingVendorプロトコルを実装するためのガイダンスを得ることができれば、本当に感謝しています。これに関するチュートリアルはどこにもありません。

78
user440096

NSCopying を実装するには、オブジェクトが-copyWithZone:セレクターに応答する必要があります。準拠すると宣言する方法は次のとおりです。

@interface MyObject : NSObject <NSCopying> {

次に、オブジェクトの実装(.mファイル)で:

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

あなたのコードは何をすべきですか?最初に、オブジェクトの新しいインスタンスを作成します。[[[self class] alloc] init]を呼び出して、現在のクラスの初期化されたオブジェクトを取得できます。これは、サブクラス化に適しています。次に、コピーをサポートするNSObjectのサブクラスであるインスタンス変数について、新しいオブジェクトに対して[thatObject copyWithZone:zone]を呼び出すことができます。プリミティブ型(intcharBOOLおよびその仲間)の場合、変数を等しくなるように設定するだけです。したがって、対象ベンダーの場合、次のようになります。

- (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;
}
179
Jeff Kelley

この回答は受け入れられたものと似ていますが、_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;
}
_
6
Justin Meiners

Swiftバージョン

object.copy()を呼び出してコピーを作成します。

値の型は「自動的に」コピーされるため、copy()は使用しませんでした。しかし、class型にはcopy()を使用する必要がありました。

docs 推奨されていないと言うため、NSZoneパラメーターを無視しました。

このパラメーターは無視されます。メモリゾーンは、Objective-Cでは使用されなくなりました。

また、これは簡略化された実装であることに注意してください。 サブクラスがある場合、少し巧妙になりますので、動的型: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
    }
}
1
kgaidis