Настройка условного кода в подпроекте
У меня есть проект, который имеет подпроект библиотеки, который импортируется. У меня есть доступ к исходному коду основного проекта и подпроекта.
Подпроект использует основной текст. Поскольку подпроект должен использоваться в приложениях до и после 3.2, Core Text слабо связан, и весь связанный с Core Text код обернут в логику:
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
Идея этой строки кода заключается в том, что если CoreText не связан, код пропускается. Если версия iPhone меньше 3.2; CoreText не связан в.
Причиной этой цели является то, что основной (ые) проект (ы) (и их несколько) не все используют Core Text, и если не делать, то без 'определенные (CORETEXT) `они не будут компилироваться.
Кажется, это работает нормально, и все компилируется без ошибок в проектах, использующих Core Text. Однако при выполнении код не найден, и во время выполнения возникает ошибка "NSString не отвечает на XXXX" (часть кода представляет собой категорию на NSString
).
Кто-нибудь сталкивался с этим? Мой вопрос, частично неопределенный из-за того, что это работа клиента, ясен?
В идеале я хочу настроить это так, чтобы подпроект не нужно было изменять, когда основной проект использует Core Text, а когда нет.
Обратите внимание, что __CORETEXT__
определяется в заголовке каркаса Core Text.
Обновление по вопросу
Я попробовал предложения, предложенные до сих пор, и они неэффективны. Возможно, немного больше кода поможет обрисовать проблему. У меня есть категория со следующим заголовком:
@interface NSString (Internal)
- (NSString *)stringByUnescapingEntities;
- (NSString *)flattenHTML;
- (NSString *)flattenHTMLAndParseParagraphBreaks:(BOOL)parseBreaks;
- (NSString *)stringByEscapingForURL;
- (NSString *)stringByEscapingForJSON;
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
- (CFAttributedStringRef)attributedStringFromHTML;
- (CFAttributedStringRef)attributedStringFromHTMLWithBaseAttributes:(NSDictionary*)baseAttributes;
#endif
@end
Положительный тест
Все методы за пределами #if
Блок работает отлично. Все методы внутри #if
составить отлично, без предупреждений.
Отрицательный тест
Если я изменю __CORETEXT__
что-то вроде __THIS_IS_NOT_REAL__
Я получу ошибку компиляции для методов внутри блока. Поэтому я знаю, по крайней мере во время компиляции, что __CORETEXT__
Флаг определен и полностью функционален.
Проблема времени выполнения
Однако, когда я обращаюсь к одному из методов, которые объявлены внутри блока #if, я получаю следующую ошибку во время выполнения:
- [NSCFString attributedStringFromHTMLWithBaseAttributes:]: нераспознанный селектор, отправленный экземпляру 0x689c000
Пример 0x689c000
быть NSString
,
Если я удалю __CORETEXT__
проверка логики, тогда все работает, пока главный проект использует Core Text. Если мастер-проект не использует Core Text, появляются ошибки компиляции с отсутствующими компоновщиками.
Надеюсь, это проясняет проблему.
ПРИМЕЧАНИЕ. Все проекты используют -all_load
флаг; как это было нужно для TouchXML
уже.
Я создал тестовый проект, который демонстрирует проблему.
7 ответов
Тот факт, что вы заявляете, что по крайней мере одна вещь, которая в итоге пропускается, является методом категории для NSString, заставляет меня думать, что вы столкнулись с замечательной проблемой, которая, возможно, является возможной ошибкой в компоновщике.
Во-первых, посмотрите этот ответ в другом месте и убедитесь, что проект, включающий вашу библиотеку, использует флаг компоновщика -all_load.
Во-вторых, в некоторых отношениях, которые я имел с использованием библиотеки Three20 для iPhone, возникла необходимость также использовать этот странный "хак", который применяется, если, например, ваша категория NSString является единственной вещью в файле.m, где она реализована.,
//CategoryBugHack.h
#define FIX_CATEGORY_BUG(name) @interface FIX_CATEGORY_BUG##name @end @implementation FIX_CATEGORY_BUG##name @end
Тогда категория.h/.m ...
//NSStringCategory.h
@interface NSString (MyAdditions)
// etc, etc
@end
//NSStringCategory.m
#import "CategoryBugHack.h"
FIX_CATEGORY_BUG(NSString_MyAdditions)
@implementation NSString (MyAdditions)
// etc, etc
@end
В основном, кажется, есть дополнительная проблема, связанная со статическими библиотеками, где файл реализации содержит ТОЛЬКО реализации методов категорий. Макрос FIX_CATEGORY_BUG в основном просто расширяется, чтобы определить интерфейс и реализацию этого интерфейса без каких-либо методов, просто чтобы вставить что-то в файл.m, который не является категорией.
Вы можете выполнить [NSObject responsedsToSelector:] проверки, чтобы увидеть, доступен ли метод категории или нет.
Изменить: я думаю, что я не полностью прочитал вопрос в первый раз. Вот еще пара вещей, которые я видел.
Тот #if
наверное не сработает. Это потому, что все это проверяется во время компиляции, а не во время выполнения. Я не совсем уверен в том, что включается при слабом связывании, но, насколько я понимаю, заголовок слабосвязанного фреймворка будет обрабатываться во время компиляции. Это означает, что символы для прототипов методов будут доступны при сборке, поэтому нет ошибок или предупреждений при сборке.
Я также на 99% уверен, что проверка iOS не будет работать. Это только проверяет максимально допустимую версию, которая устанавливается во время компиляции. Если вы не делаете разные сборки с разными настройками для каждой iOS (что, как я полагаю, нет), это почти всегда будет одинаковым значением, независимо от того, на каком устройстве вы работаете.
Вам определенно понадобятся проверки во время выполнения (например, вышеупомянутый RespondsToSelector). Проверка версии устройства iOS также может быть выполнена только во время выполнения.
Ответ в двух частях:
CoreText.h
импортируется в PCH-файл вашего приложения, но нигде в библиотеке (PCH не каскадируются). Поэтому в библиотеке__CORETEXT__
код всегда#ifdef
вышли Однако, когда экспортированный заголовок из библиотеки компилируется в приложении,#ifdef
Прототип метода d включен, и поэтому вы не получите предупреждений во время компиляции для этого метода на NSString.Быстрое решение проблемы - буквальное
#import <CoreText/CoreText.h>
в файле PCH вашей библиотеки. Что, конечно, побеждает цель того, что вы пытаетесь сделать.
Исправление это еще одна тема, но я подозреваю, что вам лучше использовать свой собственный -D или
#define
и затем пытаюсь каскадировать этот ваш подпроект, хотя я пока не знаю, как это сделать сам.Один хороший способ временно проверить, если ваш
#ifdef
код компиляции ставит#error
в условных выражениях, например#ifdef WHEE // compile-time error will happen here #error #endif
Я полагаю, что недавно обнаружил похожую ситуацию, и, как я уже говорил вам в твиттере, я думаю, что вам нужно установить -ObjC в дополнение к -all_load, как описано здесь: http://developer.apple.com/mac/library/qa/qa2006/qa1490.html
Если категория размещена в подпроекте, вам может потребоваться установить цель этого проекта. У меня были странные побочные эффекты, подобные этому, когда я выполнял подпроекты внутри проекта.
Редактировать: Значит, подпроект может быть настроен на компиляцию с 3.1.3 и возможно никогда не соответствует вашей логике?
Может быть, вы должны сделать условное в
#if defined(__CORETEXT__) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2)
В конце концов, остальная часть ссылочного проекта связана, правильно? Проблема должна быть условной препроцессора.
Я помню, что сделал что-то похожее на Дэвида, но только для одного метода. Я использовал функции времени выполнения Objective C для вызова этого метода, чтобы он компилировался в каждой версии. Я сделал что-то вроде:
if ([theReceiver respondToSelector:@selector(mySelector:)]){
objc_msgSend(theReceiver, @selector(mySelector:), param1);
}else{
// do it other way here.
}
Не самый красивый код, который я написал, и я не пробовал раньше, но, возможно, возможно добавить ваши методы и ivars с функциями времени выполнения, такими как class_addIvar, class_addMethod и т. Д.