web-dev-qa-db-ja.com

元のクラスが利用できない場合、オブジェクトをどのようにデコードできますか?

カスタムオブジェクトをアプリのiCloud Docsフォルダーにファイルとして保存するiOS 7アプリケーションがあります。これには、NSCodingプロトコルを使用します。

_@interface Person : NSObject <NSCoding>

    @property (copy, nonatomic) NSString *name
    @property (copy, nonatomic) NSString *lastName

@end
_

オブジェクトのシリアル化は、アプリのiOS 7バージョンで完全に機能します。

  1. initWithCoderおよびencodeWithCoder

  2. _[NSKeyedArchiver archivedDataWithRootObject:person]_

  3. person = NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)theData]

しかし、このアプリをiOS 8に移動する必要があります。このクラスは、Swiftでコーディングされ、この新しいiOS 8バージョンのアプリ用に「名前が変更」されます。

_class PersonOldVersion: NSObject, NSCoding {
    var name = ""
    var lastName = ""
}
_

オブジェクトをアーカイブ解除しようとすると、次のエラーが発生しました。

_*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Person)'
_

私はすでにSwift class 'PersonOldVersion'を元のクラス名( 'Person')に名前変更しましたが、それでも失敗します。

元のクラスが利用できないオブジェクトをどのようにデコードできますか?

39
Aнгел

Objective-Cのクラスの名前が重要な場合は、名前を明示的に指定する必要があります。それ以外の場合、Swiftは、いくつかの破損した名前を提供します。

@objc(Person)
class PersonOldVersion: NSObject, NSCoding {
    var name = ""
    var lastName = ""
}
27
newacct

これは別の解決策かもしれませんが、それは私がやったことです(そのときにSwiftを使用していなかったため))。

私の場合、「City」クラスのオブジェクトをアーカイブしましたが、完全に新しい「City」クラスを作成したため、クラスの名前を「CityLegacy」に変更しました。

古い「City」オブジェクトを「CityLegacy」オブジェクトとしてアーカイブ解除するには、これを行う必要がありました。

// Tell the NSKeyedUnarchiver that the class has been renamed
[NSKeyedUnarchiver setClass:[CityLegacy class] forClassName:@"City"];

// Unarchive the object as usual
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"city"];
CityLegacy *city = [NSKeyedUnarchiver unarchiveObjectWithData:data];
64
Ferran Maylinch

Swift上記のFerran Maylinchの回答の翻訳です。

製品の2つのビルドを作成するために元のターゲットを複製した後、Swiftプロジェクトで同様の問題が発生しました。2つのビルドを相互に使用できるようにする必要がありました。

だから、私はmyapp_light.appと私のapp_pro.appのようなものを持っていました。クラスを設定すると、この問題が修正されました。

NSKeyedUnarchiver.setClass(MyClass1.classForKeyedUnarchiver(), forClassName: "myapp_light.MyClass1")
NSKeyedUnarchiver.setClass(MyClass1.classForKeyedUnarchiver(), forClassName: "myapp_pro.MyClass1")



if let object:AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile("\path\...") {
    var myobject = object as! Dictionary<String,MyClass1>
    //-- other stuff here
}
15
nspire

私はあなたがそれを次のように解決できると思います:

@implementation PersonOldVersion

+ (void)load
{
    [NSKeyedUnarchiver setClass:[PersonOldVersion class] forClassName:@"Person"];
}
3
Shuangquan Wei

newacctの回答 はクラス名の助けとなりましたが、Objective-Cクラスから変数名をSwiftクラスで変更しました。@objc()もそれを修正しました:

古いクラス:

@interface JALProgressData : NSObject <NSCoding>

@property (nullable, nonatomic, strong) NSString *platformID;
@property (nonatomic, assign) NSTimeInterval secondsPlayed;
@property (nonatomic, assign) NSTimeInterval totalDuration;
@property (nullable, nonatomic, strong) NSDate *lastUpdated;

@end

新しいクラス:

@objc(JALProgressData)
class VideoProgress: NSObject, NSCoding {
    @objc(platformID) var videoId: String?
    var secondsPlayed: NSTimeInterval?
    var totalDuration: NSTimeInterval?
    var lastUpdated: NSDate?
}

また、値の型にencodeObjectdecodeObjectForKeyを使用していたため、Swiftクラスで問題が発生していました。これらの値に対してencodeDouble型をdecodeDoubleForKey型にNSTimeIntervalに修正するとaDecodernilを返すことに気付きました。

1
JAL