Why does loop variable become `nil` after loop

291 views Asked by At

I have:

NSDictionary* server;

for (server in self.servers)
{
    if (<some criterium>)
    {
        break;
    }
}

// If the criterium was never true, I want to use the last item in the
// the array. But turns out that `server` is `nil`.

The loop block never changes server.

servers is an NSMutableArray with dictionaries, a property that's not changed during the loop.

Why does server have the value nil after the loop has ended?

Was the first time I used such a variable after the loop. Without thinking too much, I assumed it would work like (in the old C days):

int i;
for (i = 0; i < n; i++)
{
    ...
}
3

There are 3 answers

1
gnasher729 On BEST ANSWER

The language defines that the loop variable will be set to nil when the loop exits. The language doesn't state that the loop variable will have the last value, quite the opposite.

Behind the scenes, there is a very good reason for that. Fast iteration makes assumptions about the underlying data. For example, when you iterate over a mutable array, the array may not be modified while you iterate. Fast iteration is fast, among other things, because it doesn't retain and release the loop variable. Instead it relies on the underlying object (for example an array) to hold a reference.

But once the loop exits, that underlying object gives no guarantees anymore. The array could go away, or the array element that was last used might get deleted. So the compiler either must retain the variable, or set it to nil. Setting to nil is quicker.

1
AnthoPak On

The variable used in a for-loop will always be nil when loop ends (except if the loop is broken with break statement, as pointed by @rmaddy).

Another way for your loop which will avoid misunderstanding is :

for (NSDictionary* server in self.servers)
{
    //... server is not nil
}
//here server doesn't exist (out of scope)

If you want to save a value of self.servers in a var outside of the loop you'll have to do this :

NSDictionary* serverSave;

for (NSDictionary* server in self.servers)
{
    serverSave = server;
}
//here you can use serverSave, which will contain the last value of self.servers

Hope this helps.

2
Wyetro On

It's nil after the loop because Objective-C with ARC nills it (see Value of loop variable after "for in" loop in Objective C).

Thus when you get to the end of:

NSDictionary* server; // server is nil here

for (server in self.servers)
{
    ... // Does not set server to nil.
}

server will be nil.

If you are looking for a certain value you can do this:

NSDictionary *dict;

for (NSDictionary* server in self.servers)
{
    if (server == whatever you want){
        dict = server;
        break;
    }
}

Or to just get the last value:

NSDictionary *dict;

for (NSDictionary* server in self.servers)
{
    dict = server;
}