The generally-preferred code for 10.5+/iOS.
for (id object in array) {
// do something with object
}
This construct is used to enumerate objects in a collection which conforms to the NSFastEnumeration
protocol. This approach has a speed advantage because it stores pointers to several objects (obtained via a single method call) in a buffer and iterates through them by advancing through the buffer using pointer arithmetic. This is much faster than calling -objectAtIndex:
each time through the loop.
It's also worth noting that while you technically can use a for-in loop to step through an NSEnumerator
, I have found that this nullifies virtually all of the speed advantage of fast enumeration. The reason is that the default NSEnumerator
implementation of -countByEnumeratingWithState:objects:count:
places only one object in the buffer on each call.
I reported this in radar://6296108
(Fast enumeration of NSEnumerators is sluggish) but it was returned as Not To Be Fixed. The reason is that fast enumeration pre-fetches a group of objects, and if you want to enumerate only to a given point in the enumerator (e.g. until a particular object is found, or condition is met) and use the same enumerator after breaking out of the loop, it would often be the case that several objects would be skipped.
If you are coding for OS X 10.6 / iOS 4.0 and above, you also have the option of using block-based APIs to enumerate arrays and other collections:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
You can also use -enumerateObjectsWithOptions:usingBlock:
and pass NSEnumerationConcurrent
and/or NSEnumerationReverse
as the options argument.
10.4 or earlier
The standard idiom for pre-10.5 is to use an NSEnumerator
and a while loop, like so:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
I recommend keeping it simple. Tying yourself to an array type is inflexible, and the purported speed increase of using -objectAtIndex:
is insignificant to the improvement with fast enumeration on 10.5+ anyway. (Fast enumeration actually uses pointer arithmetic on the underlying data structure, and removes most of the method call overhead.) Premature optimization is never a good idea — it results in messier code to solve a problem that isn't your bottleneck anyway.
When using -objectEnumerator
, you very easily change to another enumerable collection (like an NSSet
, keys in an NSDictionary
, etc.), or even switch to -reverseObjectEnumerator
to enumerate an array backwards, all with no other code changes. If the iteration code is in a method, you could even pass in any NSEnumerator
and the code doesn't even have to care about what it's iterating. Further, an NSEnumerator
(at least those provided by Apple code) retains the collection it's enumerating as long as there are more objects, so you don't have to worry about how long an autoreleased object will exist.
Perhaps the biggest thing an NSEnumerator
(or fast enumeration) protects you from is having a mutable collection (array or otherwise) change underneath you without your knowledge while you're enumerating it. If you access the objects by index, you can run into strange exceptions or off-by-one errors (often long after the problem has occurred) that can be horrific to debug. Enumeration using one of the standard idioms has a "fail-fast" behavior, so the problem (caused by incorrect code) will manifest itself immediately when you try to access the next object after the mutation has occurred. As programs get more complex and multi-threaded, or even depend on something that third-party code may modify, fragile enumeration code becomes increasingly problematic. Encapsulation and abstraction FTW! :-)
tl;dr
class C
defines a class, just as in Java or C++.
object O
creates a singleton object O
as instance of some anonymous class; it can be used to hold static members that are not associated with instances of some class.
object O extends T
makes the object O
an instance of trait T
; you can then pass O
anywhere, a T
is expected.
- if there is a
class C
, then object C
is the companion object of class C
; note that the companion object is not automatically an instance of C
.
Also see Scala documentation for object and class.
object
as host of static members
Most often, you need an object
to hold methods and values/variables that shall be available without having to first instantiate an instance of some class.
This use is closely related to static
members in Java.
object A {
def twice(i: Int): Int = 2*i
}
You can then call above method using A.twice(2)
.
If twice
were a member of some class A
, then you would need to make an instance first:
class A() {
def twice(i: Int): Int = 2 * i
}
val a = new A()
a.twice(2)
You can see how redundant this is, as twice
does not require any instance-specific data.
object
as a special named instance
You can also use the object
itself as some special instance of a class or trait.
When you do this, your object needs to extend some trait
in order to become an instance of a subclass of it.
Consider the following code:
object A extends B with C {
...
}
This declaration first declares an anonymous (inaccessible) class that extends both B
and C
, and instantiates a single instance of this class named A
.
This means A
can be passed to functions expecting objects of type B
or C
, or B with C
.
Additional Features of object
There also exist some special features of objects in Scala.
I recommend to read the official documentation.
def apply(...)
enables the usual method name-less syntax of A(...)
def unapply(...)
allows to create custom pattern matching extractors
- if accompanying a class of the same name, the object assumes a special role when resolving implicit parameters
Best Answer
This is actually in my opinion an appropriate use case for 2.10 macros: you want access to information that you know the compiler has, but isn't exposing, and macros give you a (reasonably) easy way to peek inside. See my answer here for a related (but now slightly out-of-date) example, or just use something like this:
Now we can write the following:
And this is all perfectly safe—you'll get a compile-time error if you ask for values of a type that isn't sealed, has non-object children, etc.