我有一个项目,其中包含一个可导入的库子项目。我可以访问主项目和子项目的源代码。
子项目使用核心文本。因为子项目必须在3.2之前和之后的应用程序上使用,所以Core Text是弱链接的,并且所有与Core Text相关的代码都包含在逻辑中:
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
这行代码的思想是如果没有链接CoreText,则跳过代码。如果iPhone版本小于3.2; CoreText未链接。
这个目标的原因是主要项目(并且有几个)并不全都使用Core Text,如果没有那么没有'defined( CORETEXT )` ,他们不会编译。
这个似乎可以正常工作,所有内容在使用Core Text的项目上编译时没有错误。但是在执行时,找不到代码,并且“NSString”中的运行时错误不响应XXXX(代码的一部分是NSString
上的类别)。
有没有人碰到这个?我的问题,部分模糊,因为它是客户工作,清楚吗?
理想情况下,我想进行设置,以便在主项目使用核心文本时不需要更改子项目,而不需要更改子项目。
请注意,__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 attributionStringFromHTMLWithBaseAttributes:]:无法识别的选择器发送到实例0x689c000
实例0x689c000
是NSString
。
如果我删除__CORETEXT__
逻辑检查,那么只要主项目使用Core Text,一切都可以正常工作。如果主项目未使用Core Text,则会显示有关缺少链接器的编译错误。
希望澄清这个问题。
注意:所有项目都使用-all_load
标记;因为TouchXML
已经需要它。
我创建了一个演示此问题的测试项目。
答案 0 :(得分:2)
你提到至少有一件东西最终缺失的事实是NSString的类别方法让我觉得你遇到了一个可爱的问题,这似乎是链接器中可能存在的错误。
首先,请参阅this answer elsewhere并确保包含您的库的项目使用-all_load链接器标记。
其次,在我使用iPhone的Three20库进行的一些交易中,有必要使用这种奇怪的“黑客”,例如,如果您的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文件。
答案 1 :(得分:1)
您可以执行[NSObject respondsToSelector:]检查以查看类别方法是否可用。
编辑:我想我第一次没有完全阅读这个问题。以下是我看到的更多内容。
那#if
可能无效。这是因为这是在编译时而不是运行时检查的。我不确定弱链接时包含什么,但据我了解,弱链接框架的头将在编译时得到处理。这意味着在构建时可以使用方法原型的符号,这就是没有构建错误或警告的原因。
我也99%确定iOS检查不起作用。它只检查允许的最大版本,这是在编译时设置的。除非您使用不同的设置为每个iOS制作不同的版本(我假设不是这样),否则无论您运行的是哪个设备,它都将始终具有相同的值。
你肯定需要运行时检查(例如前面提到的respondsToSelector)。 iOS设备的版本检查也只能在运行时完成。
答案 2 :(得分:0)
也许你应该将条件转化为
#if defined(__CORETEXT__) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2)
毕竟,引用项目的其余部分已链接,对吗?问题必须是预处理器的条件。
答案 3 :(得分:0)
我记得我做过类似大卫的事情,但只是针对一种方法。 我使用Objective-C运行时函数来调用该方法,以便在每个版本中进行编译。 我做了类似的事情:
if ([theReceiver respondToSelector:@selector(mySelector:)]){
objc_msgSend(theReceiver, @selector(mySelector:), param1);
}else{
// do it other way here.
}
不是我编写的最漂亮的代码,也不是我之前尝试过的,但也许可以使用运行时函数(如class_addIvar,class_addMethod等)添加方法和ivars。
答案 4 :(得分:0)
如果类别位于子项目中,您可能需要设置该项目的目标。在项目中进行子项目时,我有这样奇怪的副作用。
编辑:意思是,子项目可以设置为针对3.1.3进行编译,可能永远不符合你的逻辑?
答案 5 :(得分:0)
我相信我最近发现了类似的情况,正如我在twitter上告诉你的那样,我认为除了-all_load之外你还需要设置-ObjC,如下所述:http://developer.apple.com/mac/library/qa/qa2006/qa1490.html
答案 6 :(得分:0)
答案分为两部分:
CoreText.h
导入到您应用的PCH文件中,但库中没有(PCH不会级联)。因此,在库中,__CORETEXT__
代码始终为#ifdef
'。但是,当在库中编译导出的标头时,会包含#ifdef
'd 方法原型,因此您没有 编译时< / em> NSString上该方法的警告。
快速解决此问题的方法是输入文字
#import <CoreText/CoreText.h>
在您图书馆的PCH文件中。当然,这会破坏你想要做的目的。
修复这是另一个主题,但我怀疑你最好使用你自己的-D或#define
,然后尝试级联你的子项目,虽然我不知道如何自己做,爱好。
临时测试你的#ifdef
代码是否正在编译的一个好方法是将#error
放入条件中,例如
#ifdef WHEE
// compile-time error will happen here
#error
#endif