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つの配列を保持します。

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オブジェクトの配列をコピーする必要があります。にNSCopyingプロトコルを実装する必要があると思います。これは、の配列を保持VendorCarVendorいるため、にも実装する必要があることを意味する場合がありCarます。つまり、Carオブジェクトに属する2つの配列に保持されているクラスにも実装する必要があります。

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


NSCopyingのドキュメントを読みましたか?必要なときにそれが非常に明確であることがわかりました。
jv42 2010年

4
はい、読んで、それを再読します。プログラミング中にメソッドなどを見つけるのに最適ですが、アップルのドキュメントを簡単に学ぶことはめったにありません。ありがとう

回答:


186

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

2
@Code:copy通常、Jeffが示したような浅いコピーとして実装されます。考えられないことではありませんが、完全なディープコピー(すべてがコピーされる)が必要になるのは珍しいことです。ディープコピーもはるかに厄介なので、通常、それが本当に必要なものであることを確認する必要があります。
チャック

3
サブクラスをコピーするコードに問題があります。copyWithZone:参照カウントが1のオブジェクトを返し、自動解放がない場合、リークが発生します。少なくとも自動リリースを追加する必要があります。
マリウス

22
代わりに[[self class] alloc]使用するべきではありませんallocWithZoneか?これを持ってきてすみません。
jweyrich 2012年

1
皆さん、ARCを使用することで(どのアプリでもサポートされる最小のIOSは4.3なので)、リリースと自動リリースについて心配する必要はないと思います。
rishabh 2012

1
@GeneralMike:これはおそらく別の質問になるはずですが、一般的に(私がそこで何をしたかを参照してください)、ディープコピー中に元のオブジェクトからすべてのオブジェクトをコピーし、それらの-copyメソッドもディープコピーを実行するようにします。 。
ジェフケリー

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では使用されなくなりました。

また、これは単純化された実装であることに注意してください。サブクラスがある場合は少し注意が必要なので、動的型を使用する必要があります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.