13

Which of the following is faster and why?

CGFloat sum = 0;
for (UIView *v in self.subviews)
    sum += v.frame.size.height;

or

CGFloat sum = [[self.subviews valueForKeyPath:@"@sum.frame.size.height"] floatValue];
Monolo
  • 18,205
  • 17
  • 69
  • 103
L_Sonic
  • 594
  • 6
  • 22
  • I doubt that you will notice a significant difference for less than 100 subviews. For large arrays I made a similar comparison here: http://stackoverflow.com/a/15931719/1187415, where fast enumeration turned out to be the fastest. – Martin R May 02 '13 at 11:53
  • 3
    Why don't you try it yourself? Should be easy to investigate... Personally I would go with the solution that is more readable. – David Rönnqvist May 02 '13 at 11:54
  • the kvc thing is much more readable but it the fast enumeration is faster I will go with that. But right now I have no time to test... :( – L_Sonic May 02 '13 at 12:00
  • Get a timestamp before the action and calculate the time. I'm not sure but probably fast enum will get more power if you are on multicore device it probably use dispatch_apply under the hood (but this just my opinion I have no proof about it) – Andrea May 02 '13 at 12:03
  • 1
    @Andrea: Fast enumeration does not start multiple threads, but you can use *block enumeration* with the NSEnumerationConcurrent option (also tested here: http://stackoverflow.com/a/15931719/1187415, sorry for the self-promotion :-) – Martin R May 02 '13 at 12:06
  • @MartinR no self promotions they very useful test.. THX :-D – Andrea May 02 '13 at 12:14
  • 1
    @MartinR yes, but is accessing UI elements advisable outside of the main thread, even if it is only to get their height? – Monolo May 02 '13 at 12:15
  • @Monolo: That is a valid point! – Martin R May 02 '13 at 12:17
  • The second code fragment using key paths won't work because `frame.size.height` is not a valid key path. The `frame` method of a `UIView` returns a `CGRect` which is a C struct, not an Objective-C object. – JeremyP May 02 '13 at 12:21
  • @JeremyP: You are right. Interestingly, `[self.subviews valueForKeyPath:@"@sum.layer.frame.size.height"]` does work, compare http://stackoverflow.com/a/15657943/1187415. – Martin R May 02 '13 at 12:28
  • @MartinR That's interesting to know. They probably shouldn't have done that because it confuses the distinction between a key path and C struct members. – JeremyP May 02 '13 at 12:48
  • Thank you for you insights. I am not using this in my code. I have a category that summarizes this "@sum.frame.size.height" into "@sum.height" which is an objC member. Thank you all. All that answers my question. I will run some tests to see how much faster it is before I take the final decision. – L_Sonic May 02 '13 at 14:34
  • 4
    Do you really have a performance problem here? If you do, are you sure it isn't an architectural issue? – bbum May 02 '13 at 17:31
  • No performance issue. I just find it cleaner to use KVC and I had the question about performance as Later on it might be important. – L_Sonic May 13 '13 at 13:28
  • This is an old question, but today I found out the hard way that KVC is *way* slower than Fast Enumeration, especially if processing arrays with 1000s of records (like I was). – Z S Jun 11 '21 at 20:50

3 Answers3

14

Really, a lot of how elegant (or clever) a language is comes down to how well it avoids loops. for, while; even fast enumeration expressions are a drag. No matter how you sugar-coat them, loops will be a block of code that does something that is much simpler to describe in natural language.

"get me the average salary of all of the employees in this array",

double totalSalary = 0.0;
for (Employee *employee in employees) {
  totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];

versus...

Fortunately, Key-Value Coding gives us a much more concise--almost Ruby-like--way to do this:

[employees valueForKeyPath:@"@avg.salary"];

KVC Collection Operators allows actions to be performed on a collection using key path notation in valueForKeyPath:.

Any time you see @ in a key path, it denotes a particular aggregate function whose result can be returned or chained, just like any other key path.

Fast Enumeration is Faster then KVC.

Hope it helps you.

Nishant Tyagi
  • 9,893
  • 3
  • 40
  • 61
  • 1
    Using KVC for such tasks introduces fragility into your codebase. Specifically, the compiler can't validate the KVC expression, thus moving a potential bug from being detected at compile time to only failing at runtime if that codepath happens to be executed. As well, the entire first paragraph is opinion that doesn't add anything to the answer. – bbum May 02 '13 at 17:17
6

First, your KVC expression won't work; KVC can't be used to retrieve struct members (save for very limited cases where a class has a special implementation of valueForKey: that does special processing).

Secondly, this smells like premature optimization.

Have you actually quantified a performance problem related to this code?

If not, you are wasting your time "optimizing" this code path.

That aside, using KVC for such operations adds considerable fragility to your code. The compiler cannot verify the expression passed to valueForKey: and, thus, what would be a compile time error or warning is now an error at runtime that'll only be found if you execute that codepath.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • All the KVC expressions can't be verified by the compiler. is that means that we should use KVC if we can't not user other things ? – samir May 02 '13 at 20:05
  • In general, yes. Certainly, KVC has uses, but limiting use of KVC, introspection based programming, etc... contributes greatly to codebase maintainability. – bbum May 02 '13 at 20:38
0

Also have a look in the Apple docs:

Though key-value coding is efficient, it adds a level of indirection that is slightly slower than direct method invocations. You should use key-value coding only when you can benefit from the flexibility that it provides.

Key Value Coding Docs

TheEye
  • 9,280
  • 2
  • 42
  • 58