Expressing enum with associated values in Objective-C - objective-c

In Swift there are enums with associated values:
enum Coffee {
case Black
case BlackWithSugar(spoons: Int)
// ....
}
Clearly the goal here is to:
Have one case that expresses BlackWithSugar (not 100 cases, like BlackWith1Sugar, BlackWith2Sugar) and so on
But preserve additional info (number of spoons) inside the same structure
What's the closest / most elegant way to express the same in Objective-C?
Note: I saw this question, but 2 answers do not say how to express the same intent in Objective-C. I'm ok with using something other than enum, and using Swift is not an option.

While it is possible with a lot of work to accomplish something very similar to Swift's enums with associated types in Objective-C (see the blog post I linked in a comment above), it requires a bunch of hard-to-comprehend code.
I'd recommend taking a more natural Objective-C style approach. You'll lose out on some of the type safety Swift provides, but ObjC is naturally a less type-safe language.
For example:
// Create an options enum. Bridges into Swift as an OptionSet
typedef NS_OPTIONS(NSInteger, CoffeeOptions) {
CoffeeOptionsBlack = 0,
CoffeeOptionsWithSugar = 1 << 0,
CoffeeOptionsWithCream = 1 << 1
};
// Create a (non-extensible) string-typed "enum". Bridges into Swift as an enum-style struct with string "raw values"
typedef NSString *CoffeeQuantityKey NS_TYPED_EXTENSIBLE_ENUM;
static CoffeeQuantityKey const CoffeeQuantityKeySpoonsOfSugar = #"SpoonsOfSugar";
static CoffeeQuantityKey const CoffeeQuantityKeyTeaspoonsOfCream = #"TeaspoonsOfCream";
#interface CoffeeMachine : NSObject
- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities;
#end
#implementation CoffeeMachine
- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities
{
// Make coffee
if (options & CoffeeOptionsWithSugar) {
NSNumber *sugarAmount = quantities[CoffeeQuantityKeySpoonsOfSugar] ?: #1; // Default to 1 spoon
NSLog(#"Adding %# spoons of sugar", sugarAmount);
}
if (options & CoffeeOptionsWithCream) {
NSNumber *creamAmount = quantities[CoffeeQuantityKeyTeaspoonsOfCream] ?: #1; // Default to 1 teaspoon
NSLog(#"Adding %# teaspoons of cream", creamAmount);
}
}
#end
This also has the advantage of bridging relatively nicely into Swift:
let cm = CoffeeMachine()
cm.makeCoffee(options: [.withSugar, .withCream], quantities: [.spoonsOfSugar : 3])

Related

Keeping Swift Double precision in Objective C

A double created in Swift
let d: Double = 1.0
when passed to Objective C, will not preserve the .0. It ends up as 1.
Is there a way preserve .0 for whole doubles in Objective C?
Edit:
Here's what I'm doing:
Parent.m
#implementation Parent
-(void)log:(NSDictionary*)data {
}
#end
Parent.h
#interface Parent : NSObject
-(void)log:(NSDictionary*)data;
#end
Child.swift
class Child: Parent {
func log() {
let measure = Double(1)
let isLoggedIn = false
let data: [String: Any] = ["is_logged_in": isLoggedIn, "measure": measure]
log(data) // calling parent method, measure ends up as 1
}
}
From the wording of your question you might need to research the difference between a double value (the same in both languages) and a textual representation of a double value (which your question suggests might not default to the same in both languages).
Once you are clear on that look up the NSString method stringWithFormat and see if you can produce the format you require using that. (NSLog() supports the same formatting.)
If the above fails to meet your requirements look up NSNumberFormatter.
HTH

Varying Return Type Objective-C or c

How can I have a method/function that can return any type? For example sometimes the type will need to be float and sometimes it will need to be NSString* so id won't work because float isn't an id. I am not opposed to doing it in a c or c++ function if it's easier.
The reason why I need a dynamic return type is because I'm using objc/runtime to get an Ivar.
I would like some_type to be able to anything:
- (some_type)getIvarWithName:(const char *)name in:(id)obj
{
Ivar ivar(class_getInstanceVariable(object_getClass(obj),name));
return (some_type)ivar;
}
Return a float wrapped in an NSNumber, then you can use the id return type.
To simplify it, you can even use boxing literals, for example:
return #(1.1f);
The first thing to think about is why you would need a function that can return any type. It really doesn't make sense because you wouldn't be able to assign the value to anything, since you don't know the type. Of course, the situation is different when dealing strictly with Obj-C objects, as the language utilizes unknown objects with the id keyword. Unknown objects are like mixing Honeycrisp apples with Macintosh apples (no pun intended), and what you are trying to do is like mixing Honeycrisp apples with airplanes! However, if you want a certain type returned based off of the parameters (such as returning int for int parameters and float for float parameters), then you can overload the functions. Otherwise, then only way that I know of to return absolutely anything would be a void pointer (void *). This would point to a chunk of data that could really be anything. But back to the original problem. What does it represent and how long is it? Good luck!
UPDATE: As other answers mention, you can wrap simple data types (int, float, etc.) in objects such as NSNumbers or NSValues, which will work for your case. But when extending to more general scenarios with complex types such as structs, these generally can't be wrapped in built-in classes. You would need to make your own class using Obj-C.
There is no polymorphism of that kind in Obj-C.
If you know in advance what will be returned then you could use to methods of course.
Retruning id would work when you use an NSNumber for the float value.
You could even introduce a response object that either carries a number or a string and provides (bool) isNumber and (bool) isString methods for later processing.
But what are you really up to? In which context are you using that and what do you really try to achieve. To me it sounds as if there may be better solutions available.
Ofcourse it's weird solution, but you have weird question.
You need enable objective-c++: rename .m-file to .mm
Then yours code will look something like that:
void weird_function(int a)
{
switch (a)
{
case 0: throw #"Hello";
default: throw a;
}
}
void some_code(int a)
{
try
{
weird_function(a);
}
catch (int a)
{
NSLog(#"Catch int: %d", a);
}
catch (NSString* str)
{
NSLog(#"Catch string: %#", str);
}
}
Yours method can be implemented something like that:
union ValueHolder
{
void* voidPtrValue;
int intValue;
float floatValue;
NSString* nssstringValue;
};
- (void)getIvarWithName:(const char *)name in:(id)obj
{
ValueHolder vh;
Ivar ivar = object_getInstanceVariable(obj,name, &vh.voidPtrValue));
if (NULL == ivar)
return;
const char* encoding = ivar_getTypeEncoding(ivar);
if (0 == strcmp(encoding, #encode(int)))
throw vh.intValue;
if (0 == strcmp(encoding, #encode(float)))
throw vh.floatValue;
if (0 == strcmp(encoding, "#\"NSString\""))
throw vh.nsstringValue;
}
I found that using a template in c++ works to have a custom type
The following code works best for my situation:
template <typename _type>
static inline _type & hookVar(id self, NSString*name)
{
Ivar ivar(class_getInstanceVariable(object_getClass(self),[name UTF8String]));
#if __has_feature(objc_arc)
void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>((__bridge void *)self) + ivar_getOffset(ivar));
#else
void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>(self) + ivar_getOffset(ivar));
#endif
return *reinterpret_cast<_type *>(pointer);
}
To call the function I just use something like:
NSWindow *win = hookVar<NSWindow*>(self, #"_window");

Store an array of NSObject Pointers in C array

I'd like to create an NSObject subclass that contains a few member vars:
#interface PointMass : NSObject
{
CGPoint mCurPosition;
CGPoint mLastPosition;
CGPoint mAcceleration;
}
-(id) initWithPosition:(CGPoint*) pos;
#import "PointMass.h"
#implementation PointMass
-(id) initWithPosition:(CGPoint*)pos
{
mCurPosition = *pos;
mLastPosition = *pos;
mAcceleration = ccp(0,0);
return self;
}
#end
And I would like to create a C-style array to hold a bunch of them within a cocos2d class:
// mNumPoint declared in interface, I've set it to 100
PointMass *pointMassList;
pointMassList = malloc(sizeof(PointMass*) * mNumPointMass);
for (int = 0; i < mNumPointMass; i++)
{
CGPoint point = ccp(100,100);
PointMass *p = [[PointMass alloc] initWithPosition: &point];
pointMassList[i] = p;
}
But I get an error
Expected method to write array element not found on object of type 'PointMass *'
Do I need to tell the compiler more about my PointMass Object if I want to store pointers to it in a C array?
I'm basically trying to have a play around with some particle math on iPhone without needing to unpack points from an NSArray constantly if it isn't clear what I'm trying to achieve here.
If I've gone about this in a backwards way I'd love to be corrected - it has been a while since I wrote vanilla C and I'm a little rusty!
it has been a while since I wrote vanilla C
You should still be able to make the distinction between a pointer-to-T and a pointer-to-pointer-to-T (T being PointMass in this case). You want to store an array of PointMass *, and not an array of PointMass (which you couldn't do anyway). So change the declaration of pointMassList to
PointMass **pointMassList;
and it will work. However, if you're using Objective-C anyway, why don't you simply store the instances into an NSArray?

Enumerations in Objective-C

I'm making the jump from Java to Objective-C. I'm wondering if there is a concept analogous to Java's enums, that supports implementations of methods. I realize Objective-C has plain old C enumerations, but those are really just ints.
I'm looking to prevent switch-yards -- Java enums would be perfect.
Objective-C is just C with some additional markup for objects, no new types have been added.
That means, no.
For mutual exclusive flags, Apple uses strings.
header.h
extern NSString * const kNSSomeFlag;
extern NSString * const kNSOtherFlag;
extern NSString * const kNSThirdFlag ;
code.m
NSString * const kNSSomeFlag = #"kNSSomeFlag";
NSString * const kNSOtherFlag = #"kNSOtherFlag";
NSString * const kNSThirdFlag = #"kNSThirdFlag";
…
void myFunction(NSString *flag)
{
if (flag == kNSSomeFlag) {
// the code
}
}
An example of this can be found in the NSDistributedNotificationCenter.

What is the best way to define string constants in an objective-c protocol?

I have defined a protocol that all my plug-ins must implement. I would also like the plug-ins to all use certain strings, like MyPluginErrorDomain. With integers this is quite easily achieved in an enum, but I can't figure out how to do the same with strings. Normally, in classes I would define
extern NSString * const MyPluginErrorDomain;
in the .h file and in the .m file:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
but that doesn't work very well in a protocol, because then each plug-in would have to provide its own implementation which defeats the purpose of having a constant.
I then tried
#define MYPLUGIN_ERROR_DOMAIN #"MyPluginErrorDomain"
but the implementing classes in the plug-in can't seem to see the #define. Who knows a good solution?
You can declare them in the header with the protocol (but outside the protocol interface itself), then define them in an implementation file for the protocol (obviously it wouldn't have an #implementation section - just your NSString definitions).
Or have a separate .h/.m pair that is just for the string constants (the protocol header can import the string constants header).
You keep the .h definition:
extern NSString * const MyPluginErrorDomain;
but put this part into a separate .m file that gets included in your framework:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
So plug-ins can still implement the interface but when compiling they link or compile in your other .m file, so they will see the value of MyPluginErrorDomain.
In C++, I would declare them in a header like this:
const char * const MYPLUGIN_ERROR_DOMAIN = "MyPluginErrorDomain";
const char * const MYPLUGIN_FOO_DOMAIN = "MyPluginFooDomain";
Note that as the pointers are const, they will be local to the translation units the header is #included in, and so there will be no need to use extern to prevent multiple definition errors.
You should implement it as extern strings as in your example:
extern NSString * const MyPluginErrorDomain;
or provide extern functions which return static storage data. For example:
/* h */
extern NSString * MyPluginErrorDomain();
/* m */
NSString * MyPluginErrorDomain() {
static NSString * const s = #"MyPluginErrorDomain";
return s;
}
The reason is that strings and keys are often used and compared by pointer value or hash value, rather than true string comparison (isEqualToString:).
At the implementation level, there is a big difference between:
In code, that means that when the strings compared are defined in multiple binaries:
Say 'MyPluginErrorDomain' and 'key' have identical string values, but are defined in different binaries (i.e. on in the plugin host, one in the plugin).
/////// Pointer comparison (NSString)
BOOL a = [MyPluginErrorDomain isEqualToString:key];
BOOL b = MyPluginErrorDomain == key;
// c may be false because a may be true, in that they represent the same character sequence, but do not point to the same object
BOOL c = a == b;
/////// Hash use (NSString)
// This is true
BOOL d = [MyPluginErrorDomain hash] == [key hash];
// This is indicative if true
BOOL e = [MyPluginErrorDomain hash] == [someOtherStringKey hash];
// because
BOOL f = [MyPluginErrorDomain isEqualToString:someOtherStringKey];
// g may be false (though the hash code is 'generally' correct)
BOOL g = e == f;
It is therefore necessary to provide the keys in many cases. It may seem like a trivial point, but it is hard to diagnose some of the problems associated with the difference.
Hash codes and pointer comparisons are used throughout Foundation and other objc technologies in the internals of dictionary storage, key value coding... If your dictionary is going straight out to xml, that's one thing, but runtime use is another and there are a few caveats in the implementation and runtime details.