Я узнал что-то новое, пытаясь выяснить, почему мое свойство readwrite, объявленное в частной категории, не генерировало установщик. Это потому, что моя категория была названа:
// .m
@interface MyClass (private)
@property (readwrite, copy) NSArray* myProperty;
@end
Изменение на:
// .m
@interface MyClass ()
@property (readwrite, copy) NSArray* myProperty;
@end
и мой сеттер синтезирован. Теперь я знаю, что расширение класса - это не просто другое имя для анонимной категории. Если оставить категорию без имени, она превратится в другого зверя: тот, который теперь обеспечивает реализацию реализации метода во время компиляции и позволяет добавлять ivars. Теперь я понимаю общие принципы, лежащие в основе каждого из них: категории обычно используются для добавления методов к любому классу во время выполнения, а расширения классов обычно используются для обеспечения реализации частного API и добавления ivars. Я принимаю это.
Но есть мелочи, которые меня смущают. Во-первых, на высоком уровне: зачем так дифференцироваться? Эти понятия кажутся похожими идеями, которые не могут решить, являются ли они одинаковыми или разными понятиями. Если бы они были одинаковыми, я ожидал бы, что с использованием категории без имени можно будет сделать то же самое, что и с именованной категорией (которой они не являются). Если они разные, (которые они есть), я бы ожидал большего синтаксического различия между ними. Кажется странным сказать: «О, кстати, чтобы реализовать расширение класса, просто напишите категорию, но пропустите имя. Оно волшебным образом меняется».
Во-вторых, по вопросу применения времени компиляции: если вы не можете добавить свойства в именованную категорию, почему это убеждает компилятор в том, что вы сделали именно это? Чтобы уточнить, я проиллюстрирую своим примером. Я могу объявить свойство только для чтения в заголовочном файле:
// .h
@interface MyClass : NSObject
@property (readonly, copy) NSString* myString;
@end
Теперь я хочу перейти к файлу реализации и предоставить себе частный доступ для чтения и записи к свойству. Если я делаю это правильно:
// .m
@interface MyClass ()
@property (readwrite, copy) NSString* myString;
@end
Я получаю предупреждение, когда я не синтезирую, а когда я это делаю, я могу установить свойство, и все персиковое. Но, к сожалению, если я немного ошибаюсь в разнице между категорией и расширением класса, я стараюсь:
// .m
@interface MyClass (private)
@property (readwrite, copy) NSString* myString;
@end
Компилятор полностью усвоил мысль о том, что свойство перезаписано. Я не получаю предупреждений и даже не вижу приятной ошибки компиляции «Объект не может быть установлен - либо свойство только для чтения, либо не найден установщик» после установки myString, если бы я не объявил свойство readwrite в категории. Я просто получаю исключение «Не отвечает селектору» во время выполнения. Если добавление ivars и свойств не поддерживается (именованными) категориями, не слишком ли много вопросов, чтобы компилятор проигрывал по тем же правилам? Я скучаю по какой-то великой философии дизайна?