Search code examples
objective-cuniqueidentifieridempotent

Return an unique identifier in a idempotent way


I'm creating a third party lib and I need to find a way to create an unique identifier in a idempotent way. I'll try to explain.

Suppose you a UIViewController like MyViewController. The library exposes two different APIs to register/deregister a component.

When I do a register of the view controller, I need to create an unique identifier for the controller that should be valid until it will be deallocated. If I create another instance of this controller, the identifier should be different.

In other words, suppose I have

MyViewController* a = // alloc-init

Now within a, I register itself like so

// I'm within viewDidLoad for example
[[LibraryManager sharedManager] registerEntry:self];

At this point, my desiderata is the following: Create an unique identifier (within my library) based on the instance I passed in.

If a wants to deregister, it can do simply as

// I'm within dealloc for example
[[LibraryManager sharedManager] deregisterEntry:self];

Based on the instance passed in (self), I need to retrieve (in some way) the identifier previously created in order to clear stuff created with the register method.

If I create another controller, say b, the identifier should be different form the one created with a.

I would not to rely on a delegate pattern in order to ask the controller (in this case) to provide an unique identifier by means of [[NSUUID UUID] UUIDString]; or similar.

Is there a way to create an unique identifier based on the instance passed in? I'm thinking about storing a string representation of the memory address or similar but It's a kind of hack, IMHO.


Solution

  • It's not entirely clear what you want to do, but you may be able to use associated references. For example, when you want the unique identifier for an object, you would attempt to get it using:

    NSUUID* uuid = objc_getAssociatedObject(theObject, myKey);
    

    If the object has no unique ID assigned, yet, that will return nil. In that case, you could generate the unique ID and set it for the object:

    if (!uuid)
    {
        uuid = [NSUUID UUID];
        objc_setAssociatedObject(theObject, myKey, uuid, OBJC_ASSOCIATION_RETAIN);
    }
    

    The key myKey is just something which is unique for your library. One common strategy (used for the context for KVO, for example) is to create a static variable and use its address:

    static const int foo;
    static const void* const myKey = &foo;
    

    You can combine these to make the key hold its own address:

    static void* const myKey = (void*)&myKey;
    

    Anyway, the associated object will be released when the object it is associated with is deallocated (or if a different associated object is set on it, although that won't be done in your case).

    Unfortunately, this approach is not thread-safe since two threads could both try to look up the unique ID, find that none has been assigned yet, generate a new one, and assign it. The last assignment will win, but in the meantime, the other could have returned an ID that was ultimately replaced. In effect, the object would have had two different IDs.

    So, if there's any chance that two threads will attempt to query the ID at the same time, you need to provide synchronization within your method. For example, create a serial GCD queue and dispatch_sync() a block containing the code described above to that queue.