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