I'm a beginner obj-c programmer. I want to store enums in an NSMutableArray, then retrieve them.
I do this by first converting the enum to NSNumber object then storing that object into the array, and then retrieving it and converting it back to an integer. I have code that does it without arrays, and works, but I need to use an array.
Code:
//What I am trying to do:
NSNumber *n = [NSNumber numberWithInt:west];
[DirectionList addObject:n];
Direction intendedDirection = [[DirectionList objectAtIndex:0] intValue];
if (intendedDirection == west)
{
exit(-1); // DOES NOT EXIT, BUT SHOULD
}
//This code works, though
NSNumber *n = [NSNumber numberWithInt:west];
Direction intendedDirection = [n intValue];
if (intendedDirection == west)
{
exit(-1); // EXITS AS EXPECTED
}
Any ideas why the 1st one doesn't work?
Thanks.
If you don't need to mutate the array, I suggest you this method which is much, much cleaner than the one you're trying to follow.
I'm using NSString for this example but you can use any object you want
typedef NS_ENUM(NSUInteger, Direction) {
North,
South,
West,
East
};
NSString *const directionDescriptions[] = {
[West] = #"West",
[East] = #"East",
[North] = #"North",
[South] = #"South",
}
Then you can access your description using the enum as the index of this array.
// E.g.
NSSLog(#"%#", directionDescriptions[West]); // #"West"
Related
I have in my objective-c application a number of constants that I need to have inputted from an external source using strings. The reason of course, is that constants are better to work with, but can't be passed external.
I have made this objective-c code to convert, and it works 100%, but a) it is ugly, and b) quite obscure. I suppose I could have converted to NSNumber and made an array, but that seems like a lot of code/processing (though maybe the right solution)
Can anyone provide a better solution?
NSArray *types = #[#"text_input",#"textbox",#"select",#"yesno",#"date",#"signature",#"label",#"SectionHeading"];
int indexes[10];
indexes[0] = FieldTypeTextInput;
indexes[1] = FieldTypeTextBox;
indexes[2] = FieldTypeSelect;
indexes[3] = FieldTypeYesNo;
indexes[4] = FieldTypeDate;
indexes[5] = FieldTypeSignature;
indexes[6] = FieldTypeLabel;
indexes[7] = FieldTypeSectionHeading;
for (int i=0;i<[types count];i++)
{
NSString *string_i = [types objectAtIndex:i];
if ([type_string isEqualToString:string_i])
I suggest to use an NSDictionary.
enum YourNiceTypes : NSInteger {FieldNotFound, FieldTypeTextInput, FieldTypeTextBox, ...};
NSDictionary *types = #{"text_input" : #(FieldTypeTextInput), ... };
enum YourNiceType type = [types[textInput] integerValue];
You used the trick to define wrong input with zero, which will be handled automatically correctly, as calling integerValue on a nil object will return 0.
lately I work much with arrays and I'm wonder.. what's diffrences between those two lines.
NSArray *array = [NSArray arrayWithArray:someArray];
and
NSArray *array = [someArray copy];
Which of it is faster? What in case we have NSMutableArray and mutableCopy?
Which of it is faster?
Don't worry about it. Premature optimization.
The main difference: the first approach results in an autoreleased "copy" that you don't own and don't have to release, while you do own the object created on the second line.
Both arrays will be immutable, by the way.
In addition to the other answers, also note, that when someArray is nil,
the first line will make array point to an empty array and the second will make
it point to nil. This might be an important difference, especially in mutable arrays.
The difference between the two is that the latter will be retained. The former will be autoreleased.
Both versions make a shallow copy of the array.
NSMutableArray *notMutableReally = [NSArray arrayWithArray:aMutableArray];
Should give you a compiler warning as you will be trying to assign a NSArray to a NSMutableArray.
Use.
NSMutableArray *mutableArrayCopy = [NSMutableArray arrayWithArray:aMutableArray];
Which is faster? Dont worry, they are all far faster than the rest of the stuff you will be doing. Check with Instruments if you really care.
The main difference is that -copy knows better how to copy itself (can do it more efficiently and maybe use a more adapted subclass of NSArray) while +arrayWithArray: will create a new instance of NSArray (well, in fact the concrete class used by Foundation for arrays) and feed it with the same list of objects from the initial object. Also it will add an extra autorelease.
So -copy is (very very) likely more efficient.
In fact for immutable NSArrays, -copy is just doing -retain, so it does not even bother creating a new instance.
NSMutableArray *arr = [NSMutableArray array];
for ( int i = 0; i < 10000; i ++)
{
[arr addObject:#(i*1000000ULL)];
}
// MARK
// arr = (id)[NSArray arrayWithArray:arr];
NSTimeInterval t = [NSDate timeIntervalSinceReferenceDate];
NSArray *res = nil;
for ( int i = 0; i < 10000; i ++)
{
res = [arr copy];
}
NSLog(#"time A: %f", [NSDate timeIntervalSinceReferenceDate] - t);
t = [NSDate timeIntervalSinceReferenceDate];
for ( int i = 0; i < 10000; i ++)
{
res = [NSArray arrayWithArray:arr];
}
NSLog(#"time B: %f", [NSDate timeIntervalSinceReferenceDate] - t);
time A: 1.572795, time B: 1.539150, B [NSArray arrayWithArray:] always faster but time difference very small. But if we uncomment "MARK" and get copy from NSArray instead NSMutableArray we will have other runtime A: 0.000473 time B: 1.548400 result: ~3200x times faster
One of them is probably faster. Run them a million times and see if anyone wins.
In case of NSArray vs NSMutableArray, an immutable array being copied does not have to actually return a copy since it can't change. However, if you have a mutable array, it would need to be copied since you could change the original. And of course doing a mutable copy always needs to return a new object.
In your entire app, the speed and memory difference is probably not going to matter compared to everything else that's going on.
In Swift, it's very different. Thanks to the new open-source Foundation for Swift, we know that whereas init(array:) creates a new array with the items given (if any), copy() simply returns self.
public override func copy() -> AnyObject {
return copyWithZone(nil)
}
public func copyWithZone(zone: NSZone) -> AnyObject {
return self
}
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSArray.swift#L82
public convenience init(array: [AnyObject]) {
self.init(array: array, copyItems: false)
}
public convenience init(array: [AnyObject], copyItems: Bool) {
let optionalArray : [AnyObject?] =
copyItems ?
array.map { return Optional<AnyObject>(($0 as! NSObject).copy()) } :
array.map { return Optional<AnyObject>($0) }
// This would have been nice, but "initializer delegation cannot be nested in another expression"
// optionalArray.withUnsafeBufferPointer { ptr in
// self.init(objects: ptr.baseAddress, count: array.count)
// }
let cnt = array.count
let buffer = UnsafeMutablePointer<AnyObject?>.alloc(cnt)
buffer.initializeFrom(optionalArray)
self.init(objects: buffer, count: cnt)
buffer.destroy(cnt)
buffer.dealloc(cnt)
}
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSArray.swift#L116
So, obviously, copy() is faster, and now you know how they both work! (Just only in Swift)
I'm not familiar with C. How can I pass a C array to a Objective-C function ?
I actually need an example of a class function converting NSArray to C arrays.
This is what I have so far:
+ (NSArray *)convertArray:(NSString*)array { //I don't think this is correct: the argument is just a NSString parameter and not an array
NSMutableArray * targetArray = [NSMutableArray array];
for (i = 0; i < SIZE; i++) //SIZE: I dunno how to get the size of a C array.
{
[targetArray addObject: [NSString stringWithString:array[i]];
}
return targetArray;
}
There are a few ways.
If your array size is fixed at compile-time, you can use the C99 static modifier:
-(void) doSomething:(NSString *[static 10]) arg
{
}
If not, you have to pass it as two separate arguments. One as a pointer to the first element of it, and the second as the length of it:
-(void) doSomething:(NSString **) arg count:(size_t) count
{
}
Now you can access your variables like any other array you may have.
Because you are dealing with a C-array of objective-c objects, you can actually use NSArray's built in constructor for turning a C-array into a NSArray:
NSArray *result = [NSArray arrayWithObjects:arg count:count];
I have a string I want to parse and return an equivalent enum. I need to use the enum type elsewhere, and I think I like how I'm defining it. The problem is that I don't know a good way to check the string against the enum values without being redundant about the order of the enums.
Is there no option other than a big if/else?
typedef enum {
ZZColorRed,
ZZColorGreen,
ZZColorBlue,
} ZZColorType;
- (ZZColorType)parseColor:(NSString *)inputString {
// inputString will be #"red", #"green", or #"blue" (trust me)
// how can I turn that into ZZColorRed, etc. without
// redefining their order like this?
NSArray *colors = [NSArray arrayWithObjects:#"red", #"green", #"blue", nil];
return [colors indexOfObject:inputString];
}
In Python, I'd probably do something like the following, although to be honest I'm not in love with that either.
## maps url text -> constant string
RED_CONSTANT = 1
BLUE_CONSTANT = 2
GREEN_CONSTANT = 3
TYPES = {
'red': RED_CONSTANT,
'green': GREEN_CONSTANT,
'blue': BLUE_CONSTANT,
}
def parseColor(inputString):
return TYPES.get(inputString)
ps. I know there are color constants in Cocoa, this is just an example.
try this: Map enum to char array
Pseudo code.. untested.
int lookup(const char* str) {
for(name = one; name < NUMBER_OF_INPUTS; name++) {
if(strcmp(str, stats[name]) == 0) return name;
}
return -1;
}
A more objective-c'ish version of the code could be:
// build dictionary
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
for(i=0; i<number_of_strings; i++) {
[dict setObject:[NSNumber numberWithInteger:i] forKey:[NSString stringWithUTF8String:names[i]]];
}
// elsewhere... lookup in dictionary
id obj = [dict objectForKey:name];
if(obj) return [obj intValue];
return -1;
This has already been answered: Converting between C enum and XML
Basically, you wind up defining corresponding strings when you define your enum, and then you use a category on NSArray so that you can do this:
static NSArray* colorNamesArray = [[NSArray alloc] initWithObjects:colorNames];
//colorNames is a nil-terminated list of string literals #defined near your enum
NSString* colorName = [colorNamesArray stringWithEnum:color];
//stringWithEnum: is defined with a category
Sure, the #define is a little ugly, but the code above, which is what you'll work with most of the time, is actually pretty clean.
I was never satisfied with any of the suggestions. (But I appreciate the effort that went into them.) I tried a few of them but they didn't feel good or were error-prone in practice.
I ended up created a custom dictionary to map integers to strings which feels a lot better because it's Cocoa through and through. (I didn't subclass NSDictionary in order to make it harder to misuse.)
#interface ZZEnumDictionary : NSObject {
NSMutableDictionary *dictionary;
}
+ (id)dictionary;
+ (id)dictionaryWithStrings:(id)firstString, ...;
- (NSString *)stringForInt:(NSInteger)intEnum;
- (NSInteger)intForString:(NSString *)stringEnum;
- (BOOL)isValidInt:(NSInteger)intEnum;
- (BOOL)isValidString:(NSString *)stringEnum;
- (BOOL)stringEquals:(NSString *)stringEnum intEnum:(NSInteger)intEnum;
- (BOOL)setContainsString:(NSSet *)set forInt:(NSInteger)intEnum;
- (NSArray *)allStrings;
#end
#interface ZZEnumDictionary ()
- (void)setInt:(NSInteger)integer forString:(NSString *)string;
#end
I'm making a program that has a lot of constants. I decided to put them all into a separate class and I'm importing it by the classes that need it. The files look similar to this
// Constants.h
extern const int baseCostForBuilding;
extern const int maxCostForBuilding;
// etc
// Constants.m
const int baseCostForBuilding = 400;
const int maxCostForBuilding = 1000;
// etc
What I'm trying to do is access them using key-value coding. What I've tried so far hasn't worked.
id object = [self valueForKey:#"baseCostForBuilding"];
But I can do the following and it works fine.
id object = baseCostForBuilding;
This may seem pointless but I have a lot of variables that have to end in "CostForBuilding" and the function I need this in only gets the first part of the string. Example, "base", "max", "intermediate", etc. It will then combine it with "CostForBuilding" or something else to get the variable name.
If this is possible, it would be way nicer to only have one or two lines of code instead of multiple if-statements to access the correct variable. Does anyone know a way to do this? Thanks in advance.
You can fill a dictionary with the appropriate values:
- (id)init
{
...
buildingCosts = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:100], #"base",
[NSNumber numberWithInt:200], #"max",
...,
nil];
...
}
- (int)buildingCostForKey:(NSString *)key
{
return [(NSNumber *)[buildingCosts objectForKey:key] intValue];
}
- (void)dealloc
{
[buildingCosts release];
}
Which you could then use as follows:
int baseCost = [myClass buildingCostForKey:#"base"];