NSArray assignments causes "Multiple unsequenced modifications" - objective-c

I'm assigning elements to an array at compile time (Xcode). The compiler warning ("Multiple unsequenced modifications") implies that the compiler might not compute the assignments in the same order as the elements are listed.
Simplified code example (I'm actually using a macro to assign array1 elements):
- (void)arrayAssignmentTest
{
int x = 0;
// The compiler warning implies that I cannot rely on
// the order the compiler executes the assignments.
NSArray *array1 = #[
#(x++), // #(0) <-- WARNING: Multiple unsequenced modifications to 'x'
#(x++), // #(1)
#(x++), // #(2)
];
// But, there's no warning here, so I assume I can rely on
// the order in which the compiler executes these assignments.
NSArray *array2 = #[
myFunction(), // #(0)
myFunction(), // #(1)
myFunction(), // #(2)
];
}
NSNumber *myFunction()
{
static int x = 0;
return #(x++);
}
The compiler is warning about array1 assignments, but it doesn't it warn about array2.
Why does the compiler treat array1 differently from array2, and is there a better way to tell the compiler to assemble the array1 elements in order instead of using a separate function or method as for array2?

Related

Selector name for NSArray literal constructor, like #[]?

It's been a while since Clang added Objective-C literal syntax for NSDictionary, NSArray, NSNumber, and BOOL literals, like #[object1, object2,] or #{key : value}
I'm looking for the selector name associated with the array literal, #[].
I tried to find out using the following code for NSArray, but I didn't see a selector that seemed right.
unsigned int methodCount = 0;
Method * methods = class_copyMethodList([NSArray class], &methodCount);
NSMutableArray * nameOfSelector = [NSMutableArray new];
for (int i = 0 ; i < methodCount; i++) {
[nameOfSelector addObject:NSStringFromSelector(method_getName(methods[i]))];
}
#[] is not a method on NSArray, so you're not going to find it there.
The compiler just translates #[] into a call to [NSArray arrayWithObjects:count:]. As in it basically finds all the #[] and replaces it with [NSArray arrayWithObjects:count:] (carrying across the arguments of course)
See the Literals section here
#[] uses +arrayWithObjects:count:
Official Clang Documentation
Array literal expressions expand to calls to +[NSArray arrayWithObjects:count:], which validates that all objects are non-nil. The variadic form, +[NSArray arrayWithObjects:] uses nil as an argument list terminator, which can lead to malformed array objects.
When you write this:
NSArray *array = #[ first, second, third ];
It expands to this:
id objects[] = { first, second, third };
NSArray *array = [NSArray arrayWithObjects:objects count:(sizeof(objects) / sizeof(id))];

Needing to typecast

