Полезные факты о языке программирования Objective-C

    Я уже 2 года занимаюсь разработкой приложений под iOS и в этой статье мне захотелось представить те факты, которые показались мне интересными и полезными. Буду рад, если вы так же поделитесь своими знаниями в комментариях. В следующей статье хотелось бы собрать подобные факты о Foundation Kit.

    .m


    Расширение .m (message) ввели для того чтобы выделить ключевую особенность Objective-С. По сути, мы не вызываем методы у класса, мы отправляем сообщение объекту, после чего происходит диспетчеризация в ходе которой диспетчер методов Objective-C ищет нужный класс и вызывает у него необходимый метод.

    NS


    Префикс NS обозначает Next Step. Он возник еще в те времена, когда не было Cocoa, а фрейворк назывался NextSTEP и был продуктом NeXT Software. Apple купила эту компанию в 1996 году и чтобы не нарушать обратную совместимость кода продолжила использовать этот префикс.


    #include & #import


    В Си используют include guard во избежании рекурсивного включения заголовочных файлов.
    В Objective-C эта функция встроена в #import.

    BOOL


    BOOL в Objective-C в действительности представляет собой typedef для signed char, который использует 8 бит памяти. YES определено как 1, а NO – как 0 с применением директивы #define.
    Objective-C не рассматривает BOOL как истинно логический тип, который в состоянии принимать только два значения — YES и NO. Компилятор рассматривает BOOL как 8-битовое число, а значение YES и NO представляют собой всего лишь соглашения. Это приводит к трудноуловимым ошибкам: если вы будете недостаточно внимательны и присвоите переменной типа BOOL значение более одного байта, такое как short или int, то для логического значения будет использован только младший его байт. Если этот байт случайно окажется нулевым (например в случае 8960 (шестнадцатеричное значение 0x2300)), то значение BOOL тоже будет нулевым, т. е. значение NO. По этой причине не стоит ни когда сравнивать значение типа BOOL непосредственно с YES.

    -(void)myMethod


    Ведущее тире указывает, что это объявление метода Objective-C. Это тире предназначено для того чтобы отличить объявление метода от прототипа функции.

    isa


    Класс NSObject объявляет одну переменную экземпляра isa для того чтобы в ней хранить указатель на класс объекта. Переменная носит такое имя, поскольку она фактически определяет отношение “is a” (является) между подклассом и надклассом, т.е. Rectangle является Shape, и Circle является Shape.

    База плюс смещение


    При работе компилятор использует механизм «база плюс смещение». Имея базовый адрес объекта – т.е. место расположения в памяти первого байта первой переменной экземпляра – компилятор может найти все остальные переменные экземпляра, добавляя к данному базовому адресу определенные смещения.

    Эта проблема решена в новой 64-битной системе времени выполнения Objective-C, где для определения местоположения переменных экземпляра используется косвенность.

    set & get


    Все знают что префикс set используется для методов установки или сеттеров. Но почему мы не используем get для геттеров? Слово get в Cocoa имеет особый смысл: в имени метода Cocoa оно обозначает, что метод возвращает значение посредством указателя, переданного в качестве параметра. Например, NSData имеет метод getBytes:.

    @ class


    Импорт заголовочного файла устанавливает строгую зависимость между заголовочным файлом и импортирующим его файлом с исходным текстом. Если заголовочный файл изменяется, то все файлы, зависящие от него, требуют перекомпиляции. Иногда это требует большое количества времени. Objective-C дает нам возможность минимизировать перекомпиляцию вызванную зависимостями. Вопросы зависимостей появляются из-за того, что компилятор Objective-C для успешной работы нуждается в определенной информации. Временами ему требуется полная информация о классе, такая как схема размещения переменных экземпляра и цепочка его наследования, а иногда достаточно только имени класса без его полного определения.
    Ключевое слово @class является опережающей ссылкой (forward reference). Это способ сказать компилятору «доверься мне; ты знаешь что это такой класс есть, а больше тебе пока знать и не надо».

    Информация взята из книги «Objective-C 2.0 и программирование для Mac» Марк Далримпл, Скотт Кнастер.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 37

      0
      А компилятор не предупреждает когда присваиваешь в BOOL какой-нибудь int?
        0
        Ворнинг будет. Но многие ли их читают? Увы, нет.
          +8
          Ну накопление ворнингов в проекте — достаточно плохая штука. Мое мнение — увидел warning, попробуй устранить. Часто они на действительно серьезные проблемы же указывают. А тут совсем просто — сделал приведение типа и все…

          По статье: для любого человека, мало-мальски читавшего официальную документацию она (статья) может показаться ну совсем уж капитанской =)
            +1
            Ну накопление ворнингов в проекте — достаточно плохая штука.

            Я стараюсь их убивать на корню (с анализом «что не так», разумеется), да ещё и статическим анализатором регулярно прохожусь.

            она (статья) может показаться ну совсем уж капитанской

            О том же и я написал, чуть ниже =)
              0
              Я стараюсь их убивать на корню (с анализом «что не так», разумеется), да ещё и статическим анализатором регулярно прохожусь

              Вы про стандардный Analyzer или есть что-то по лучше?
                +1
                Да, я пользуюсь стандартным. Он вполне неплох, мне кажется.
              +1
              Приведение типа имеет тот же самый эффект. Обрежутся лишние байты и всё.
              Вот тут: govnokod.ru/12177 интересное обсуждение вопроса возникло. Про nil/NULL/NSNull там на сайте тоже где-то было, кстати.
              +1
              ну наверно они не программисты, раз ворнинги оставяют и не читают. это же бред.
                0
                Увы, таких людей много. Для них и был создан -Werror.
            0
            "… системе времени выполнения"?
            прошу поправить.
              +5
              Статья простовата, ожидал большего. Мне кажется, было бы полезнее написать о литералах (и boxed-литералах), о mutable/immutable, о структуре класса в ObjC, о богатейшем рантайме.
                0
                Нормально, речь ведь идет о простых и интересных фактах а не о глубинных особенностях рантайма или синтаксическом сахаре, для этого лучше документация подходит.

                Я, например, никогда не задумывался почему именно .m, или почему объявление методов начинается с "-" или "+". О соглашении насчет «get» тоже было интересно узнать.
                  0
                  Кстати, можно было бы пройтись ещё и по соглашениям языка, например про соглашения о функциях, начинающихся с «alloc», «new» и прочего.
                +2
                Ведущее тире указывает, что это объявление метода Objective-C. Это тире предназначено для того чтобы отличить объявление метода от прототипа функции.

                А я всегда думал что это для того чтобы отличить метод экземляра от метода класса, вот я дурак…
                  +4
                  Вы об отличиях "-" и "+", а автор пишет о том, с какого перепугу вообще понадобились эти ±, потому что обычное объявление методов уже «занято» чистым C. Можно было, конечно, сделать вроде С++ и писать «void Class::Method(void) {}» или другие соглашения принять, но авторы выбрали такой подход.
                  +5
                  Поздравляю автора с тем что он осилил первую главу любого учебника по obj-c. Очередной пост ради поста от автора-рака.
                  +1
                  Вот вы написали про get/set — а кто мне мешает самому написать свой getter для любого объекта/переменной? И про @property/@synthesize не слова. Так же про .h вы умолчали а это тоже основа Objective-C (в части про import) что мы посылаем abstract reference методов класса.
                    0
                    Вот вы написали про get/set — а кто мне мешает самому написать свой getter для любого объекта/переменной?

                    Извиняюсь, не понял вопроса. Ни кто не мешает. Я написал: «Слово get в Cocoa имеет особый смысл: в имени метода Cocoa оно обозначает, что метод возвращает значение посредством указателя, переданного в качестве параметра.»
                    Поэтому не стоит его использовать в других случаях, если вы конечно не хотите запутать программистов, которые будут читать ваш код.
                      0
                      Хорошо я выражусь по другому — getter/setter почти во всех языках дает reference к адресу обьекта/параметра (pass-by-reference) против pass-by-value. У меня в этим проблем нету. Тока я говорю что это не совсем полное описания get/set — ведь get/set это modular methods в который @property/@synthesize входит как один из вариантов обращения к обьектам/параметрам.
                        0
                        Пардон, это mutator methods — не успел поменять
                          +1
                          Да, написать можно было больше и конкретно про getter и setter, но мне показалась интересной информация о том где мы используем префикс get, хотя казалось бы логичным использовать его для getter'а. Возможно это не так важно как информация о @property/@synthesize, но я не ставил целью собирать важную информацию — она есть везде. Я хотел в одном месте собрать интересные и полезные факты.

                          К сожалению, на мой призыв добавить другие факты в комментариях ни кто не отозвался.
                            0
                            А чем вам не интересный факт — не надо писать свои mutator methods и access через classIntance.reference (e.g. self.object)
                              0
                              Или как на счет sugar syntax для get?

                              #import

                              interface Photo: NSObject {
                              NSString* caption;
                              NSString* photographer;
                              }

                              — caption;
                              — photographer;

                              end

                              против

                              #import

                              interface Photo: NSObject {
                              NSString* caption;
                              NSString* photographer;
                              }

                              — (NSString*) caption;
                              — (NSString*) photographer;

                              end

                              Я знаю что первый возвращаем instance id method, но так писать то можно. (У меня таги source не работают так что пардон за такой стиль написания).
                                0
                                исходник побило очень =\
                                Что Вы имеете ввиду под «компиляция типа налету»?
                                  0
                                  У меня теги в комментариях не работают, еще раз извиняюсь.
                                  Я иммел в виду runtime.

                                  Когда делаем обращение к get в другом классе можно или так вызвать
                                  NSString *ns = photoInstance.caption; // photoInstance.caption будет id, и в runtime id перебьет под NSString * и здесь уже чисто риск программиста а что же было такое id (компилятор пропустит).
                                  или через cast
                                  NSString *ns = (NSString *) photoInstance.caption; //cast в NSString *
                                    0
                                    Рантайм ничего не «перебьет». Обычное присваивание указателей. То что фактический тип остается на совести программиста — верно.

                                    Кастовать id в любой другой тип смысла нет вообще, id по определению присваивается любому объекту без ворнинга и каста.

                                    И еще, касты в Objective C, как и в C — только для успокоения компилятора. Никаких проверок типов при кастовании не происходит (в отличие например от каста в Java или dynamic_cast в C++). Но в случае id его даже «успокаивать» не надо.

                                    И где тут аналогия со Scala? Там есть только compile-time вывод типа, в рантайме с типами ничего не происходит, jvm же.
                                      0
                                      «перебьет» -> «superclass to subclass typecast» можно использовать как обозначание?

                                      id — superclass, тоже что Object в Java.

                                      И еще, касты в Objective C, как и в C — только для успокоения компилятора. Никаких проверок типов при кастовании не происходит (в отличие например от каста в Java или dynamic_cast в C++). Но в случае id его даже «успокаивать» не надо. — да вы правы, не знал. Тогда опять же — все на совести программиста.

                                      pattern matching в Scala разве не runtime?
                                        0
                                        «перебьет» -> «superclass to subclass typecast» можно использовать как обозначание?

                                        можно, но фактически в рантайме никаких дополнительных действий из-за каста не будет, как было присваивание указателя, так и останется.

                                        id не суперкласс, id — «магический тип», которому можно слать любые сообщения и присваивать любым объектным переменным без каста (проделайте-ка тоже самое с Object в Java).

                                        Суперкласс один — NSObject.

                                        Ну в ходе patterin matching'а наверное могут осуществляться проверки instanceof и касты, да.
                                          0
                                          id не суперкласс, id — «магический тип», которому можно слать любые сообщения и присваивать любым объектным переменным без каста (проделайте-ка тоже самое с Object в Java).

                                          Век живи веч учись — спасибо, буду знать. Да про Java знаю, я с нее начинал.

                                          В Scala кажеться вот в этом случии тоже в runtime x получает :Int

                                          type Set = Int => Boolean
                                          def union(s: Set, t: Set): Set = x => s(x) || t(x)
                                            0
                                            Тут x — placeholder аргумента возвращаемой методом функции. Его тип выводится компилятором во время компиляции, и аргументы всех вызовов функторов типа Set проверяются опять же во время компиляции.
                                              0
                                              Понятно. Буду знать.
                    0
                    всегда думал что .m — это "(m)odule" или «i(m)plementation», а оказывается вот оно что.
                      +1
                      Все намного проще — Брэд Кокс (создатель Objective-C) на вопрос от причинах выбора расширения ".m" ответил так:
                      «Because .o and .c were taken. Simple as that.»
                        0
                        Если честно, я не знаю что бы ответил на его месте, если бы мне такие вопросы задавали по емейл. Наверное, этот парень был не первый кто спросил.

                        Но это очень интересно разобраться в чем-то до последнего бита.
                          0
                          Мне кажется, что у Брэда особо не было причин специально говорить неправду. Ответить что-то типа «Yes, .m is for 'messages'.» было бы явно не сложнее.
                          Хотя конечно я не знаю, как сильно люди задолбали его этим вопросом :)
                      +4
                      копипаст из книги… отличные статьи пошли…

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое