Iphone -- maintaining a list of strings and a corresponding typedef enum - iphone

Suppose I have the following:
typedef enum functionType {ln, sin, sqrt} functionType;
NSArray *functions = [NSArray arrayWithObjects: #"ln", #"sin", #"sqrt", nil];
Suppose further that *functions will not change at runtime.
Question -- is there any way to set up a single structure which updates both of these? So that I only have to keep track of one list, instead of two.
To explain what is going on -- the idea is that string input from the user will be stored in a variable of type functionType. Later on, I will have code like this:
double valueOfFunction: (functionType) function withInput: (double) input
switch (function) {
case ln:
return ln(input);
case sin:
return sin(input);
case sqrt:
return sqrt(input);
//etc . . . could grow to include a lot of functions.
}
And valueOfFunction needs to be fast. So I don't want to be doing string comparisons there.

It sounds like you want a map from strings to enum objects. There are a number of ways to do this.
You could use an NSDictionary with NSString keys and NSNumber-encoded ints representing the objects.
You could use an NSArray of the function names (#"ln", #"sin", etc), and only store the index into the array; this basically gets rid of the enum.
If you really want a joined list of enum types and string objects, you could also do something like this:
typedef enum FunctionType {
ln, sin, cos, tan, exp
} FunctionType;
typedef struct FunctionItem {
FunctionType type;
NSString *name;
} FunctionItem;
FunctionItem functions[] = {
{ln, #"ln"},
{sin, #"sin"},
{cos, #"cos"},
{tan, #"tan"},
{exp, #"exp"},
};
Watch out for symbol clashes, though! You can't have an enum identifier called sin and also use the standard sin() function.
Good luck with your calculator-type app!

Related

Converting Pascal's variant record to Swift

I am converting a program written in Pascal to Swift and some Pascal features do not have direct Swift equivalents such as variant records and defining sets as types. A variant record in Pascal enables you to assign different field types to the same area of memory in a record. In other words, one particular location in a record could be either of type A or of type B. This can be useful in either/or cases, where a record can have either one field or the other field, but not both. What are the Swift equivalents for a variant record and a set type like setty in the Pascal fragment?
The Pascal code fragment to be converted is:
const
strglgth = 16;
sethigh = 47;
setlow = 0;
type
setty = set of setlow..sethigh;
cstclass = (reel,pset,strg);
csp = ^constant; /* pointer to constant type */
constant = record case cclass: cstclass of
reel: (rval: packed array [1..strglgth] of char);
pset: (pval: setty);
strg: (slgth: 0..strglgth;
sval: packed array [1..strglgth] of char)
end;
var
lvp: csp
My partial Swift code is
let strglgth = 16
let sethigh = 47
let setlow = 0
enum cstclass : Int {case reel = 0, pset, strg}
var lvp: csp
Any advice is appreciated. Thanks in advance.
Variant records in Pascal are very simular to unions in C.
So this link will probably be helpful:
https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift
In case the link ever goes dead, here's the relevant example:
union SchroedingersCat {
bool isAlive;
bool isDead;
};
In Swift, it’s imported like this:
struct SchroedingersCat {
var isAlive: Bool { get set }
var isDead: Bool { get set }
init(isAlive: Bool)
init(isDead: Bool)
init()
}
That would be more like a functional port. It does not seem to take care of the fact that Variant records are actually meant to use the same piece of memory in different ways, so if you'd have some low-level code that reads from a stream or you have a pointer to such a structure, this might not help you.
In that case you might want to try just reserving some bytes, and write different getters/setters to access them. That would even work if you'd have to port more complex structures, like nested variant types.
But overall, if possible, I'd recommend to avoid porting such structures too literally, and use idioms that match Swift better.

What's the best way to declare a list of scalar values in Swift

I was wondering: What is the best way to declare a non-ordered list of scalar values in Swift, that are fully backed by a symbol and are liable to be checked by the compiler?
Let's say I want to declare a list of LetterSpacing values that I want to access and use in my code via their symbols.
My understanding is that I should declare an swift enum like this:
enum LetterSpacing: Double {
case Short = 1.0
case Medium = 2.0
case Large = 3.0
}
This works fine and gets compiler checks but the annoying part is that I need to LetterSpacing.XXX.rawValue each time to access the underlying value. The .rawValue bothers me a bit to have to specify this everywhere.
I suppose it wouldn't make sense or would be inappropriate to declare a struct with three static let Short = 1.0 etc..
So, how would you handle such cases? Is it possible to add somekind of protocol/extension swift magic to the existing types to be able to have the Short enum act as its own value? (i.e. let size = LetterSpacing.Short would be of type Double and have a value of 1.0)
The enum would be preferred for multiple reasons.
First, the enum actually ends up with a smaller memory footprint. This is because Swift optimizing enum values into a single byte (no matter what the raw value is), and we only get the full value out of it when we call rawValue on the enum value. Paste the following code into a playground to see:
struct LetterSpacingS {
static let Short: Double = 1.0
}
enum LetterSpacingE: Double {
case Short = 1.0
}
sizeofValue(LetterSpacingS.Short)
sizeofValue(LetterSpacingE.Short)
The double is 8 bytes, while the enum is just 1.
Second, the importance of the first point extends beyond just memory footprint. It can apply to the execution efficiency of our program as well. If we want to compare two values from our struct, we're comparing 8 bytes. If we want to compare two of our enum values, we comparing just a single byte. We have 1/8th as many bytes to compare in this specific case. And this is just with a double. Consider that Swift enums can also have a string as a backing type. Comparing two strings can be extraordinarily expensive versus just comparing the single byte they're hidden behind in an enum.
Third, using the enum, we're not comparing floating point numbers, which you really don't want to do, generally speaking.
Fourth, using the enum gives us some type safety that we can't get with the struct of constants. All the struct of constants does is let us define a handful of predefined constants to use.
struct LetterSpacingS {
static let Short = 1.0
static let Medium = 2.0
static let Long = 3.0
}
But now what would a method look like that expects one of these values?
func setLetterSpacing(spacing: Double) {
// do stuff
}
And now what happens when Joe-Coder comes in and does:
foo.setLetterSpacing(-27.861)
Nothing's stopping him. It takes a double, any double. This might not be so common, but if this is in a library you're distributing, you are leaving yourself open to questions and comments like "When I pass a value of 5.0 in, it doesn't look right at all!"
But compare this to using the enum.
func setLetterSpacing(spacing: LetterSpacing) {
// do stuff
}
And now, we only get the three predefined choices for what to pass into this argument.
And outside of your internal use for the values held by the enum, you should have to use rawValue too much.
The fifth reason isn't a strong reason for using an enum over a struct, but mostly just a point to make to eliminate one of the reasons suggested for using a struct over an enum. Swift enums can be nested in other types, and other types can be nested in Swift enums. You don't have to use a struct to get nested types.
I don't see why creating a Struct with three static constants would be inappropriate. I see a lot of Swift code (also from Apple) that does this.
Structs lets you create nice hierarchies:
struct Style {
struct Colors {
static let backgroundColor = UIColor.blackColor()
...
}
struct LetterSpacing {
static let Short = 1.0
}
...
}

NSDictionary with key => String and Value => c-Style array

I need an NSDictionary which has key in the form of string (#"key1", #"key2") and value in the form of a C-style two-dimensional array (valueArray1,valueArray2) where valueArray1 is defined as :
int valueArray1[8][3] = { {25,10,65},{50,30,75},{60,45,80},{75,60,10},
{10,70,80},{90,30,80},{20,15,90},{20,20,15} };
And same for valueArray2.
My aim is given an NSString i need to fetch the corresponding two-dimensional array.
I guess using an NSArray, instead of c-style array, will work but then i cannot initialize the arrays as done above (i have many such arrays). If, however, that is doable please let me know how.
Currently the following is giving a warning "Passing argument 1 of 'dictionaryWithObjectsAndKeys:' from incompatible pointer type" :
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:valueArray1,#"key1",
valueArray2,#"key2",nil];
Is valueArray2 also an int[][3]? If so, you could use
[NSValue valueWithPointer:valueArray1]
to convert the array into an ObjC value. To retrieve the content, you need to use
void* valuePtr = [[myDict objectForKey:#"key1"] pointerValue];
int(*valueArr)[3] = valuePtr;
// use valueArr as valueArrayX.
If there's just 2 keys, it is more efficient to use a function like
int(*getMyValueArr(NSString* key))[3] {
if ([key isEqualToString:#"key1"]) return valueArray1;
else return valueArray2;
}
Rather than Adding Array Directly as a value in NSDictionary make a custom class in which create variable of NSArray ... and set this class object as value like
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:MyClassObj1,#"key1",
MyClassObj2,#"key2",nil];
where MyClassObj1 and MyClassObj2 are member of MyClass

What kind of data is in an "enum" type constant? How to add it to an NSArray?

What kind of information is stored behind such an enum type thing? Example:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
I am not sure if I can safely add such an enum constant to an array. Any idea?
Enums in Objective-C are exactly the same as those in C. Each item in your enum is automatically given an integer value, by default starting with zero.
For the example you provided: UIViewAnimationCurveEaseInOut would be 0; UIViewAnimationCurveEaseIn would be 1, and so on.
You can specify the value for the enum if required:
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn = 0,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
This result of this would be: UIViewAnimationCurveEaseInOut is 0; UIViewAnimationCurveEaseIn is 0; UIViewAnimationCurveEaseOut is 1; and so on. However, for basic purposes you shouldn't need to do anything like that; it just gives you some useful info to toy with.
It should be noted based on the above, that an enum can't assume to be a unique value; different enum identifiers can be equal in value to each other.
Adding an enum item to a NSArray is as simple as adding an integer. The only difference would be that you use the enum identifer instead.
[myArray addObject:[NSNumber numberWithInt:UIViewAnimationCurveEaseInOut]];
You can check this out for yourself by simply outputting each enum to the console and checking the value it provides you with. This gives you the opportunity to investigate the details of how it operates. But for the most part you won't really need to know on a day to day basis.
Enums are typically int values. You can store them in an array by wrapping them in an NSNumber:
[myMutableArray addObject:[NSNumber numberWithInt:myAnimationCurve]];
... then get them back out like this:
UIViewAnimationCurve myAnimationCurve = [[myMutableArray lastObject] intValue];
Enums in Objective-C are the same as enums in vanilla C. It's just an int. If you're using an NSArray, then it expects a pointer and you'll get a warning if you try to add an int to it:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:UIViewAnimationCurveEaseInOut];
// Last line results in:
// warning: passing argument 1 of 'addObject:' makes
// pointer from integer without a cast
If you're storing a large collection of 32-bit integers, consider using the appropriate CF collection type rather than the NS collection type. These allow you to pass in custom retain methods, which gets rid of the need to box every integer added to the collection.
For example, let's say you want a straight array of 32-bit ints. Use:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
The last parameter tells the array to not retain/release the "addresses" you pass in to it. So when you do something like this:
CFArrayAppendValue(arrayRef, 1);
What the array thinks is that you're passing in a pointer to an object living at the memory address 0x1. But since you told it to not call retain/release on that pointer, it gets treated as a standard int by the collection.
FWIW, for educational value, standard NSMutableArrays have equivalent CF types. Through toll-free bridging you can use the CF collection as a standard Foundation collection:
CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, kCFTypeArrayCallbacks);
NSMutableArray *array = (NSMutableArray *)arrayRef;
[array addObject:#"hi there!"];
NSLog(#"%#", [array objectAtIndex:0]); // prints "hi there!"
You can apply the same tricks to dictionaries (with CFDictionary/CFMutableDictionary), sets (CFSet/CFMutableSet), etc.

Obj-C: Difference between "Fairfield" and #"Fairfield" (with at string)?

I just had a ridonkulous typo in my iPhone app, answered here.
Now I'm wondering about the #"..." notation.
why this works:
NSArray *someArray = [NSArray arrayWithObjects: #"Fairfield", nil];
and this does not (even though it compiles, it will throw an EXC_BAD_ACCESS):
NSArray *someArray = [NSArray arrayWithObjects: "#Fairfield", nil];
Edit:
Ok, so you guys have pointed out that I can't add a C string to an NSArray, because it's obviously not an object.
Now another question: Isn't this somewhat of an oversight? I mean, why does the "...WithObjects:" message specify a list of (id) instead of (NSObject *)?
"#Fairfield" is a normal C string with an '#' character in it. #"Fairfield" is an Objective-C string (NSString on OS X) with no literal '#' in it.
You cannot add C strings to Cocoa collections.
It accepts id rather than NSObject because all initialisers return id. All initialisers return id because subclasses would otherwise override the return type of their ancestors' initialisers.
For example, -[NSMutableString init] can't return NSMutableString * because it subclasses -[NSString init], which can't return NSString * because it overrides -[NSObject init].
Unfortunately, implicit type-casting between const char * and id is perfectly legit, so the compiler won't throw a warning, however a static analyser may be able to pick this sort of mishap up fairly easily.
"Fairfield" is a C string, #"Fairfield" is an Objective-C string.
#"Fairfield" is an object (NSString), so you can send it methods ([#"Fairfield" uppercaseString]) and add it to Objective-C arrays ([NSArray arrayWithObjects:#"Fairfield",nil]). You can only add objects to NSArrays.
On the other hand, "Fairfield" is a C string, and is generally not used in Cocoa. For the most part, you can get by with only using #"Fairfield"
The other reason that a number of things in Cocoa deal with id rather than NSObject* is because, unlike some other languages (say, Java and C#), where all objects in the language must inherit from some global base class, it's entirely possible to have objects that do not descend from NSObject (NSProxy being one example). It's not something you'd do often, but it is possible. The id type means "pointer to any Objective C instance".