Выявление/синтез свойств iVar в Objective c

У меня есть класс, который по существу действует как легкий класс-оболочка вокруг другого класса. Он содержит этот другой класс как iVar. Я хочу иметь возможность отображать определенные свойства (на самом деле довольно много) iVar, но для этого мне нужно написать каждое средство доступа к свойствам следующим образом:

- (void) setProperty:(Class *)value{
    _iVar.property = value;
}
- (Class *) property{
    return  _iVar.property;
}

Конечно, я должен сделать это для каждого отдельного свойства, что является проблемой (их около 30). Я хотел бы иметь возможность синтезировать это, но я не смог понять, как это сделать.

Можно ли синтезировать?

Кроме того, я не могу создавать подклассы... ну, я мог бы, но это действительно не рекомендуется. Класс iVar действительно довольно тяжелый (он реализует CoreText). Я лучше распишу методы от руки.


person Aaron Hayman    schedule 23.02.2012    source источник
comment
Взгляните на этот вопрос (и мой собственный ответ на него): stackoverflow.com/questions/8763028 Очень хороший вопрос, хотя!   -  person Richard J. Ross III    schedule 23.02.2012
comment
@RichardJ.RossIII Спасибо за помощь. Пост на mikeash.com действительно помог мне разобраться в этом вопросе. Я разместил ответ.   -  person Aaron Hayman    schedule 23.02.2012


Ответы (2)


Итак, вот решение, которое я нашел... оказалось довольно простым, когда вы знали, что делать. Сначала перезапишите «- (id) forwardingTargetForSelector: (SEL) aSelector» и верните iVar:

- (id) forwardingTargetForSelector:(SEL)aSelector{
    return iVar;
}

Когда среда выполнения ищет метод и не может его найти, она вызывает этот метод, чтобы узнать, есть ли другой объект для пересылки сообщения. Обратите внимание, что этот метод обычно возвращает nil, и если вы вернете nil здесь, ваша программа рухнет (что является подходящим поведением).

Вторая часть проблемы заключается в том, чтобы скрыть ошибки/предупреждения компилятора, которые вы получите, когда попытаетесь отправить сообщение, которое не объявлено. Это легко сделать, объявив категорию, которую вы не реализуете.

@interface Class (iVarClassMethods)
@propoperty (strong) Class *property1;
......more properties
@end

Пока вы никуда не вставляете реализацию, также известную как @implementation Class (category), компилятор не будет жаловаться (он будет предполагать, что реализация где-то....).

Теперь единственный недостаток, который я вижу, это то, что если вы измените какое-либо из свойств в интерфейсе класса iVar, вам нужно убедиться, что вы обновляете все другие классы, которые используют метод, описанный выше, иначе вы будете падать, когда другой класс попытается отправить что сейчас является неправильным методом (и компилятор не предупредит вас заранее). Однако это можно обойти. Вы можете объявить протоколы в категории. Поэтому вместо этого вы создаете отдельный протокол для класса iVar и перемещаете нужные методы/свойства из класса iVar в протокол.

@protocol iVarClassProtocol
@propoperty (strong) Class *property1;
......more properties
@end

Добавьте этот протокол в подкласс iVar, чтобы теперь эти методы были объявлены через протокол.

@interface iVarClass <iVarClassProtocol>
....other methods/properties you don't need forwarded
@end

Наконец, просто добавьте протокол в категорию. Итак, вместо вышеупомянутой категории с явными объявлениями у вас будет:

@interface Class (iVarClassMethods) <iVarClassProtocol>
@end

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

person Aaron Hayman    schedule 23.02.2012

Я думаю, вы можете пересылать сообщения на ivar:

- (void) forwardInvocation: (NSInvocation*) invocation
{
    [invocation invokeWithTarget:ivar];
}

- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
    NSMethodSignature *our = [super methodSignatureForSelector:selector];
    NSMethodSignature *ivars = [ivar methodSignatureForSelector:selector];
    return our ? our : ivars;
}

Затем вам нужно скрыть или подделать тип вашего объекта, например, путем приведения к id, иначе компилятор будет жаловаться, что ваш класс не реализует эти методы. Конечно, было бы лучше, если бы вы могли придумать какой-нибудь лучший дизайн, который бы обходился без таких ухищрений.

person zoul    schedule 23.02.2012
comment
Однако вы не можете использовать для этого точечную нотацию, так как для этого не будет подходящего интерфейса. - person Richard J. Ross III; 23.02.2012
comment
да. Вы можете привести объект к классу ивара, что иногда имеет смысл и порадует компилятор даже при точечной нотации. - person zoul; 23.02.2012
comment
Вообще говоря, вы не хотите приводить объект к классу, которым он не является, особенно когда имеете дело с чем-то вроде свойств, так как многое может сломаться по пути (что, если оболочка определяет свойство с тем же именем, что и у одного). из iVar возникает неожиданное поведение, поскольку вы ожидаете, что он вызовет реализацию iVar, когда на самом деле вызывает реализацию оболочки). - person Richard J. Ross III; 23.02.2012
comment
да. А вообще говоря, это дизайнерский бардак и решения наверное все повредят, просто в разных местах. - person zoul; 23.02.2012
comment
Я согласен, переделывать объект нежелательно. Тем более, что у основного класса есть методы, которых нет у класса iVar. - person Aaron Hayman; 23.02.2012
comment
Я думаю, что вы на правильном пути, хотя. Я нашел решение благодаря Ричарду Дж. Россу III (см. его комментарий под моим вопросом). Я не писал прокси, но нашел решение здесь: mikeash.com/pyblog/. Посмотрите на «- (id) forwardingTargetForSelector: (SEL) aSelector». Если вы сможете найти решение и отредактировать свой ответ, я приму его. В противном случае, я напишу ответ через пару часов. - person Aaron Hayman; 23.02.2012