如何制作只能在库中看到的目标 c 方法,而不是在导出的标头中

Posted

技术标签:

【中文标题】如何制作只能在库中看到的目标 c 方法,而不是在导出的标头中【英文标题】:How can I make an objective c method that can only be seen within a library, not in exported headers 【发布时间】:2012-11-06 19:56:09 【问题描述】:

我正在构建一个用于控制 ios 外部附件的静态库。作为其中的一部分,我想展示一组最小的公共头文件,以与编译的库一起分发。

我有一个需要公开的类,但我想在它上面创建一些框架私有方法。我为其中一个公共类创建了一个类别来添加私有方法,包括一个初始化程序。这适用于项目测试,但会生成 -[MyClass initMethodDefinedInCategory:]: unrecognized selector sent to instance 错误。

类别标题:

#import "MyClass.h"

@interface MyClass (LibraryPrivateMethods)
- (id)initMethod:(NSData *)data;
- (int)someOtherMethod:(NSData *)data;
@end

使用类

#import "MyOtherClass.h"
#import "MyClass.h"
#import "MyClass+LibraryPrivateMethods.h"

@implementation MyOtherClass

#pragma mark - Instance Methods
- (id)initWithData:(NSData *)data

    self = [super init];
    if(self)
    

        MyClass *mc = [[MyClass alloc] initMethod:data]; // errors here
        _property = mc;
    
    return self;

我已尝试按照this Apple tech note 中的建议添加-ObjC 链接器标志。我还尝试按照this SO answer 中的建议添加-all_load 标志。

编辑:意识到我只是在 XCode 构建中添加,忘记检查构建脚本,如下所示。

set -ex

INSTALL_PATH=$WORKSPACE/artifacts
[ -z $INSTALL_PATH ] || INSTALL_PATH=$PWD/artifacts

rm -rf $INSTALL_PATH
mkdir -p $INSTALL_PATH

PROJ=$SRCROOT/MyLib.xcodeproj

xcodebuild -project $PROJ -sdk iphoneos INSTALL_ROOT=$INSTALL_PATH/device install
xcodebuild -project $PROJ -sdk iphonesimulator INSTALL_ROOT=$INSTALL_PATH/simulator install

lipo -create -output $INSTALL_PATH/libMyLib.a $INSTALL_PATH/device/libMyLib.a $INSTALL_PATH/simulator/libMyLib.a
mv $INSTALL_PATH/device/Headers $INSTALL_PATH
rm -rf $INSTALL_PATH/device $INSTALL_PATH/simulator

# create zip for distribution.
(cd $INSTALL_PATH; zip -r ../MyLib-release.zip libMyLib.a Headers ../documentation/LibraryDocs/html ../documentation/LibraryDocs/docset)

我应该在哪里将 -ObjC 标志添加到构建脚本?

你有什么理由不能在一个类别中有一个初始化方法吗?正如this Apple doc 所说,这似乎是可能的

类别方法可以做任何在类中定义的方法 适当的可以做。在运行时,没有区别。方法 添加到类的类别被所有类的继承 子类,就像其他方法一样。

然后我尝试使用类扩展,但我不确定如何使该方法仅对框架类可见。

@interface MyClass ()
- (id)initMethod;
@end

如果我随后从库中的另一个类调用 [[MyClass alloc] initMethod],我会收到“MyClass 的无可见 @interface 声明选择器 initMethod”错误。

如何使对象上的方法只能被框架中的其他类访问,而不是在 MyClass.h 中导出?

【问题讨论】:

这很奇怪——我以前也遇到过这个问题,添加-ObjC 链接器标志解决了我的问题。在创建静态库和链接最终可执行文件时,您是否尝试将 -ObjC 标志传递给两个链接器命令行? 你的initMethodDefinedInCategory:方法的实现在哪里?它与公共方法在同一个 .m 中,还是在单独的 .m 文件中? @Darren 实现在 MyClass+LibraryPrivateMethods.m 文件中。所以一个单独的文件到公共方法。 @AdamRosenfield 你可能正在做某事。我忘记了构建脚本。将其添加到问题中。我将在构建脚本中的哪里传递 -ObjC 标志?还是 XCode 会自动做这种事情? @aj.esler:如果您使用xcodebuild 来构建静态库,那么您不会在命令行上传递任何内容——您可以在某处添加-ObjC 标志项目设置。如果没有明确的设置,应该在某处有一个“附加命令行选项”字段(或类似的东西)。 【参考方案1】:

将类别声明放在单独的 .h 文件中。将此私有 .h 文件导入框架中需要访问类别方法的任何 .m 文件中。不要将此 .h 文件打包到分发给客户使用的公共 .h 文件集中。

顺便说一句 = 不要使用类扩展,使用命名类别。

【讨论】:

这正是我所做的。我已更新我的问题以更好地显示问题。【参考方案2】:

见the Apple Tech note底部的说明:

重要提示:对于 64 位和 iPhone OS 应用程序,存在阻止 -ObjC 从仅包含类别且不包含类的静态库加载对象文件的链接器错误。解决方法是使用 -all_load 或 -force_load 标志。

由于MyClass+LibraryPrivateMethods.m 仅包含类别代码,听起来您看到了注释中描述的错误。

将您的私有实现移至主 MyClass.m 或尝试使用 -all_load-force_load 标志。

【讨论】:

我对此的解释是我很好,因为图书馆既有类又有类别。总共有大约十几个班级,一个类别。 该注释与单个对象文件有关。 MyClass+LibraryPrivateMethods.m 将编译为不包含任何类的单个目标文件。该错误会阻止链接器正确处理该目标文件。【参考方案3】:

无法在为该问题分配的时间内解决此问题。我最终删除了该类别并将初始化程序代码移动到 MyOtherClass initWithData 方法中。 这编译得很好并且可以工作。

【讨论】:

以上是关于如何制作只能在库中看到的目标 c 方法,而不是在导出的标头中的主要内容,如果未能解决你的问题,请参考以下文章

我有一个Xcode静态库项目,如何添加测试目标,以便我可以在那里运行它? (而不是在链接到它的项目中。)

OpenCV未定义引用我自己在库中的方法

如何从库中动态添加对象?

实验六 进程基础

关于 Linux 中的共享库,有没有办法在库中选择导出函数?

如何制作可在不同代码库中重用的C“库”?