I have a class with an initializer that takes a NSDictionary:
-(id)initWithVector:(NSDictionary *) vectorDictionary;
when i try to pass it a NSDictionary, its giving me an error:
Incompatible point types sending'VectorClass * _strong' to parameter
type 'NSDictionary *'
code:
// myVectorList is an array of dictionaries
for (NSDictionary *vector in self.myFielder.myVectorList)
{
if ([vector isKindOfClass:[NSDictionary class]])
{
// hardcoded for testing purposes
if ([[vector objectForKey:HANDLE] isEqualToString:#"pt07p48u17aj75qx8n2fri9jlkrc262yt8"])
{
// GET THE WARNING ON PASSING "VECTOR"
VectorClass *vector = [[VectorClass alloc] initWithVector:vector];
[vector retrieveVectorAttributeTable];
[vector retrieveVectorMetadataTable];
}
}
}
if i typecast (NSDictionary *)vector, no warning.
vector should be a dictionary, so why am i getting the warning?
You use the same name for two different variables. The inner most variable is of type VectorClass defined in the same line, so the compiler tries to pass it to the init method, instead, change its name:
VectorClass *vectorC = [[VectorClass alloc] initWithVector:vector];
[vectorC retrieveVectorAttributeTable];
[vectorC retrieveVectorMetadataTable];

How can I pass a C array to a objective-C function?

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];

objective C using a string to call a method

Hi im new to objective C and was wondering if someone might be able to help me with this. I have a few different methods each requiring 3 input values and normally call it using
[self methodA:1 height:10 speed:3]
but the method name I want to read from a string in a plist so for example if the string was methodB i would get
[self methodB:1 height:10 speed:3]
for "methodC"
[self methodC:1 height:10 speed:3]
and so on.
Any ideas how I might do this I tried defining the string as a Selector using NSSelectorFromString
NSString *string = [plistA objectForKey:#"method"];
SEL select = NSSelectorFromString(string);
[self performSelector:select:c height:b speed:a];
However this did not work either any help would be greatly appreciated.
Have tried the solution below but could not get to work here is what i've tried.
So just to recap I have methods such as
spawnEnemyA:2 withHeight:3 withSpeed:4
spawnEnemyB:3 withHeight:2 withSpeed:5
and I want to read the values I want to pass to these methods as well as the method type from a plist file. my code is as follows, //////////////////////////////////////////////////////////////
//These are the values I read from the plist that I want my method to use
int a = [[enemySettings objectForKey:#"speed"] intValue];
int b = [[enemySettings objectForKey:#"position"] intValue];
int c = [[enemySettings objectForKey:#"delay"] intValue];
// I Also read the method name from the plist and combine it into a single string
NSString *method = [enemySettings objectForKey:#"enemytype"];
NSString *label1 = #"spawn";
NSString *label2 = #":withHeight:withSpeed:";
NSString *combined = [NSString stringWithFormat:#"%#%#%#",label1, method,label2];
//Check that the string is correct get spawnEnemyA:withHeight:withSpeed:
CCLOG(#"%#",combined);
//This is the Invocation part
NSInvocation * invocation = [ NSInvocation new ];
[ invocation setSelector: NSSelectorFromString(combined)];
[ invocation setArgument: &c atIndex: 2 ];
[ invocation setArgument: &b atIndex: 3 ];
[ invocation setArgument: &a atIndex: 4 ];
[ invocation invokeWithTarget:self ];
[invocation release ];
////////////////////////////////////////////////////////////////////
The code compiles without any errors but the methods are not called. Any ideas? Cheers
You can't use performSelector for a method with 3 (or more) arguments.
But for your information, here's how to use it:
SEL m1;
SEL m2;
SEL m3;
m1 = NSSelectorFromString( #"someMethodWithoutArg" );
m2 = NSSelectorFromString( #"someMethodWithAnArg:" );
m1 = NSSelectorFromString( #"someMethodWithAnArg:andAnotherOne:" );
[ someObject performSelector: m1 ];
[ someObject performSelector: m2 withObject: anArg ];
[ someObject performSelector: m2 withObject: anArg withObject: anOtherArg ];
For methods with more than 2 arguments, you will have to use the NSInvocation class.
Take a look at the documentation to learn how to use it.
Basically:
NSInvocation * invocation = [ NSInvocation new ];
[ invocation setSelector: NSStringFromSelector( #"methodWithArg1:arg2:arg3:" ) ];
// Argument 1 is at index 2, as there is self and _cmd before
[ invocation setArgument: &arg1 atIndex: 2 ];
[ invocation setArgument: &arg2 atIndex: 3 ];
[ invocation setArgument: &arg3 atIndex: 4 ];
[ invocation invokeWithTarget: targetObject ];
// If you need to get the return value
[ invocation getReturnValue: &someVar ];
[ invocation release ];
In general, this kind of dynamism often indicates an anti-pattern. Colluding data with implementation in this fashion is not generally a best practice.
Sometimes, though, it is necessary. If you are going down this path, then given that your various method declarations likely look like:
- (void)methodAWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s;
- (void)methodBWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s;
- (void)methodCWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s;
You would probably want something like:
NSString *selName = [NSString stringWithFormat:#"method%#Width:height:speed:", ... one of #"A", #"B", or #"C" ....];
SEL selector = NSelectorFromString(selName);
Then:
if (![target respondsToSelector:selector])
return; // no can do
void (*castMsgSend)(id, SEL, NSUInteger, NSUInteger, NSUInteger) = (void*)objc_msgSend;
castMsgSend(target, selector, 1, 10, 3);
Every method call is compiled down to a call to objc_msgSend(). By doing the above, you are creating a fully type-safe/type-checked call site that goes through the normal Objective-C messaging mechanism, but the selector is dynamically defined on the fly.\
While performSelector: (and multi-arg variants) are handy, they can't deal with non-object types.
And, as MacMade pointed out in a comment, watch out for floating point and structure returns. They use different variants of objc_msgSend() that the compiler automatically handles in the normal [foo bar] case.
You can directly use objc_msgsend:
NSString *methodName = [plistA objectForKey:#"method"];
objc_msgSend(self, methodName, c, b, a);
Mind that the selector must include all pieces, eg #"method:height:speed:"
You should replace the following line with:
[self performSelector:select:c height:b speed:a];
and write following:
[self performSelector:select withObject:[NSArray arrayWithObjects:c,b,a,nil]];

Passing and calling dynamic blocks in Objective C

As part of a unit test framework, I'm writing a function genArray that will generate NSArrays populated by a passed in generator block. So [ObjCheck genArray: genInt] would generate an NSArray of random integers, [ObjCheck genArray: genChar] would generate an NSArray of random characters, etc. In particular, I'm getting compiler errors in my implementation of genArray and genString, a wrapper around [ObjCheck genArray: genChar].
I believe Objective C can manipulate blocks this dynamically, but I don't have the syntax right.
ObjCheck.m
+ (id) genArray: (id) gen {
NSArray* arr = [NSMutableArray array];
int len = [self genInt] % 100;
int i;
for (i = 0; i < len; i++) {
id value = gen();
arr = [arr arrayByAddingObject: value];
}
return arr;
}
+ (id) genString {
NSString* s = #"";
char (^g)() = ^() {
return [ObjCheck genChar];
};
NSArray* arr = [self genArray: g];
s = [arr componentsJoinedByString: #""];
return s;
}
When I try to compile, gcc complains that it can't do gen(), because gen is not a function. This makes sense, since gen is indeed not a function but an id which must be cast to a function.
But when I rewrite the signatures to use id^() instead of id, I also get compiler errors. Can Objective C handle arbitrarily typed blocks (genArray needs this), or is that too dynamic?
Given that blocks are objects, you can cast between block types and id whenever you want, though if you cast the block to the wrong block type and call it, you're going to get unexpected results (since there's no way to dynamically check at runtime what the "real" type of the block is*).
BTW, id^() isn't a type. You're thinking of id(^)(). This may be a source of compiler error for you. You should be able to update +genArray: to use
id value = ((id(^)())(gen))();
Naturally, that's pretty ugly.
*There actually is a way, llvm inserts an obj-c type-encoded string representing the type of the block into the block's internal structure, but this is an implementation detail and would rely on you casting the block to its internal implementation structure in order to extract.
Blocks are a C-level feature, not an ObjC one - you work with them analogously to function pointers. There's an article with a very concise overview of the syntax. (And most everything else.)
In your example, I'd make the gen parameter an id (^gen)(). (Or possibly make it return a void*, using id would imply to me that gen generates ObjC objects and not completely arbitrary types.)
No matter how you declare your variables and parameters, your code won't work. There's a problem that runs through all your compiler errors and it would be a problem even if you weren't doing convoluted things with blocks.
You are trying to add chars to an NSArray. You can't do that. You will have to wrap them them as some kind of Objective C object. Since your only requirement for this example to work is that the objects can be inputs to componentsJoinedByString, you can return single-character NSStrings from g. Then some variety of signature like id^() will work for genArray. I'm not sure how you parenthesize it. Something like this:
+ (id) genArray: (id^()) gen;
+ (id) genString {
...
NSString * (^g)() = ^() {
return [NSString stringWithFormat:#"%c", [ObjCheck genChar]];
};
...
}
NSString * is an id. char is not. You can pass NSString * ^() to id ^(), but you get a compiler error when you try to pass a char ^() to an id ^(). If you gave up some generality of genArray and declared it to accept char ^(), it would compile your call to genArray, but would have an error within genArray when you tried to call arrayByAddingObject and the argument isn't typed as an id.
Somebody who understands the intricacies of block syntax feel free to edit my post if I got some subtle syntax errors.
Btw, use an NSMutableArray as your local variable in genArray. Calling arrayByAddingObject over and over again will have O(n^2) time performance I imagine. You can still declare the return type as NSArray, which is a superclass of NSMutableArray, and the callers of genArray won't know the difference.