11

I am using NSKeyedUnarchiver to unarchive an object and would like to use the delegates (NSKeyedUnarchiverDelegate), but my delegates are not called. Archiving and Unarchiving is working fine, but the Delegates (unarchiver & unarchiverDidFinish) are not called. Can someone help?

I have the following implementation:

        class BlobHandler: NSObject , NSKeyedUnarchiverDelegate{

           func load() -> MYOBJECTCLASS{          
              let data:NSData? = getBlob();      
              var mykeyedunarchiver:NSKeyedUnarchiver=NSKeyedUnarchiver(forReadingWithData: data!);
              mykeyedunarchiver.delegate = self;
              let temp=mykeyedunarchiver.decodeObjectForKey("rootobject")
// No delegates are called
                            if temp==nil {
                                blobsexists=false;
                            }else{
                                objectreturn = temp! as! MYOBJECTCLASS;
                                return objectreturn;
                            }
        }

    func save1(myobject:MYOBJECTCLASS){
            let data = NSMutableData()
            var keyedarchiver:NSKeyedArchiver=NSKeyedArchiver(forWritingWithMutableData: data);
            keyedarchiver.encodeObject(maptheme, forKey: "rootobject");

            let bytes = data.bytes;
            let len=data.length; 
            saveblob(bytes);
    }

The following delegates, which are also implemented in my Blobhandler, are never called:

func unarchiver(unarchiver: NSKeyedUnarchiver, cannotDecodeObjectOfClassName name: String, originalClasses classNames: [String]) -> AnyClass? {
    print("I am in unarchiver !");
    return nil;
}

func unarchiverDidFinish(_ unarchiver: NSKeyedUnarchiver){
    print("I am in unarchiverDidFinish ! ");
}
Alexander Telegin
  • 665
  • 1
  • 7
  • 22
mcfly soft
  • 11,289
  • 26
  • 98
  • 202
  • 1
    Try calling mykeyedunarchiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) – Shripada Jan 16 '16 at 09:55
  • Thanks for helping. I tried. For this I need to encode the object with a rootkey, I guess ? But I am to stupid to code this. I updated my question with my implementation attept. Could you please give me a sample howto encode an object with a root key. I did not find any sample out there. – mcfly soft Jan 17 '16 at 09:35
  • are you sure your delegate is still alive at the time method is supposed to be called? can you please post all the methods you're using like `getBlob()` and `saveblob`. – Max Komarychev Jan 24 '16 at 21:43

3 Answers3

1

I don't know what it was, but its working after a clean and rebuild of the project. I notice with different cases, that the builds are not in sync sometimes. There is sometimes code, which is in XCode but it is not executed. Sounds unbelievable, but I guess its true. XCode 7.2

mcfly soft
  • 11,289
  • 26
  • 98
  • 202
0

I think the first function is never called since you didn't actually feed a "cannotDecodeObjectOfClassName" at all, since you only did try to unarchive previously archived data. You can try this method(or something requires a class name) to validate your solution(feed a class doesn't conform NSCoding):

unarchiver.decodeObjectOfClass(cls: NSCoding.Protocol, forKey: String)

The second one is a little bit tricky. I've tried this method in a similar situation and it turned out that unarchiverDidFinish only get called when a complete unarchiving job is done and probably before it's destroyed. For example, I had a NSCoding class and the convenience initiator is like

required convenience init?(coder aDecoder: NSCoder) {
    let unarchiver = aDecoder as! NSKeyedUnarchiver
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    unarchiver.delegate = appDelegate.uad


    let name = unarchiver.decodeObjectForKey(PropertyKey.nameKey) as! String

    print(321)

    self.init(name: name, photo: photo, rating: rating)
}

uad is an instance of class:

class UAD:NSObject, NSKeyedUnarchiverDelegate {
    func unarchiverDidFinish(unarchiver: NSKeyedUnarchiver) {
        print(123)
    }
}

And in the view controller the loading process is like

func load() -> [User]? {
    print(1)
    let ret = NSKeyedUnarchiver.unarchiveObjectWithFile(ArchiveURL.path!) as? [User]
    print(2)
    return ret
}

And the output is like:

1 321 321 321 321 321 123 2

After finishing loading a group of users, the unarchiverDidFinish finally got called once. Notice that this is a class function and an anonymous instance is created to finish this sentence:

NSKeyedUnarchiver.unarchiveObjectWithFile(ArchiveURL.path!) as? [User]

So I really believe that this function only get called before it is destroyed or a group of call back functions is finished.

I am not quite sure if this is the case for you. You may try to make your unarchiver object global and destroy it after your loading is done to see whether this function is called.

Correct me if anything not right.

alvinzoo
  • 493
  • 2
  • 8
  • 17
  • Dear alvinzoo. Thanks a lot for helping and your effort. I don't know what to do now regarding the +50 reputation, because in the end it was an XCode problem, I would say. The compiled code was not in sync with what I had in textform in XCode (not 100% sure, but I observed such problems already in the past, that when debugging it didn't reach some code, till I rebuild the project). After rebuilding the project (which is big), delegates are called. I didn't change anything in my code and the object always was filled with data. – mcfly soft Jan 25 '16 at 09:31
  • @mcflysoft no problem at all. Good to know this is solved. Maybe we are talking about different scenarios. – alvinzoo Jan 26 '16 at 08:37
0

To make either unarchiverWillFinish: and unarchiverDidFinish: be called properly, we have to invoke finishDecoding when finished decoding.

Once you have the configured decoder object, to decode an object or data item, use the decodeObjectForKey: method. When finished decoding a keyed archive, you should invoke finishDecoding before releasing the unarchiver.

We notify the delegate of the instance of NSKeyedUnarchiver and perform any final operations on the archive through invoking this method. And once this method is invoked, according to Apple's official documentation, our unarchiver cannot decode any further values. We would get following message if we continue to perform any decoding operation after invoked finishDecoding:

*** -[NSKeyedUnarchiver decodeObjectForKey:]: unarchive already finished, cannot decode anything more

It also makes sense for encoding counterparts.

kwokt
  • 65
  • 3