12/08/2018, 14:28

Measure cyclical complexity of Objective-C

There are vaious articles about reviewing Objective-C code, but because of there are so many "if-else nest" inside the code so i wanted to measure the cyclical complexity of those nests For short, because if and switch blocks in our code create many logic branches, so CCN is a number implies to ...

There are vaious articles about reviewing Objective-C code, but because of there are so many "if-else nest" inside the code so i wanted to measure the cyclical complexity of those nests

For short, because if and switch blocks in our code create many logic branches, so CCN is a number implies to that logic branches.

If CCN is less than 10 then the code structure is good. The structure is not good if CCN is more than 10.

But for my personal point of view, it is harder and harder to follow the code that has a more-than-5 in terms of CCN (code becomes dark)

Related Articles

  • https://en.wikipedia.org/wiki/Cyclomatic_complexity

It is ok if we measure by OCLint (http://oclint.org/) while developing, but this time I just wanted to measure so i did not use OCLint

Measure the CCN of Objective-C by terryyin/lizard (https://github.com/terryyin/lizard)

There are so many tools for us to try, but i did search on Github and decided to use terryyin/lizard this time

I think python is already installed on your Mac so we can install it by pip

$ pip install lizard

Actual Measurement

Pods and gem can also be measured, so that we have to navigate to the directory that we want to measure and run the lizard command

$ cd PROJECT_ROOT/PROJECT
$ lizard -Tcyclomatic_complexity=10
================================================
  NLOC    CCN   token  PARAM  length  location
------------------------------------------------
       3      1     19      0       4 application: didFinishLaunchingWithOptions:@18-21@./AppDelegate.m
       2      1      9      0       4 applicationWillResignActive:@24-27@./AppDelegate.m
       2      1      9      0       4 applicationDidEnterBackground:@30-33@./AppDelegate.m
       2      1      9      0       3 applicationWillEnterForeground:@36-38@./AppDelegate.m
       2      1      9      0       3 applicationDidBecomeActive:@41-43@./AppDelegate.m
       2      1      9      0       3 applicationWillTerminate:@46-48@./AppDelegate.m
       5      1     35      2       5 main@12-16@./main.m
       3      1      8      0       4 viewDidLoad@17-20@./ViewController.m
       3      1      8      0       4 didReceiveMemoryWarning@23-26@./ViewController.m
5 file analyzed.
==============================================================
NLOC    Avg.NLOC  AvgCCN  Avg.token  function_cnt    file
--------------------------------------------------------------
      3       0.0     0.0        0.0         0     ./AppDelegate.h
     17       2.2     1.0       10.7         6     ./AppDelegate.m
      5       5.0     1.0       35.0         1     ./main.m
      2       0.0     0.0        0.0         0     ./ViewController.h
     10       3.0     1.0        8.0         2     ./ViewController.m

=============================================================================================
No thresholds exceeded (cyclomatic_complexity > 10 or length > 1000 or parameter_count > 100)
==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
        37       2.7     1.0       12.8        9            0      0.00    0.00

This is the result for a brand new project with no code inside (no problem found)

Put a large amount of if blocks and measure

I put a really bad code like this:

- (void)viewDidLoad {
    [super viewDidLoad];
    if (true) {
        if (true) {
            if (true) {
                if (true){
                    if (true){
                        if (true){ }
                    }
                }
            }
        }
    } else {
        if (true) { }
    }
    if (true) { }
    if (true) { }
    if (true) { }
    if (true) { }
}

Run lizard

$ lizard -Tcyclomatic_complexity=10
... 略 ...

=========================================================================================
!!!! Warnings (cyclomatic_complexity > 10 or length > 1000 or parameter_count > 100) !!!!
================================================
  NLOC    CCN   token  PARAM  length  location
------------------------------------------------
      21     12     77      0      22 viewDidLoad@17-38@./ViewController.m

CCN is calculated equals to 12, and a warning added too

We are able to measure the code quality now (if CNN is counted one of the criterias)

How to avoid this?

We can return sooner, and break method into smaller blocks to get rid of building a nest in our code

Does it really "not matter" if the code has CNN less than 10?

Following is the CNN-equals-7-code looks like:

- (void)viewDidLoad {
    [super viewDidLoad];
    if (true) {
        if (true) {
            if (true) {
                if (true){
                    if (true){
                        if (true){ }
                    }
                }
            }
        }
    }
}

6 blocks of if makes the code looks like a mess...

We can get the same result if we set by SwiftLint

The CNN will be getting higher in Swift if we add more and more switch case of enum

========================================================================================
!!!! Warnings (cyclomatic_complexity > 5 or length > 1000 or parameter_count > 100) !!!!
================================================
  NLOC    CCN   token  PARAM  length  location
------------------------------------------------
      18     15    127      1      18 hoge@134-151@./Hoge/Error.swift
      30      8    142      1      31 hoge@28-58@./Hoge/Message.swift
      11      6    117      0      11 hoge@59-69@./Hoge/Controller.swift
0