Вместо написания собственных методов доступа, включения в прикладной код директивы ©synthesize или применения автоматического синтеза можно снабдить объявление свойства директивой ©dynamic (в разделе реализации).
Эта директива предписывает компилятору не предоставлять методы доступа автоматически, даже если он не обнаружит реализации ни одного из методов доступа к данному свойству. В любом случае компилятор должен разрешить объявление свойства на том основании, что во время выполнения, когда дело дойдет до вызова одного из методов доступа, этот вызов будет так или иначе обработан в прикладном коде, хотя компилятору может быть и невдомек, как это будет сделано. По существу, упомянутой выше директивой подавляется система предупреждения компилятора. Она просто отступает в сторону, предоставляя вам возможность действовать на свой страх и риск во время выполнения.
Это редкое, но не такое уж и неслыханное явление. Оно возникает, главным образом, в следующих двух контекстах: при определении собственного свойства анимационного представления, а также при использовании управляемых свойств в базе данных Core Data. В обоих случаях вы берете на себя бразды правления средой Cocoa, чтобы каким-нибудь чудесным образом обработать вызовы методов доступа. Несмотря на то что вам точно неизвестно, каким образом такая обработка выполняется в среде Cocoa, вас это не должно особенно беспокоить.
Если эту чудесную обработку требуется выполнить самостоятельно, то интересно, прежде всего, знать, как в подобных случаях это делается в самой средой Cocoa? Ответ на этот вопрос кроется в возможностях языка Objective-C организовывать динамический обмен сообщениями. Хотя это расширенные возможности, тем не менее они настолько привлекательны, что так и напрашиваются на демонстрацию на конкретном примере.
Итак, предлагается написать класс, в котором объявляются свойства name (типа NSString) и number (типа NSNumber), но отсутствуют методы доступа к этим свойствам и не предполагается их синтез. Вместо этого свойства name и number объявляются в разделе реализации данного класса как динамические. Поскольку мы отказываемся от любой помощи, которую может предоставить автоматический синтез, то нам придется самостоятельно объявить переменные экземпляра, как показано ниже.
Теперь перейдем непосредственно к изобретению способов чудесной обработки. В данном примере для этой цели используется малоизвестный метод resolve Ins tanceMethod: из класса NSObject.
Отправка сообщения объекту отличается от вызова того же самого метода для этого объекта. Если этот метод отсутствует у данного объекта, то предпринимаются дополнительные шаги, чтобы разрешить такой метод. Один из этих первых шагов заключается в следующем: когда к объекту в первый раз поступает заданное сообщение, у которого нет соответствующего метода в классе этого объекта, среда выполнения пытается найти реализацию метода resolvelnstanceMethod: в этом классе. Если она найдет такой метод, то вызовет его, передав ему селектор данного сообщения. Метод resolvelnstanceMethod: возвратит логическое значение типа BOOL. Если это логическое значение YES, то оно означает, что все в порядке и можно вызывать данный метод.
Как метод resolvelnstanceMethod: вообще мог возвратить такой ответ? Что он мог сделать такого, чтобы стал возможным вызов несуществующего метода? Он мог просто создать этот метод. Динамические возможности языка Objective-C позволяют это сделать. Не следует забывать, что метод resolvelnstanceMethod: вызывается лишь один раз для каждого разрешаемого метода. Следовательно, если он создал такой метод и возвратил логическое значение YES, то проблему существования этого метода можно в дальнейшем считать разрешенной раз и навсегда.
Для того чтобы создать метод в реальном времени, т.е. динамически, следует вызвать функцию class_addMethod (). Для этого придется организовать импорт файла, введя <ob j с / runt ime. h> или соответствующую директиву @ import языка Objective-C, если применяются модули. Данная функция принимает следующие параметры.
В рассматриваемом здесь примере необходимо охватить два динамических свойства (паше и number) и два метода доступа к ним (setName: и setNumber:). Итак, чтобы вызвать функцию class_addMethod() в методе resolvelnstanceMethod:, нужно также написать функции С, которые будут действовать как IMP для каждого из упомянутых методов доступа. Для этого можно было бы написать четыре функции С, но это было бы бесполезно! Если сделать это, то зачем вообще утруждать себя применением динамического метода доступа? Вместо этого предлагается написать только две функции С: одну — для обращения к любому get-методу, который, возможно, потребуется направить ей, а другую — для обращения к любому set-методу, который также может быть направлен ей.
Рассмотрим сначала get-метод, поскольку он намного проще. Что же вообще должен делать обобщенный get-метод? Он должен получать доступ к соответствующей переменной
экземпляра. Как должна называться такая переменная? Получается так, что эта переменная должна носить имя метода с предшествующим знаком подчеркивания. Поэтому сначала нужно извлечь имя метода, что вполне возможно, поскольку оно передано данной функции в качестве ее второго параметра, т.е. селектора, а затем присоединить его к знаку подчеркивания и возвратить в качестве значения той переменной экземпляра, имя которой выводится. Ради простоты примера значение данной переменной экземпляра получается с помощью механизма доступа к значениям по ключам, как показано ниже. Наличие знака подчеркивания означает, что это не приведет к зацикливанию, т.е. функция не завершится вызовом самой себя в бесконечной рекурсии.
Теперь, когда имеется get-метод, становится понятнее, как написать set-метод. В частности, чтобы получить имя переменной экземпляра, придется выполнить несколько более сложное манипулирование именем селектора, а именно: отбросить слово set в начале этого имени и двоеточие в его конце, сделать строчной первую букву и затем присоединить полученное имя к знаку подчеркивания, как и прежде. Ниже показано, как все это реализуется непосредственно в коде.
В заключение, можно приступить к написанию метода resolvelnstanceMethod:. В данном примере этот метод выполняет роль контролера, проверяющего, является ли метод, который должен быть вызван, методом доступа к одному из динамических свойств, как показано ниже. (Если вам не понятно, что собой представляет закодированная символьная строка, указанная в приведенном ниже коде в качестве четвертого аргумента функции class_addMethod () языка С, обращайтесь за справкой к документации, где поясняется ее назначение.)
Рассмотренная выше реализация динамических методов доступа довольно проста. В частности, ради простоты в ней применяется механизм доступа к значениям по ключам. Здесь также пришлось отказаться от копирования семантики в методе установки из класса NSString. Это довольно распространенный прием, который все же позволяет раскрыть внутренний механизм языка Objective-C.