Улучшаем производительность труда. Макросы и литералы objective-c


    Всем привет!
    Не секрет, что мы, программисты, минимум половину времени тратим на написание кода. Логично было бы это время как можно лучше сократить.
    Однажды, когда я в очередной раз написал конструкцию NSString *, я подумал, что пора что-то менять.
    Как же можно упростить себе жизнь, разрабатывая под iOS?
    Статья является расширением другой статьи



    Литералы


    Не так давно, Objective-c стал поддерживать большое количество литералов для уменьшения объема кода.
    Все примеры будут актуальны для Xcode 4.5.
    Итак, заменяем следующие конструкции на сжатые аналоги:
    [NSArray arrayWithObjects:@"1", @"2", @"3", nil] => @[@"1", @"2", @"3"];
    [NSDictionary dictionaryWithObjects:@"1", @"2", @"3", nil forKeys:@"one", @"two", @"three", nil] => @{@"one" : @"1", @"two" : @"2", @"three" : @"3"}
    

    Следите, чтобы объекты были не nil, иначе получите исключения.
    Продолжаем
    [NSNumber numberWithChar:'A'] => NSNumber *theA = @'A';          
    
    //Целые
    [NSNumber numberWithInt:42] =>  NSNumber *fortyTwo = @42;
    [NSNumber numberWithUnsignedInt:42U] => NSNumber *fortyTwoUnsigned = @42U; 
    [NSNumber numberWithLong:42L] => NSNumber *fortyTwoLong = @42L;
    [NSNumber numberWithLongLong:42LL] => NSNumber *fortyTwoLongLong = @42LL;
    
    //С плавающей точкой
    [NSNumber numberWithFloat:3.141592654F] => NSNumber *piFloat = @3.141592654F;
    [NSNumber numberWithDouble:3.1415926535] => NSNumber *piDouble = @3.1415926535;
    
    //Булевые
    [NSNumber numberWithBool:YES] => NSNumber *yesNumber = @YES;
    [NSNumber numberWithBool:NO] => NSNumber *noNumber = @NO;
    

    Аналогично можно задавать объекты из переменных. Просто возьмите их в скобки @()

    Дальше интереснее!
    NSMutableArray * array = [@[@"1", @"2", @"3"] mutableCopy] ;
    [array objectAtIndex:0] => array[0];
    array[0] = @"5";
    
    NSMutableDictionary *dictionary = [@{@"one" : @"1", @"two" : @"2", @"three" : @"3"} mutableCopy];
    NSString *key = @"one";
    oldObject = dictionary[key];
    dictionary[key] = newObject;
    

    В общем, тонна синтаксического сахара.

    Макросы


    Можно по-разному относиться к макросам, однако же, они могут хорошо ускорить разработку.
    Привожу список интересных макросов, подходящих для разработки:
    #define ApplicationDelegate                 ((MyAppDelegate *)[[UIApplication sharedApplication] delegate])
    #define UserDefaults                        [NSUserDefaults standardUserDefaults]
    #define SharedApplication                   [UIApplication sharedApplication]
    #define Bundle                              [NSBundle mainBundle]
    #define MainScreen                          [UIScreen mainScreen]
    #define ShowNetworkActivityIndicator()      [UIApplication sharedApplication].networkActivityIndicatorVisible = YES
    #define HideNetworkActivityIndicator()      [UIApplication sharedApplication].networkActivityIndicatorVisible = NO
    #define NetworkActivityIndicatorVisible(x)  [UIApplication sharedApplication].networkActivityIndicatorVisible = x
    #define NavBar                              self.navigationController.navigationBar
    #define TabBar                              self.tabBarController.tabBar
    #define NavBarHeight                        self.navigationController.navigationBar.bounds.size.height
    #define TabBarHeight                        self.tabBarController.tabBar.bounds.size.height
    #define ScreenWidth                         [[UIScreen mainScreen] bounds].size.width
    #define ScreenHeight                        [[UIScreen mainScreen] bounds].size.height
    #define TouchHeightDefault                  44
    #define TouchHeightSmall                    32
    #define ViewWidth(v)                        v.frame.size.width
    #define ViewHeight(v)                       v.frame.size.height
    #define ViewX(v)                            v.frame.origin.x
    #define ViewY(v)                            v.frame.origin.y
    #define SelfViewWidth                       self.view.bounds.size.width
    #define SelfViewHeight                      self.view.bounds.size.height
    #define RectX(f)                            f.origin.x
    #define RectY(f)                            f.origin.y
    #define RectWidth(f)                        f.size.width
    #define RectHeight(f)                       f.size.height
    #define RectSetWidth(f, w)                  CGRectMake(RectX(f), RectY(f), w, RectHeight(f))
    #define RectSetHeight(f, h)                 CGRectMake(RectX(f), RectY(f), RectWidth(f), h)
    #define RectSetX(f, x)                      CGRectMake(x, RectY(f), RectWidth(f), RectHeight(f))
    #define RectSetY(f, y)                      CGRectMake(RectX(f), y, RectWidth(f), RectHeight(f))
    #define RectSetSize(f, w, h)                CGRectMake(RectX(f), RectY(f), w, h)
    #define RectSetOrigin(f, x, y)              CGRectMake(x, y, RectWidth(f), RectHeight(f))
    #define DATE_COMPONENTS                     NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
    #define TIME_COMPONENTS                     NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit
    #define FlushPool(p)                        [p drain]; p = [[NSAutoreleasePool alloc] init]
    #define RGB(r, g, b)                        [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0]
    #define RGBA(r, g, b, a)                    [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
    


    Засуньте их в один заголовочный файл и подключите в <project_name>-Prefix.pch. После этого используйте их в коде. Весьма удобно.

    Примечание: пользователь iStyx заметил, что параметры в макросах неплохо бы оборачивать в скобки, например так
    #define RGB(r, g, b)   [UIColor colorWithRed:(r)/255.f green:(g)/255.f blue:(b)/255.f alpha:1.f]
    


    Переопределения типов


    Прежде всего, переопределения типов я использую, чтобы отойти от звезд и префиксов NS. Главное, попривыкнуть к ним.
    Вот несколько, например:

    typedef NSString*                           String;
    typedef NSNumber*                           Number;
    typedef NSArray*                            Array;
    typedef NSMutableArray*                     MutableArray;
    typedef NSDictionary*                       Dictionary;
    typedef NSMutableDictionary*                MutableDictionary;
    


    Заключение


    В общем, поверхностное ознакомление мы осуществили. Упрощайте себе работу, и не пишите лишних символов (но не забывайте о логичных названиях переменных!!!)

    Удачи в разработке.

    P.S. Пользователь Pilot34 поделился своей библиотекой вспомогательных инструментов.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 30

      +5
      Вы это серьезно на счет макросов?.. facepalm.jpg

      1. Пишите код, выделяете его и перетаскиваете в окошечко справа, предварительно выбрав там CodeSnippets



      2. Задаете параметры для вашего сниппета



      3.…

      4. Profit!!1
        0
        Полезно, спасибо. Но макросы могут быть удобнее в других задачах.
          0
          Вот именно что в других!
            +1
            Выбранный вами подход значит постоянное повторение кода.
            Макросы же решают одну задачу все время одинаково в рамках одного проекта.
              0
              Сниппеты позволяют мне не тратить время на многочисленные drawRect, layoutSubviews и т.п., при чем во всех проектах, а макросы нужно подключать во все проекты, подключать хедеры, в общем кучу лишних телодвижений, ради того чтобы кагбы упростить себе жизнь.
              Я считаю что подход с макросами имеет место быть, но вариант со сниппетами более гибкий и удобный.
          0
          все-таки когда можно обойтись макросом или функцией — это лучше сгенеренного кода. Менять потом проще.
          0
          [array objectAtIndex:0] => array[0];
          array[0] = @«5»;

          Вот эта штука (и для nsdictionary тоже) будет работать начиная в ios 6+

          Плюс, категорически не согласен с заменой типов, какая-то Java получилась. Остальное ок.
            +2
            Вот эта штука (и для nsdictionary тоже) будет работать начиная в ios 6+

            Нет. Будет работать и в младших версиях.
              0
              Нет, не будет. Читайте во что оно транслируется компилятором и смотрите с какой версии iOS эти методы доступны.
                +3
                Нет, будет. Просто проверьте.
                  +1
                  Хм. Сработало. Когда смотрел доки, сразу полез в хедеры увидел, что objectAtIndexedSubscript: доступен с 6.0. Проверять не стал, зря.

                  Признаю неправоту. Другой вопрос — есть ли у этого добра минимальная версия? Или оно при невозможности вызова objectAtIndexedSubscript вызывает objectAtIndex?
                    0
                    Я пробовал на 4.3 на железе — также работает. Видимо, при указании низшего target транслируется в другой код.
                      0
                      Вот ответ. В случае, если в проекте используется ARC, приведенный метот будет работать в 4.3+. В противном случае, можно классы допилить категориями или руками слинковать arclite.

                      Рекомендую дополнить статью.
                        0
                        Да ну нет же :)
                        Проект у меня как раз без ARC, и там все работает
                          +1
                          Сдаюсь :)
                • UFO just landed and posted this here
                    0
                    Да, меня смутило, что на сайте clang'а написано, что транслируется оно objectAtIndexedSubscript:, который доступен только в iOS 6+.
              • UFO just landed and posted this here
                +4
                Не забывайте, что параметрами макросов могут быть выражения, поэтому в самих макросах надо их оборачивать в скобки.

                Например, в этом макросе:
                #define RGB(r, g, b)   [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0]

                если я вызову его так:
                RGB(shift+r, shift+g, shift+b)

                получится черт-те-что.

                Поэтому, надо переписать его так (обратите внимание, что colorWithRed:green:blue:alpha: принимает не double, а CGFloat):
                #define RGB(r, g, b)   [UIColor colorWithRed:(r)/255.f green:(g)/255.f blue:(b)/255.f alpha:1.f]

                Ну, и, с остальными макросами та же беда.
                  +2
                  Ну и есть еще одна проблема, если передать в макрос что-то типа i++, а макрос наш кривой и подставит эту переменную два раза, то на выходе будет совсем не то чего мы хотели. Для того чтобы этого избежать, нужно создавать локальные переменные, и задавать им переданные макросу значения.
                    +2
                    Ну, такую проблему можно решить, например, так (NSObjCRuntime.h):
                    #define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a<__b ? __a : __b; })

                    #define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a<__b ? __b : __a; })
                      0
                      Именно, просто лень было искать этот код)
                  +3
                  Имхо, вместо ViewWidth ViewHeight гораздо удобнее категории:

                  — (CGFloat)width
                  {
                  return self.size.width;
                  }

                  — (void)setWidth:(CGFloat)width
                  {
                  CGRect newFrame = self.frame;
                  newFrame.size.width = width;
                  self.frame = newFrame;
                  }

                  — (CGFloat)height
                  {
                  return self.size.height;
                  }

                  — (void)setHeight:(CGFloat)height
                  {
                  CGRect newFrame = self.frame;
                  newFrame.size.height = height;
                  self.frame = newFrame;

                  }


                  И т.п.
                  Я все свои вспомогательные штуки собрал в github.com/pilot34/P34Utils, может кому-то еще что-то удобно будет позаимствовать. Поключается как CocoaPod.
                    0
                    Ага, и каждый новый девелопер, вынужденный работать с Вашим кодом, будет обязан выучить макросы. И это я еще не говорю о возможных name conflicts из-за имен макросов и переопределений типов без префиксов.
                      0
                      И да, иногда utility-методы все же нужны, и лучше имхо их делать inline-функциями. Как минимум это дает проверку типов на этапе компиляции и лучшую читаемость, чем макросы (особенно если макросы многострочные).
                      0
                      Когда они упростят уже [NSString stringWithFormat:@«sound%d.mp3», pageNumber];
                      до чего-то типа @{"%d",pageNumber}? (Уж не знаю как лучше скобки ставить в данном примере)
                        0
                        Идеально было бы как в ruby

                        str = "sound#{page_number}.mp3"
                        
                          +1
                          #define SPRINTF(format, args...) [NSString stringWithFormat:(format), args]

                          NSString *str = SPRINTF(@"%@ %.2f [%d]", @"string", 3.1415f , 2*3*7);
                          NSLog(@"%@", str);
                          

                          string 3.14 [42]
                        +1
                        Т.к. меня упомянули в самом посте, пришлось в README.markdown примеры написать, совесть заела :)

                        Only users with full accounts can post comments. Log in, please.