Pytanie Implementacja NSCopying


Czytałem NSCopying doktorzy, ale nadal nie jestem pewien, jak wdrożyć to, co jest wymagane.

Moja klasa 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

The Vendor klasa ma tablicę obiektów nazywanych Car.

Mój Car obiekt:

@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

Więc, Vendor trzyma tablicę Car obiekty. Car przechowuje 2 tablice innych niestandardowych obiektów.

Obie Vendor i Car są init ze słownika. Dodam jedną z tych metod, mogą one lub nie mogą być istotne.

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

Podsumowując przerażający problem.

Muszę skopiować tablicę Vendor obiekty. Uważam, że muszę wprowadzić w życie NSCopying protokół włączony Vendor, co może oznaczać, że muszę go również wdrożyć Car od Vendor trzyma tablicę Cars. Oznacza to, że muszę go również wdrożyć na zajęciach, które odbywają się w 2 tablicach należących do Car obiekt.

Byłbym bardzo wdzięczny, gdybym mógł uzyskać wskazówki dotyczące wdrażania NSCopying protokół włączony Vendor, Nie mogę znaleźć żadnych tutoriali na ten temat w dowolnym miejscu.


76
2017-11-03 16:28


pochodzenie


Czy czytałeś dokumentację NSCopying? Zauważyłem, że jest to całkiem jasne w razie potrzeby. - jv42
Tak, przeczytaj i ponownie przeczytaj. Rzadko spotykam się z łatwą nauką, z której można się nauczyć z Apple, choć świetnie nadają się do szukania metod itp. Podczas programowania. Dzięki -Kode


Odpowiedzi:


Aby zaimplementować NSCopyingTwój obiekt musi odpowiedzieć na -copyWithZone: selektor. Oto, jak deklarujesz, że się do niego dostosowujesz:

@interface MyObject : NSObject <NSCopying> {

Następnie w implementacji obiektu (twój .m plik):

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

Co powinien zrobić twój kod? Najpierw utwórz nowe wystąpienie obiektu - możesz zadzwonić [[[self class] alloc] init] uzyskać inicjalizację obejścia bieżącej klasy, która działa dobrze dla podklasy. Następnie dla każdej instancji zmiennych, które są podklasą NSObject który obsługuje kopiowanie, możesz zadzwonić [thatObject copyWithZone:zone] dla nowego obiektu. Dla typów pierwotnych (int, char, BOOL i przyjaciele) ustawiaj zmienne tak, aby były równe. Tak więc, dla twojego obejct Vendor, wyglądałoby to tak:

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

174
2017-11-03 16:42



@Kod: copy jest zwykle zaimplementowany jako płytka kopia, jak pokazał Jeff. To niezwykłe - choć nie do pomyślenia - że chcesz mieć pełną, głęboką kopię (w której kopiowane jest wszystko na dole). Głębokie kopie to o wiele więcej problemów, więc na ogół chcesz mieć pewność, że naprawdę tego chcesz. - Chuck
Występuje problem w kodzie, w którym kopiujesz swoje podklasy, jako copyWithZone: zwraca obiekt z liczbą odwołań równą 1 i bez autoreasu spowoduje to wyciek. Musisz dodać co najmniej autorelease. - Marius
Nie powinno [[self class] alloc] posługiwać się allocWithZone zamiast? Przepraszamy za to. - jweyrich
Ludzie, przypuszczam, że używając ARC (ponieważ minimalna obsługiwana IOS dla każdej aplikacji wynosi 4.3), nie musisz martwić się o wydanie i automatyczne zwolnienie. - rishabh
@GeneralMike: To prawdopodobnie powinno być oddzielne pytanie, ale ogólnie (zobacz co tam zrobiłem?), Chcesz mieć pewność, że skopiujesz każdy obiekt z oryginału podczas głębokiej kopii - i upewnij się, że ich -copy metody również wykonują głębokie kopie. - Jeff Kelley


Ta odpowiedź jest podobna do akceptowanych, ale używanych allocWithZone: i jest aktualizowany dla ARC. NSZone jest klasą podstawową do przydzielania pamięci. Podczas ignorowania NSZone może działać w większości przypadków, nadal jest niepoprawny.

Aby poprawnie zaimplementować NSCopying musisz zaimplementować metodę protokołu, która przydziela nową kopię obiektu, z właściwościami, które odpowiadają wartościom oryginału.

W deklaracji interfejsu w nagłówku określ, że twoja klasa implementuje NSCopying protokół:

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

W implementacji .m dodaj -(id)copyWithZone metoda, która wygląda mniej więcej tak:

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

4
2018-01-03 17:19





Wersja Swift

Zadzwoń object.copy() aby utworzyć kopię.

Nie użyłem copy() dla typów wartości, ponieważ są one kopiowane "automatycznie". Ale musiałem użyć copy() dla class typy.

Zignorowałem NSZone parametr, ponieważ docs powiedzieć, że jest przestarzałe:

Ten parametr jest ignorowany. Strefy pamięci nie są już używane przez   Cel C.

Zwróć też uwagę, że jest to uproszczona implementacja. Jeśli masz podklasy robi się trochę tricker i powinieneś użyć typu dynamicznego: 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
    }
}

0
2018-01-08 16:38