objective-C как первый язык программированияИсточник: habrahabr tricton
Данный пост был задуман после того, как на некоторые важные вопросы не было найдено внятных ответов. Я отнюдь не претендую на то, что стал крутым программистом. Нет, всё ещё впереди, но период высиживания уже пройден. Это статья из цикла "Не умеешь сам - научи другого". В смысле, чтобы что-то лучше понять нужно это что-то, кому-то объяснить. Мопед не мой, эта фраза встречалась мной раньше в публикациях на Хабре. Некоторые вещи очень сложно понять. И люди, которые понимают, обычно не могут объяснить начинающему. Может быть меня тоже это ждёт. Это как разговор взрослого и ребёнка. Практически конфликт поколений. Пока мой уровень не перерос в профессионала, нужно изложить моё текущее видение.
Начало: метод класса и метод экземпляраЧто такое метод? Это набор команд/инструкций или одна команда/инструкция, которая может быть применена к объекту и вызывает в нём необходимые процессы. Если так не совсем понятно, то читайте дальше и поймёте. На самом деле тяжело понять что такое метод класса и метод экземпляра. Все говорят: "со знаком "+" - это метод класса, а вот со знаком "-" - это метод экземпляра. Понятно?" И в ответ слышат: "Да-а, метод класса и метод экземпляра". Я лично сначала понимал только то что они разные, а вот что и когда применять - осталось не понятным. Википедия упорно повторяет то же самое, что мы слышали и читали. В подавляющем большинстве случаев, Вы будете работать с методами экземпляра. Итак, метод экземпляра. Представим, что у нас есть программа, в которой мы будем использовать 2 класса: Primus и Kitchen. Класс для Objective-C, часто можно интерпретировать как объект и наоборот. Но есть классы, которые нельзя назвать объектами. Они называются "протоколы". В некоторых других языках программирования их называют "абстрактные классы". Различие состоит в том, что "протокол" не может иметь подкласс или дочерний объект. Применение протоколов в реальной жизни я затрону в этой статье. Любой класс имеет методы и/или функции. Класс может иметь несколько методов со знаком "+" перед названием метода (метод класса) и несколько методов со знаком "-" перед названием метода (метод экземпляра). Вот те методы, которые со знаком "-" мы активно применяем как в отношении объектов - экземпляров класса, так и отношении самого класса. А методы со знаком "+" можно применять только в отношении самого класса, где этот метод был объявлен.
Что такое экземпляр класса?Звучит гордо, но непонятно. Кто помнит из школьного курса: в геометрии точка - родитель для круга, прямой и прочих фигур, потому что каждая фигура состоит из множества точек. Представим что есть некий класс "Горелка газовая". Он будет родительским для "Примуса" и "Газовой плиты". Для того, чтобы приблизиться к программированию, нужно немного абстрагироваться. В Objective-C есть родительский класс NSObject. В языке программирования он как точка в геометрии. Это идеальный класс: в нём нет ничего лишнего, он ничего конкретного делает, но из него можно создать всё что угодно. Такой вот кирпичик мироздания. Если в Xcode зажать кнопку "Command" и кликнуть на NSObject, то перед Вами откроется содержимое файла NSObject.h. В нём около 200 строк описания различных методов. Есть как методы со знаком "+" перед названием, а также методы со знаком "-". Суть методов со знаком "+" такова что их можно применить только непосредственно к самому NSObject. Таким методом является, например,
и применять его можно только так:
Любой другой объект в Objective-C по определению - потомок NSObject. В нём реализовываются дополнительные методы, которые придают ему черты индивидуальности. Допустим это NSResponder. У этого потомка - NSResponder"а тоже могут быть дочерние объекты. К примеру UIView. Далее по древу наследования можно в качестве примера взять UIScrollView. Потом - UITableView. В каждом случае потомки обрастают дополнительным функционалом. Кроме того, что они умеют делать что-то своё, индивидуальное, они могут делать всё то что может делать их предок. В Ваших программах вы будете брать нужный Вам класс и добавляя в него нужные методы - расширять его функционал. Также можно брать созданный Вами класс и делать его потомка, опять же расширяя функционал и одновременно создавая уникальный инструмент, для решения нужной задачи. Описанный механизм называется - создание подклассов "subclass". Не буду вдаваться в широкие описания методов со знаком "+". Нагляднее будет показать, что применение метода
к любому классу или подклассу выделяет под него нужный объём памяти в ОЗУ компьютера.
Применение метода
инициализирует объект, выделяет для него место в ОЗУ и собственно после этого объект начинает существовать и с ним можно работать.
Эти 2 метода ("alloc" и "init") только что создали объект "object". И данный объект является экземпляром объекта или экземпляром класса NSObject. Если применить эти методы по раздельности
то объект тоже будет создан. Но есть ненулевая вероятность того Вы создадите не тот объект, которому выделили память. Допустим, вызов метода
выделил для него такой адрес в памяти
Так происходит потому что при инициализации, исполняемая среда, всем объектам задаёт значение "nil", чтобы избежать ссылки на место в памяти, заполненное "мусором". Или ссылки на другой конкретный объект, который по факту тоже уже является "мусором", потому что он закончил свой жизненный цикл и уже не "хранится" в памяти, а просто "находится". Для того чтобы понять что такое "мусор", создайте простейшую программку, в которой объявите
не присваивая ей значение, а потом выведите её значение в консоль
Вы будете удивлены тем, чему равняется Ваше "i". Также будет создан экземпляр объекта "isa", который будет рассмотрен позже. Это единственный ненулевой указатель при создании Вашего объекта. Допустим, это ниточка к родительскому объекту, который породит свой экземпляр и даст жизнь нашему объекту. То есть теоретически объект уже есть после применения метода "alloc". Но он не сможет пройти проверку на
потому что он вроде как есть, но в принципе он равен "nil". Не инициализированный объект - это как машина, которую Вы только собираетесь куплить в кредит. Вроде как есть, но она не Ваша. И если не отходя от кассы применить метод "init"
то "Бинго!", и объект принадлежит Вам, с ним можно делать всё что хотите. А вот если к нему применить метод
то инициализироваться может другой объект. Вроде как вы выплатили кредит за машину, но оказалось что не за ту машину и Вам вместо одного автомобиля дают другой. Они на первый взгляд не отличимы друг от друга. Но у них разные "VIN" номера, а значит при проверке на посту "ГАИ" Вам сообщат что авто не Ваш. Ниже будет приведено дополненное объяснение этого явления. Как Вы уже поняли метод "+alloc" - метод класса, а метод "-init" - метод экземпляра. Приведу пример с реальными вещами. Полная реализация метода включает в себя обычно 2 файла с расширениями *.h и *.m. Бывает и другое количество, в зависимости от того, что это за класс и какие цели он преследует. В Вашей программе есть класс "Primus". В файле "Primus.h" объявлены методы:
Также в программе есть класс "Kitchen", где в файле "Kitchen.h", объявлены методы:
В файлах "Primus.m" и "Kitchen.m" все объявленные методы должны быть реализованы, то есть описаны: как и что в каждом методе происходит. Если вы собираетесь в каком-либо классе создавать экземпляр объекта другого класса, то в шапке нужно будет импортировать файл *.h создаваемого объекта, например в шапке файла Kitchen.h мы впишем
То есть можно будет создать класс "Primus" внутри класса "Kitchen". Так как мы импортировали заголовочный файл Primus.h в файл "Kitchen.h", то в классе "Kitchen" нам стали доступны те методы экземпляра класса "Primus", которые объявлены в файле Primus.h. Их можно будет применять либо по отношению к классу, либо по отношению к экземпляру класса "Primus". Для начала создадим экземпляр класса "Primus" внутри класса "Kitchen"
Как видите, мы посылаем метод c "+" не экземпляру класса "myPrimus", а непосредственно классу "Primus". Экземпляр класса "Primus" под названием "myPrimus" создан. В дальнейшем, методы из "Primus.h" со знаком "-" будут применяться к экземпляру класса - "myPrimus". И если захотим создать новый экземпляр класса "Primus", то можно будет опять воспользоваться методом "+hotAsHell". Также в классе "Primus" есть метод
Обычно все методы, название которых начинается с "init", делают то же самое что и метод "init" класса NSObject, только с расширенными возможностями. И в данном случае применение метода "init" в таком ракурсе
создаст новый объект класса "Primus" с уникальными характеристиками. В описанном только что примере был применён метод с аргументами. В данном, конкретном случае, он применяется для создания экземпляра класса "Primus" с чётко заданными характеристиками. По названию характеристик видно, что данный экземпляр класса "Primus" будет работать на газе типа "Propan" и при температуре 750 градусов. Обратите внимание, что названия методов в Objective-C весьма осмысленны. Временами Вы будете поражены их названиями состоящими из 10 слов, но это способствует лучшему понимаю написанного. Грубо говоря код получается самодокумментируемым. Что конечно же не исключает необходимости написания комментариев. Но если взять за правило создавать свои методы, название которых будет нести смысловую нагрузку и таким же образом относиться к созданию переменных, то количество комментариев в коде можно заметно снизить. В отличие от некоторых других языков программирования в Objective-C методы не вызываются, а посылаются объектам. Таким образом, этот ЯП относится к "message oriented language", на русском языке звучит не так строго, поэтому перевод не привожу. Также обратите внимание на то, что все методы которые после посылки объекту создают (возвращают) новый объект имеют в скобочках после "+" или "-" либо конкретное название объекта, экземпляр которого они будут возвращать, либо "id". То есть, по том, какой объект указан в скобочках сразу после знака "+" или "-", можно судить что мы получим после посылки этого метода. Что такое "id"? Это такой тип объекта, который может принимать форму (тип) любого другого объекта. Скажем это "слабый" тип объекта. Не в смысле того, что ему что-то "слабо". Если говорить общепринятыми понятиями, то это слабо типизированный объект. Наличие таких объектов в ЯП делает его слабо типизированным. Также можно в этих самых скобочках обнаружить тип "void".
Что такое "void"?Все говорят: "это когда метод ничего не возвращает". А зачем же он нужен? Применение метода этого типа всё же производит манипуляции с объектами, структурами и цифрами. Также объекты и скалярные величины, попавшие в него в качестве аргументов могут измениться либо вообще исчезнуть. Всё зависит от того, что реализовано внутри каждого конкретного метода. Дело в том, что "void" таки ничего не возвращает… нового. Как и было сказано, то что попавшие в него аргументы могут изменяться сами, либо влиять на другие объекты, манипуляция с которыми производится в методе с "void". Он может даже создавать новые объекты внутри себя, но наружу ничего не возвращается в явном виде. Обычно метод, который возвращает что-то, имеет ключевое слово "return" в самом конце. А метод типа "void", "return" в конце не имеет. Он может его иметь внутри своих собственных циклов или условий, чтобы иметь возможность прервать их в нужном месте. Но не в конце. Метод не создаёт ничего принципиально нового, он просто, работая с указателем на какой-то объект (число, массив, ячейка и т.д.), меняет данные по этому адресу или применяет их для расчёта других данных, которые присваивает другим объектам.
Что такое "self"?Это слово очень часто применяется где попало. Сам факт его применения во всевозможных местах, вносит путаницу в стройные кладовые Ваших знаний. Вернёмся к классам "Primus" и "Kitchen". Возьмём конкретный метод класса "Primus" и рассмотрим как будет выглядеть его реализация в файле "Primus.m". Например это метод.
может быть реализован таким образом:
В фигурных скобках происходит реализация или "имплиментация" метода. В данном случае, реализация метода читается так: если не myPrimus, то выполнить команду
которая в свою очередь читается как: себе инициализировать с газом:gas температурой:750. То есть, метод посылается себе, а не какому-нибудь другому объекту. В данном случае "себе" - это классу "Primus", так как метод находится внутри класса "Primus". Его объявление - в файле Primus.h и реализация в файле Primus.m. Почему был использован знак восклицания "!" в скобках после "if"? В программировании это - знак отрицания. То есть "!=" звучит как "не равно". Этот способ условия удобен своей лаконичностью. В данном примере можно было использовать другое по структуре условие, которое несло бы ту же самую функцию.
Это условие имеет в 2 раза большую длину и нужно полностью читать всю строку, чтобы понять его. Это условие небольшое, но если Вам нужно записать что-то вроде этого
то эта структура
будет в более выигрышной позиции, как по лаконичности так и по физическому размеру. Предыдущая конструкция - банально может просто не влезть в одну строку. Рассмотрим как выглядела бы реализация этого метода в рамках класса "Kitchen". В файле "Kitchen.m" тот же метод будет реализован так:
Видно, что здесь нужно указать, какой объект будет создаваться, как будет называться его экземпляр и потом непосредственно родительскому классу посылать метод. То есть, хитрое слово "self", применяется внутри класса, как будто вы уже создали его экземпляр.
и обращаетесь к экземпляру класса. Только фишка в том, что вы ничего не создавали, и обращаетесь не к экземпляру, а напрямую к классу. Для того, чтобы понять можно ли написать "self" или нельзя - просто представьте себе какой метод какому объекту Вы хотите послать и начните вводить слово "self", но не до конца, например "sel". Xcode предложит несколько вариантов, среди которых будет собственно "self". Слева от него будет описание - какой это класс. Если это именно тот класс, которому вы хотели послать метод (класс внутри которого вы сейчас производите действие), значит используйте "self". Если Xcode указывает, что "self" это не тот объект, который Вам нужен - то используйте имя класса или экземпляра нужного Вам. Также видно что здесь были задействованы аргументы. У каждого аргумента есть тип, который тоже описан в скобках, но после двоеточия. После скобок с типом аргумента идёт имя аргумента. Если в скобках присутствует знак "*" после типа аргумента, то это объект. Если знака "*" нет, то это скалярный тип, например "NSInteger", который по сути является простым "int". Имя аргумента, в данном методе
будет звучать как "gas". Вы обратили внимание, что в реализации не было использовано нормального названия газа? Например "Propan". Дело в том, что в аргументе "gas" как раз и передаётся нужное название газа. Откуда? Мы рассмотрели как реализовуется метод
В нём был задействован метод
В этом методе было 2 аргумента "gas" и "t". В посылке метода объекту, температуру мы указали, а тип газа просто продублировали от метода
То есть этот метод тоже где-то посылается. Рассмотрим поближе метод
Его реализация может выглядеть так
Обратите внимание на значение посылаемого аргумента "propanButan". Именно оно передаётся по всему классу, от метода к методу. Также здесь видно что метод посылается объекту при определённых условиях. Нужно знать что ни один Ваш метод сам по себе не может сработать. Ему нужен "пинок" снаружи. Нажатие кнопки, возникновения какого-либо события или его должен запустить метод протокола, который среда исполнения запускает в нужный ей момент. Данный метод "hotAsHell" посылается лишь после явного вызова, это не системный метод, он тоже где-то должен быть прописан либо чему-то назначен. Теперь рассмотрим метод
Он состоит из типа возвращаемого объекта, типов аргументов, аргументов и селектора. Селектор это то что остаётся от метода когда от него отнять тип возвращаемого объекта, тип аргументов, и аргументы.
именно с двоеточиями. Если аргументов нет, то двоеточий тоже нет, как в случае с "hotAsHell". Часто прийдётся сталкиваться с необходимостью употребить селектор. Теперь вы знаете что употреблять. Немного выше было использовано понятие "super".
Что такое "super"?Рассмотрим реализованный выше метод "hotAsHell". В нём есть такая конструкция
Так принято инициализировать дочерний класс - через инициализацию родительского. Предположим, что класс "Primus" - непосредственный потомок класса "NSObject". Именно тот класс, который стоит по иерархии сразу над тем классом, с которым мы работаем имеет право называться "super". Команда
вызывает метод init экземпляра NSObject. То есть, автоматически создаётся экземпляр прародителя всех объектов - NSObject, с дефолтными (сугубо NSObject'овскими) параметрами. Посылка метода "init" NSObject'y возвращает "self" от NSObject'a. То есть непосредственно экземпляр самого NSObject'a. И этот экземпляр присваивается нашему объекту "Primus". Ведь "self" в данном случае - является именно "Primus"ом". И мы на данном этапе получаем экземпляр объекта "Primus", который ничем не отличается от NSObject"а. Индивидуальные черты ему придаёт наша посылка метода
а также остальные методы, реализованные в рамках данного класса. Конструкция
это просто перестраховка на случай, если что-то пойдёт не так и дефолтный "Primus" не создастся. т.е. метод init класса родителя не выполнится. В таком случае обычно делают
где пытаются исправить положение дел, но, к сожалению, это не тот случай и "else" нам уже ничем не поможет. И здесь я хочу Вам напомнить о неинициализированном объекте из начала статьи. Именно при инициализации экземпляра объекта его суперклассом, происходит назначение места в памяти ему и передача указателя на это место в памяти. И когда проводим проверку
то именно в этот самый момент "self" может получить неожиданный адрес в памяти. То есть, полученный указатель уже будет не "self". Суперкласс не возвратит нам что-то из ряда вон выходящее. Экземпляр объекта будет идентичен тому с которым мы собираемся работать. Но экземпляр именно этого объекта не будет тем экземпляром, который нам нужен. Это настолько редкая ошибка, что Вы можете её не наблюдать очень продолжительное время в своих приложениях. Просто в один прекрасный момент Ваше приложение может начать выдавать экзотические ошибки. Вследствие чего придётся потратить время на то, чтобы найти их причину. Выделенная память под Ваш объект не была задействована. Инициализироваться мог случайный указатель, не имеющий никакой связи в объектом, которому была выделена память методом "alloc". Вот тогда "self" не проходит проверку
В рассматриваемом методе инициализации. И всё потому что в каком-то месте программы при вызове этого объекта инициализация не была произведена должным образом. Последнее, что мы должны сделать для завершения инициализации
Поскольку данный метод не "void", то он ожидает от нас, что в конце мы скажем ему что нужно возвратить. В данном случае будет возвращён объект типа "Primus", потому что "self" в рамках данного класса - именно "Primus". Также тип возвращаемого объекта нам говорит о том что ожидается именно "Primus". В этих методах
тип возвращаемых объектов - "Kitchen", "UkrainianBorsch" и "MasterPoRemontu". Таким образом, в методе "hotAsHell" мы говорим, что в конце выполнения данного метода желаем получить объект типа "Primus" с заданными параметрами.
Какими именно заданными параметрами?Опишем такую реализацию метода
Это значит, что при вызове этого метода
задаются буквально такие параметры
Параметры задаются переменным, которые объявлены в файле *.h Обратите внимание на конструкцию слова "setGas" или "setTemperature". Если есть переменная, например "variable", то задать ей нужное значение можно через префикс "set":
при этом первая буква переменной становится заглавной. Таким образом, мы выяснили что в файле Primus.h были объявлены переменные "gas" и "temperature". Но само наличие переменных не даёт нам возможности назначать их с помощью префикса "set" Для получения такой возможности нужно объявить свойства для этих переменных. Допустим такие:
и только после этого станет возможным присвоение им значения через "setGas" и "setTemperature". Эти префиксы называются сэттерами. В любой удобный момент можно попросить объект "Primus" показать Вам эти значения, обращаясь к его экземпляру
или
например для команды
Эти методы называются гэттерами. В случае обращения за значениями к названию переменной, ничего дописывать не нужно. Обратите внимание, что к свойству объекта можно обращаться как через конструкцию
так и через
Они выполняют аналогичные действия. Есть одна хитрость при доступе к переменным объекта. Это можно производить не только через свойства, но и через доступ к ним по принципу ключ-значение "key-value coding". То есть можно обратиться к переменной
так
Мы обратились за значением, которое находится в объекте "Primus" в ключевом слове "gas". А такая конструкция
в контексте "key-value coding" будет выглядеть так
Вместо свойств использовать "key-value coding" - вполне нудное занятие. Но иметь понятие о нём необходимо, чтобы в случае, когда без этого не обойтись - воспользоваться.
runtimeДаже если этого ещё не произошло, есть вероятность того что вскоре Вы встретите в документации слово "runtime" и в пределах той же статьи слово "isa". Понятие "runtime" можно охарактеризовать как "среда в которой происходит перевод Вашего кода на более низкоуровневый код". "runtime" написан непосредственно с использованием C и Ассемблера. Это ещё не перевод в машинный код, а приведение Вашего кода к языкам C и Ассемблеру. Ваш метод
в "runtime" на С выглядит примерно как
Этого для начинающего Cocoa программиста должно быть достаточно, чтобы понять: дальше лучше в дебри не лезть. Как только Вы перейдёте порог вхождения в клуб, то сами сможете отыскать нужную Вам информацию.
Пока же нас интересует что такое "isa".Это переменная, которая объявлена непосредственно в NSObject"е. Единственная его переменная. Когда мы вызывали метод
то создавался не только экземпляр класса NSobject, но и экземпляр этой переменной "isa", которая ссылается на NSObject. То есть, она конкретно говорит "runtime"у", что она принадлежит объекту NSObject. А значит, и работать с экземпляром Вашего, только что созданного объекта нужно как и с NSObject"ом. В "isa" записывается указатель на тот родительский объект, который нам нужно унаследовать. Допустим, Ваш объект - потомок NSArray или UITableView или CFDictionaryRef или любого другого объекта. В таком случае "isa" указывает на NSArray или UITableView или CFDictionaryRef или любой другой объект соответственно. Так что создание экземпляра любого объекта, создаёт и переменную класса - "isa", которая ссылается непосредственно на родительский класс, вследствие чего "runtime" знает как будет поступать с каждым экземпляром. Эта информация на этапе обучения нужна не для чего-то конкретного, а в принципе, для более объёмного понимания философии программирования Objective-C. В процессе чтения книг и различной документации, на глаза Вам не раз попадётся понятие "singleton". Как говорит один популярный интернет-мем: "Нельзя вот так сразу взять и понять, что такое "singleton".
Что такое синглтон?Вообразим себе, что Вам нужно создать объект, который при каждом его вызове в любой точке приложения, возвращает один и тот же экземпляр. На самом деле, в процессе создания приложений, Вам действительно нужно будет такое создавать. Так почему же нельзя создать объект, несколько раз и присвоить ему те же данные через метод "initWithSomething:" или с помощью сеттеров? Всё дело в работе с памятью и быстродействием, да и с собственно, меньшими затратами времени на написание кода. Памяти всегда мало, и даже когда на iPhone6 поставят 2Гб ОЗУ - её опять-таки будет мало. Создание одного экземпляра объекта, и последующее обращение к нему - экономит ресурсы устройства и ускоряет работу приложения. А ведь каждый хочет, чтобы его приложение было быстрым как "Bugatti Veyron" и юзабельным как слово "хрен". Допустим что наш "Primus" вполне может быть синглтоном. Тогда его метод
при реализации будет выглядеть так
Рассмотрим что это значит позже. Пока что выясним, зачем "Primus" делать синглтоном. Допустим, это не вполне обычный "Primus", а раритетный. На нём есть гравировка неизвестного мастера, он имеет крайне низкое потребление газа, а также у него есть специальная коробочка, в которой он идеально укладывается. А теперь задайте себе вопрос: "нужен ли Вам другой примус?". Конечно же нет! Но при посылке метода
будет создан один "Primus", а при посылке метода
будет создан абсолютно другой "Primus". Без гравировки и коробочки. Теперь вернёмся к тому, что написано в реализации метода "hotAsHell". Для начала нужно создать экземпляр объекта со свойством "static", для того, чтобы перекрыть доступ к объекту извне. Затем присвоить ему "nil", для того чтобы он не взял случайный адрес в памяти. Конструкция
создаёт предикат (условие) - "predicate" который тоже не будет виден извне. Условие заключается в том, что запрещается автоматический или динамический вызов блока, стоящего за предикатом. А вот строка
уже производит все необходимые действия для создания уникального экземпляра объекта. Конкретно "dispatch_once" означает что выражение в скобках после него гарантированно будет запущенно только один раз на протяжении всего жизненного цикла приложения. Также "dispatch_once" гарантирует потокобезопасность. То есть, при запуске приложения в многопоточном режиме эта функция не будет вызвана одновременно в нескольких потоках, и у ж точно не создаст нам ещё один "уникальный "Primus"". Также есть блок
Это вроде маленькой функции или метода. Бывают также большие блоки. В блоке происходит инициализация объекта-родителя, опять же присваивая ему "nil" в качестве адреса размещения. Вместе вся строка
означает: один раз за весь жизненный цикл приложения будет инициализирован экземпляр объекта "Primus" под названием "myPrimus" со свойствами объекта-родителя и не будет возможности обратиться к этому блоку другим путём. Но о том что был создан "myPrimus" никто не узнает, потому что этот экземпляр объекта снаружи не виден. Все происходит в фоне. И происходит благодаря GCD (Grand Central Dispatch). Рассказ о котором - отдельная тема. И конечно же в конце мы возвращаем созданный синглтон
Синглтон - "Primus" у нас есть, теперь можно добавить ему переменные: коробочка - "Box", гравировка - "Etching", КПД - "Performance". И если объявить для них свойства, то можно будет извне менять эти переменные. Обшить коробочку, почистить гравировку, прочиповать наш "Primus" для повышения КПД. Но это останется наш старый добрый "Primus". Доступ извне к объекту "Primus" будет у тех классов, в шапке которых он объявлен. Но теперь, если делать так
а потом работать с "myPrimus" как с синглтоном, то ничего работать не будет. Все обращения к переменным синглтона должны происходить в следующей манере
так можно создать экземпляр "someBox" совсем другого класса коробок и ему присвоить значение, которое имеет коробка нашего синглтона или наоборот
поменять коробку нашего "Primus". Синглтон можно применять когда нужно вызвать NSLog с описанием свойств синглтона, а самого синглтона в данном классе нет в принципе. В таком случае его нужно просто объявить в шапке файла и вызвать один раз там где нужно. Синглтон можно и даже рекомендуют использовать в качестве глобальной переменной. Точнее его переменные будут глобальными. После усвоения основных принципов, можно будет начинать решать предлагаемые в учебниках задачи. И если с написанием своих методов в своих классах и посылкой метода объекту разобраться можно, то в дальнейшем, нужно будет использовать делегаты, протоколы и прочие аспекты MVC. Необходимо будет использовать документацию Apple и применять тысячи различных методов, заботливо созданных и описанных купертиновскими программистами. И тут становится непонятно в принципе как и что работает. Если созданный Вами метод создаёт внутри себя массив, потом вносит в него объекты, потом запускает цикл и что-то в нём делает, а потом этот метод запускает другой Ваш метод, то вроде всё понятно. Но вот Вы открываете документацию по интересующему Вас объекту, а в нём 20 методов, которые могут делать весьма занятные вещи. Кроме того, есть объекты предки, методы которых этот объект тоже может принимать. Кроме того, в начале статьи я писал о протоколах, которым может соответствовать объект. Итого, методов может быть сотни. Какой из них нужно применять? Встречный вопрос: "для чего именно Вам нужен метод?". Правильно заданный вопрос - уже половина ответа.
И чем отличаются методы классов и протоколов?Если Вы хотите чтобы этот экземпляр объекта сделал что-то, присущее только классу от которого он произошёл, то в документации по этому классу нужно внимательно поискать метод, который производит необходимые Вам операции. Послать это метод объекту нужно так
То есть, в документации Вы узнали, что этот метод берёт указанный аргумент "Potato" и делает с ним что-то, что в конечном итоге приводит Вас к цели если применить метод к объекту "myPrimus". Не нужно реализовывать этот метод, он реализован за Вас для прямого применения. Исключения составляют случаи, когда Вам нужно взять готовый класс из фрэймворка и субклассировать его так, чтобы при посылке стандартных методов его экземпляру, происходили нестандартные действия. Есть также методы протоколов или абстрактных классов. Как я уже указывал в начале статьи, у них не может быть потомков. Они являются просто набором методов. Для того, чтобы применить метод протокола внутри себя, объект должен соответствовать этому протоколу. Это указывается в файле *.h
В данном случае, объект соответствует сразу двум протоколам "UITableViewDataSource" и "UITableViewDelegate". Если зайти в описание этих протоколов, то там можно найти методы, которые объект может реализовать. Обратите внимание. Методы протоколов Вы не посылаете своему объекту, а должны внутри них указать как должен отреагировать ваш объект, при обращении программы к этим методам. Некоторые методы в протоколах могут быть обязательными. И если вы решили что Ваш объект должен соответствовать протоколу, то обязательные методы нужно реализовать в первую очередь. Внутри любого метода протокола может быть любой метод класса. То есть, метод протокола - это контейнер, в котором находятся любые другие методы, собственно как и любой реализуемый Вами метод. Реализовать необходимый функционал внутри каждого метода протокола нужно исходя из потребностей. К примеру, нужно сделать так, чтобы Ваша формочка делала что-то, при определённых условиях. К примеру, меняла цвет, после того, как стала активной/неактивной. Идём в документацию Apple, смотрим какие протоколы реализуют нужные Вам методы. Затем ищем каким протоколам соответствует родительский класс формочки. Если протоколов, которые поддерживают нужный Вам функционал нет в стандартном наборе функций, то добавляем их в < > скобках. В описании этих протоколов ищем методы, которые реализуются после какого-либо события. Допустим
который автоматически выполняется когда аргумент "active" принимает значение "YES". И меняет цвет в части экрана описанной в "rect":
Методы протоколов задают параметры работы экземпляра класса, меняют функционал, передают значения. Например:
возвращает количество строк в секции "section" для заданной таблицы "tableView". На этом позволю себе закончить. Если статья поможет целевой аудитории, в которой несколько месяцев назад числился и я, то значит, что я думаю правильно и такие статьи нужны. Статьи не профессионалов, а людей, которые понимают что-то на таком уровне, который тяжело перешагнуть, не имея опоры или трамплина. Надеюсь эта статья кому-то будет трамплином или хотя бы табуреткой. Отсутствие хороших учителей, которые могут нормально что-то объяснить - фундаментальная проблема современности. У меня нет педагогического образования, но в статье изложено понятие в программировании в таком ключе, в каком лично мне было бы понятно. |