I've started using blocks, and one of the first things I encountered is an inability to set values which are captured by the closure. This is fine, I've been using C/C++ a long time. I'll just use pointers!
MyObject* bestObj = nil;
float bestDist= 10000.f;
MyObject **pBestObj = &bestObj;
float* pBestDist = &bestDist;
[self testObjects:class block:^(MyObject* obj){
CGRect r = [obj boundingBox];
// position is captured from outside this code sample
if( CGRectContainsPoint( r, position ) )
{
float dist = GetDistance( obj, position );
if(dist < bestDist)
{
*pBestDist = dist;
*pBestObj = obj;
}
}
}];
return bestObj;
My question is, is this safe? I assume that as long as my pointer points to something that hasn't gone out of scope and that still exists, that it should work. But I'm also assuming that things that take blocks don't ever, say, run them in parallel. I know my code doesn't, but I don't know about, say, using a block with an NSArray enumerateObjectsUsingBlock call.
The 'right' way to do this would be to mark those original variables as block mutable, __block
Related
-(BOOL)isInArray:(CGPoint)point{
if ([valid count]==0) {
return NO;
}
for (NSValue *value in valid) {
CGPoint er=[value CGPointValue];
if( CGPointEqualToPoint(point,er)) return NO;
}
return YES;
}
-(void)check:(CGPoint)next{
if (!next.y==0) {
int ics=(int) next.x;
int igrec=(int)next.y;
if (mat[ics][igrec]==mat[ics-1][igrec]){
if (![self isInArray:next]) {
[valid addObject:[NSValue valueWithCGPoint:next]];
NSLog(#"valid y!=0 : %#",valid);
[self check:CGPointMake(ics-1, igrec)];
}
}
}
}
y are columns , x are rows , mat is a C matrix
what i'm trying to do here is this:i get a point, next, in a matrix,mat (i'll use struct but for the scope of testing i use CGPoint..it's basicaly the same thing), and for that point i check if it's on the first row and if it's not i check if the value is equal to the value of the row above .If it is, i add the coord of the point into an array and move to the value above (recursively). I have ifs for left,right, and below too...but the idea is the same.
My issues:
For some reason it doesn't work as it should, even with a mat full of 1 values
The NSMutableArray i use to store the points is always null (note that the NSLog gets called so it should've added an object already)
Does recursivity work with methods?
If you have a better idea how to do this...i'm listening
The "valid" array is nil because you haven't allocated it. (You can send an addObject: message, or any message, to a nil pointer--it just doesn't do anything.) Make sure you've got
valid = [[NSMutableArray alloc] init];
somewhere before you're calling this code.
Also, "!next.y==0" is questionable. It might turn out to be identical to "next.y != 0" even if ! has a higher precedence that ==, but I wouldn't guarantee it. That's all I spot for now, without really grokking what this code is trying to do..
Oh, another quick note: Instead of writing your own isInArray, just use NSArray's containsObject:. The inner part of the check method (second indent) is then
NSValue* pointVal = [NSValue valueWithCGPoint:next];
if ( ![valid containsObject:pointVal] )
{
[valid addObject:next];
[self check:CGPointMake(ics-1, igrec)];
}
Or, if you don't care about the order of the points in the valid array you could use an NSMutableSet instead and not worry about checking if the point is already in the collection.
And yes, recursion in methods is fine. They're really the same as C functions, just with a couple hidden arguments (the self pointer and the method name) and called through a dispatch function.
Consider the following code fragment:
for(/* some condition */) {
int x = rand();
[array addObject:^(){
NSLog(#"%d", x);
}]
}
for(void (^block)() in array) {
block();
}
Now I would expect this code snippet to print out all values assigned to x in that for loop; however it seems that all blocks share the same 'x' variable (presumably the last one).
Any idea why this is so and how I could fix the code to have each block contain the variable 'x' as it was at the time the block was defined?
The documentation specifically says not to do this. The reason is that blocks are allocated on the stack, which means they can go out of scope. For the same reason you can't access the variable x outside of the first for loop, you also shouldn't use that block. x has gone out of scope, along with the block itself, and could contain any value.
To get around this, you can take a copy of the block like so:
for(/* some condition */) {
int x = rand();
void(^logBlock)() = ^() { NSLog(#"%d", x); }
[array addObject:[[logBlock copy] autorelease]];
}
This moves the block onto the heap, and should fix your problem.
Is it possible to implement something like a Smalltalk-style whileTrue: method in Objective-C using blocks? Specifically, instead of:
int count = 0;
while (count < 10)
{
NSLog(count);
count++;
}
I'd like to be able to do (via a wrapper on the bool primitive called OOBoolean) something like...
__block int count = 0;
[[OOBoolean booleanWithBool: count < 10] whileTrueDo: ^() {
NSLog(count);
count++;
}];
I'm having trouble understanding how this would be implemented though...
Here you have a couple of ideas,
Assuming your bool wrapper implements boolValue, a naive implementation could be:
-(void) whileTrueDo:(void (^)(void)) block{
while ([self boolValue]) {
block();
}
}
In order for the wrapper to change its bool value after each iteration, the block must be able to actually change the variable that is used to calculate the boolean condition. So, in your case, by setting the __block type modifier to count, and increasing count in each block execution, you should be able to make it work.
The problem is, if you create your wrapper by sending the evaluated condition, as you stated in your question, you wont be able to change its bool value in each iteration. So, I would change the way the wrapper is created and the whileTrueDo: naive implementation so the boolean wrapper uses an evaluation block.
__block int count = 0;
OOBooleanBlock evaluationBlock = ^BOOL{
return count < 10;
};
[[OOBoolean booleanWithBlock:evaluationBlock] whileTrueDo: ^() {
NSLog(count);
count++;
}];
//In OOBoolean
+(OOBoolean*) booleanWithBlock:(OOBooleanBlock) evaluationBlock{
//Instantiate, set the evaluationBlock ivar and return the ooboolean object.
}
-(void) whileTrueDo:(void (^)(void)) block{
while (self.evaluationBlock()) {
block();
}
}
Remember to use the __block type modifier, otherwise you will enter in an infinite loop.
I haven't tested this, I hope this helps you though.
Cheers
- (void) swapController:(MyViewController*)controller1
with:(MyViewController*)controller2
{
MyViewController *swap = controller2;
controller2 = controller1;
controller1 = swap;
}
Looks like this doesn't work because I'm not passing references. How to do it anyway?
You can do this by passing in pointers to the pointers you want to change:
- (void)swapController:(MyViewController**)controller1 with:(MyViewController**)controller2
{
MyViewController* swap = *controller2;
*controller2 = *controller1;
*controller1 = swap;
}
Not looking for this as an opposing answer here, but rather to complement #Huw's answer.
On implementing his swapController:with: method above, I noticed some issues with ARC and with primitives. ARC will take issue if you're passing a strong id* to an id* ( which it will designate as autoreleased id and other fun warnings).
If you're anything like me, well, one fine day you'll be swapping pointers back and forth in objc and one of those values will be a primitive data type.
If you do end up doing something like that, here's a little helper (like I said earlier) to complement the above answer:
void swapObjcPointerWithC ( void** ptrA, void** ptrB ) {
void *temp = *ptrA;
*ptrA = *ptrB;
*ptrB = temp;
}
...
// Later on
id objA;
int anInt = 10;
swapObjcPointerWithC ( (void*)&objA, (void*)&anInt );
There you go. My two cents.
Note: also works will nil pointers apparently.
I've got object_getInstanceVariable to work as here however it seems to only work for floats, bools and ints not doubles. I do suspect I'm doing something wrong but I've been going in circles with this.
float myFloatValue;
float someFloat = 2.123f;
object_getInstanceVariable(self, "someFloat", (void*)&myFloatValue);
works, and myFloatValue = 2.123
but when I try
double myDoubleValue;
double someDouble = 2.123f;
object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
I get myDoubleValue = 0. If I try to set myDoubleValue before the function eg. double myDoubleValue = 1.2f, the value is unchanged when I read it after the object_getInstanceVariable call. Setting myIntValue to some other value before the getinstancevar function above returns 2 as it should, ie. it has been changed.
then I tried
Ivar tmpIvar = object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
If I do ivar_getName(tmpIvar) I get "someDouble", but myDoubuleValue = 0 still! Then I try ivar_getTypeEncoding(tmpIvar) and I get "d" as it should be.
So to summarize, if typeEncoding = float, it works, if it is a double, the result is not set but it correctly reads the variable and the return value (Ivar) is also correct.
I must be doing something basic wrong that I cant see so I'd appreciate if someone could point it out.
object_getInstanceVariable is a confused little function. It is documented that the last parameter is a void ** parameter—that is, you pass the address of a void * variable and get a pointer to the instance variable—but it is implemented as if it was a void * parameter—that is, you pass the address of the variable that you want to hold a copy of the instance variable. The problem is that the implementation ignores the size of the instance variable and just does a pointer copy. So anything that's the same size as a pointer will work perfectly. If you're running on a 32-bit architecture, only the high 32 bits will be copied. (You should witness the same behavior with a long long instance variable as well.)
The solution is to use the primary API, key-value coding, using -valueForKey:.
The other solution: If you wanted to write a fixed version, say as a category for NSObject, it would look something like this:
#implementation NSObject (InstanceVariableForKey)
- (void *)instanceVariableForKey:(NSString *)aKey {
if (aKey) {
Ivar ivar = object_getInstanceVariable(self, [aKey UTF8String], NULL);
if (ivar) {
return (void *)((char *)self + ivar_getOffset(ivar));
}
}
return NULL;
}
#end
Then your code would look like this:
double myDoubleValue = *(double *)[self instanceVariableForKey:#"someDouble"];
What about using valueForKey:?
NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(#"Double value: %f", [value doubleValue];
Note: this requires you to have a "someFloat" method. If you want to use setValue:forKey:, you'll also need the "setSomeFloat:" method. This is easily implemented by declaring the ivar as an #property and synthesizing it.