views:

92

answers:

2

This is from a category that I'm using to modify UIView. The code works, but in the first method (setFrameHeight) I'm using a block and in the second method (setFrameWidth) I'm not. Is there any way to use blocks more efficiently in this example?

typedef CGRect (^modifyFrameBlock)(CGRect);

- (void) modifyFrame:(modifyFrameBlock) block {
    self.frame = block(self.frame);
}

- (void) setFrameWidth:(CGFloat)newWidth {
    modifyFrameBlock b = ^CGRect (CGRect frame) { 
        frame.size.width = newWidth;
        return frame; 
    };      
    [self modifyFrame:b];
}

- (void) setFrameHeight:(CGFloat)newHeight {
    CGRect f = self.frame;
    f.size.height = newHeight;
    self.frame = f;
}

The answer may be that blocks are not appropriate for such short methods, or something. The syntax sure seems funky.

+1  A: 

The only thing you gain is not declaring the local variable for the new rect, in exchange you need to define a block. That is not a good deal, especially as it distracts from what you are doing.

Note that your block-usage could be shortened a bit:

[self modifyFrame:^(CGRect frame) {
    frame.size.width = newWidth;
    return frame; 
}];

Or even:

[self modifyFrame:^(CGRect* frame) {
    frame->size.width = newWidth;
}];

With modifyFrame: being:

CGRect frame;
block(&frame);
self.frame = frame;

But i'd still limit such approaches to more complex methods that only require minor parts of the code to be different.

Georg Fritzsche
True, I just kind of thought I would be able to get it down to one line per each `set` function, but instead this experiment backfired, I guess.
Yar
Actually, what you get is the ability to define the method of calculating the new frame when calling the `setFrameHeight` and `setFrameWidth` methods. The real question is whether it is worth it?
Abizern
@Daniel - the `set` functions may be shorter, but the calling code gets those lines instead.
Abizern
@Georg Fritzsche, I'm not a huge fan of concise code, but declaring the block inline does made the code more readable. I like your edit: not bad at all.
Yar
@Abi: At the moment not, there is no block passed to those two.
Georg Fritzsche
@Daniel: Regarding backfiring - it *would* make sense if `modifyFrame:` wasn't that trivial. It would be useful for complex methods where only a minor part differs and passing in different values isn't enough.
Georg Fritzsche
@Georg Fritzsche, I actually wrote a question about this already. I still don't understand why, but you cannot use `self.frame.size.width = newWidth;`. Nor can you use `self.frame.size = CGSizeMake...`. http://stackoverflow.com/questions/3190639/alter-cgrect-or-any-struct
Yar
@dan: Ah, of course - its actually a function call which returns an `rvalue` *\*cough\**. Note that the getters are/can be implemented as functions and such structures like `CGRect` are returned by value (i.e. copied). If you'd return them by reference (`CGRect*`) as you do for class-types, you could modify them.
Georg Fritzsche
@dan: Take e.g. a function `int f()` - it returns a copy of some other value, not a reference, so you can't modify its source. Its the same for the getters here.
Georg Fritzsche
@Georg, oh! It returns a copy (any method that returns a struct does) so if you don't assign it, you cannot modify it because it would go nowhere! Hence the error message: Lvalue required as left operand of assignment. A quick read on lvalues here -- http://en.wikipedia.org/wiki/Value_(computer_science) -- and I think I get it. Thanks for taking the time.
Yar
+1  A: 

This particular pattern is useful in other situations (generally where extensibility is needed at runtime) and is frequently seen in C with function pointers in place of blocks (e.g. qsort() and bsearch().) For just updating a field, however, the usual way is to just call from the simpler method to the more complex one:

- (void)setDisplaysUserInterface: (BOOL)flag {
    [self setDisplaysUserInterface: flag animated: YES];
}


- (void)setDisplaysUserInterface: (BOOL)flag animated: (BOOL)animated {
    [self setDisplaysUserInterface: flag animated: animated hideAfterDelay: NAN];
}

- (void)setDisplaysUserInterface: (BOOL)flag animated: (BOOL)animated hideAfterDelay: (NSTimeInterval)hideDelay {
    // and so forth
}
Jonathan Grynspan
I had to think about why this was even an answer to the question, but yes, very good point +1. We do that in Java a ton: you can either call one of the more complex method sigs (if necessary) or you can use one of the simpler ones, which fills in default params. Totally different paradigm in ActionScript (maybe Javascript too?) and Ruby: you can specify a default, like `flag = true`. It does have its limits (you cannot tweak the variable in the 'simpler' methods), but MANY times it's just enough.
Yar