Store selector as value in an NSDictionary

CocoaCocoa TouchNsdictionarySelector

Cocoa Problem Overview


Is there a way to store a selector in an NSDictionary, without storing it as an NSString?

Cocoa Solutions


Solution 1 - Cocoa

SEL is just a pointer, which you could store in an NSValue:

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 
                       [NSValue valueWithPointer:@selector(foo)], @"foo",
                       nil];

To get the selector back, you can use:

SEL aSel = [[dict objectForKey:@"foo"] pointerValue];

Solution 2 - Cocoa

An alternative to Georg's solution would be to convert the selector into an NSString before storing it the NSDictionary:

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 
                      NSStringFromSelector(@selector(foo)), @"foo",
                      nil];

SEL selector = NSSelectorFromString([dict objectForKey:@"foo"]);

This technique, though uses more memory, gives you the ability to serialize the entire NSDictionary as a string via libraries like JSONKit.

Solution 3 - Cocoa

An NSDictionary is really just a CFDictionary that retains and releases all keys and values. If you create a CFDictionary directly, you can set it up to not retain and release values. You can typecast a CFDictionaryRef to an NSDictionary * and vice versa.

Solution 4 - Cocoa

In case of using UILocalNotification the only way is to use NSSelectorFromString([dict objectForKey:@"foo"]). With valueWithPointer the app crashing when setting userInfo property of UILocalNotification object. Be careful.

Solution 5 - Cocoa

While Georg's answer should work, NSValue does also support encoding any value using an Objective-C type encoding string, which has a special way of representing SEL— with a ":" (as opposed to the "^v" produced by -valueWithPointer:, which translates into void *).
source: Objective-C Runtime Programming Guide - Type Encodings

Working off of Georg's solution, the best API-compliant way to put a SEL into an NSValue into an NSDictionary would be:

// store
NSDictionary *dict = @{
	@"foo": [NSValue value:&@selector(foo) withObjCType:@encode(SEL)]
};

// retrieve
SEL aSel;
[dict[@"foo"] getValue:&aSel];



The rationale for handling a SEL as its own beast is that the docs describe it as “an opaque type”— which means that its internal workings (even what it's typedefd to) are off-limits to app programmers; Apple may mix it up at any time in the future.

Also, using void *s to force the system to do what you want it to do was useful in C back in the '90s, when most of us didn't know any better.  You're better than that now.

The above approach should only be used if the retrieval of the SEL happens during the program's running duration— you shouldn't be storing that NSDictionary to disk.  If you do need to store SELs long-term (across app launches), you should follow David H's approach and convert it to an NSString.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsynicView Question on Stackoverflow
Solution 1 - CocoaGeorg FritzscheView Answer on Stackoverflow
Solution 2 - CocoaDavid HView Answer on Stackoverflow
Solution 3 - CocoadrawnonwardView Answer on Stackoverflow
Solution 4 - CocoakostasView Answer on Stackoverflow
Solution 5 - CocoaSlipp D. ThompsonView Answer on Stackoverflow