In C family languages, we can add paired braces ({}
) to stipulate the scope of some code. There are two pros by doing this. Fisrt, all temporary variables will be invalid once the code goes beyond the scope. This could not only help naming the variables, but also make a cleaner stack and good performance. Second, properly using of scope improves the readability of code.
Take an example, although I do not like to write UI code, there are quite some developers are used to it. If we need to build UI with code in Objective-C, we often choose to write some code like this in -loadView
:
-(void) loadView {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
UILabel *titleLabel = [[UILabel alloc]
initWithFrame:CGRectMake(150, 30, 20, 40)];
titleLabel.textColor = [UIColor redColor];
titleLabel.text = @"Title";
[view addSubview:titleLabel];
UILabel *textLabel = [[UILabel alloc]
initWithFrame:CGRectMake(150, 80, 20, 40)];
textLabel.textColor = [UIColor redColor];
textLabel.text = @"Text";
[view addSubview:textLabel];
self.view = view;
}
Here we just added two views, but the code is already scary. Think about how it could be if we need to add 10+ views here. We have to consider the names of these views, to make sure they have the correct meaning. If we happen to miss spell a textLabel
to titleLabel
, the compiler will just be happy to accept it without any warnings. This kind of bug is hard to find. For a long scope of code without the possibility of reuse, I suggest adding local scope to divide it into smaller pieces.
The code could be rewritten in the form below. By doing so, compiler could be a help for some mistake. We could also focus on the code block than before:
-(void) loadView {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
{
UILabel *titleLabel = [[UILabel alloc]
initWithFrame:CGRectMake(150, 30, 20, 40)];
titleLabel.textColor = [UIColor redColor];
titleLabel.text = @"Title";
[view addSubview:titleLabel];
}
{
UILabel *textLabel = [[UILabel alloc]
initWithFrame:CGRectMake(150, 80, 20, 40)];
textLabel.textColor = [UIColor redColor];
textLabel.text = @"Text";
[view addSubview:textLabel];
}
self.view = view;
}
While in Swift, the braces mean a closure instead of a scope. If we want to use something similar like local scope, a possible choice is defining a global function accepting ()->()
closure, and executing it:
func local(closure: ()->()) {
closure()
}
We could use the trailing closure to "simulate" the local scope syntax:
override func loadView() {
let view = UIView(frame: CGRectMake(0, 0, 320, 480))
local {
let titleLabel = UILabel(frame: CGRectMake(150, 30, 20, 40))
titleLabel.textColor = UIColor.redColor()
titleLabel.text = "Title"
view.addSubview(titleLabel)
}
local {
let textLabel = UILabel(frame: CGRectMake(150, 80, 20, 40))
textLabel.textColor = UIColor.redColor()
textLabel.text = "Text"
view.addSubview(textLabel)
}
self.view = view
}
There is a great tip in Objective-C: we can use the declaration extension of GUN C to assign a value at the same time of limiting local scope. It is another way to make your code clean. For example, if we need to keep a reference of the titleLabel
above:
self.titleLabel = ({
UILabel *label = [[UILabel alloc]
initWithFrame:CGRectMake(150, 30, 20, 40)];
label.textColor = [UIColor redColor];
label.text = @"Title";
[view addSubview:label];
label;
});
Of course there is no GUN C extension in Swift. We can write similar code in Swift easily:
titleLabel = {
let label = UILabel(frame: CGRectMake(150, 30, 20, 40))
label.textColor = UIColor.redColor()
label.text = "Title"
self.view.addSubview(label)
return label
}()
This is also a good way to isolate your code.