静态库中的 Objective-C 类别

发布于 2024-08-27 21:01:28 字数 196 浏览 6 评论 0原文

你能指导我如何正确地将静态库链接到 iPhone 项目吗?我使用添加到应用程序项目中的静态库项目作为直接依赖项(目标 -> 常规 -> 直接依赖项)并且所有工作正常,但类别。静态库中定义的类别在应用程序中不起作用。

所以我的问题是如何将某些类别的静态库添加到其他项目中?

一般来说,在其他项目的应用程序项目代码中使用的最佳实践是什么?

Can you guide me how to properly link static library to iPhone project. I use static library project added to app project as direct dependency (target -> general -> direct dependencies) and all works OK, but categories. A category defined in static library is not working in app.

So my question is how to add static library with some categories into other project?

And in general, what is best practice to use in app project code from other projects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

溇涏 2024-09-03 21:01:28

解决方案:从 Xcode 4.2 开始,您只需转到链接库的应用程序(而不是库本身),然后单击项目导航器中的项目,单击应用程序的目标,然后构建设置,然后搜索“其他链接器标志”,单击+按钮,并添加“-ObjC”。不再需要“-all_load”和“-force_load”。

详细信息:
我在各种论坛、博客和苹果文档中找到了一些答案。现在我尝试对我的搜索和实验做一个简短的总结。

问题是由(引用自苹果技术问答 QA1490 https:// developer.apple.com/library/content/qa/qa1490/_index.html):

Objective-C 没有定义链接器
每个函数(或方法,
在 Objective-C 中) - 相反,链接器
仅为每个生成符号
班级。如果您延长已有的
带有类别的类,链接器会这样做
不知道关联目标代码
核心类的实现和
类别实施。这
防止在以下位置创建对象
响应产生的申请
到定义在的选择器
类别。

以及他们的解决方案:

为了解决这个问题,静态
库应该传递 -ObjC 选项
到链接器。该标志导致
链接器加载每个目标文件
定义一个库
Objective-C 类或类别。尽管
此选项通常会导致
更大的可执行文件(由于额外的
目标代码加载到
应用程序),它将允许
成功创建有效
Objective-C 静态库
包含现有类别
类。

iPhone开发FAQ中也有推荐:

如何链接所有 Objective-C
静态库中的类?设置
其他链接器标志构建设置为
-ObjC。

和标志说明:

-all_load 加载静态存档库的所有成员。

-ObjC 加载实现静态归档库的所有成员
Objective-C 类或类别。

-force_load (path_to_archive) 加载指定静态的所有成员
档案库。注意:-all_load
强制所有档案馆的所有成员
被加载。此选项允许您
定位特定的存档。

*我们可以使用force_load来减少应用程序二进制文件的大小并避免all_load在某些情况下可能导致的冲突。

是的,它适用于添加到项目中的 *.a 文件。
然而,我在将 lib 项目添加为直接依赖项时遇到了麻烦。但后来我发现这是我的错——直接依赖项目可能没有正确添加。当我删除它并通过以下步骤再次添加时:

  1. 将 lib 项目文件拖放到应用程序项目中(或使用项目 - >添加到项目...添加它)。
  2. 单击 lib 项目图标处的箭头 - 显示 mylib.a 文件名,将此 mylib.a 文件拖放到 Target -> 中将二进制链接到库组。
  3. 在第一页(常规)中打开目标信息,然后将我的库添加到依赖项列表中,

之后一切正常。 “-ObjC”标志对我来说就足够了。

我也对 http://iphonedevelopmentexperiences.blogspot 的想法感兴趣。 com/2010/03/categories-in-static-library.html 博客。作者说他可以使用 lib 中的类别,而无需设置 -all_load 或 -ObjC 标志。他只是将空虚拟类接口/实现添加到类别 h/m 文件中,以强制链接器使用此文件。是的,这个技巧可以做到这一点。

但作者还说他甚至没有实例化虚拟对象。嗯……正如我发现的,我们应该从类别文件中显式调用一些“真实”代码。所以至少应该调用类函数。
我们甚至不需要虚拟类。单个 c 函数执行相同的操作。

因此,如果我们将 lib 文件编写为:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

并且如果我们调用 useMyLib(); App项目中的任何地方
那么在任何类中我们都可以使用 logSelf 类别方法;

[self logSelf];

以及有关主题的更多博客:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12 /fat-iphone-static-libraries-device-and.html

Solution: As of Xcode 4.2, you only need to go to the application that is linking against the library (not the library itself) and click the project in the Project Navigator, click your app's target, then build settings, then search for "Other Linker Flags", click the + button, and add '-ObjC'. '-all_load' and '-force_load' are no longer needed.

Details:
I found some answers on various forums, blogs and apple docs. Now I try make short summary of my searches and experiments.

Problem was caused by (citation from apple Technical Q&A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html):

Objective-C does not define linker
symbols for each function (or method,
in Objective-C) - instead, linker
symbols are only generated for each
class. If you extend a pre-existing
class with categories, the linker does
not know to associate the object code
of the core class implementation and
the category implementation. This
prevents objects created in the
resulting application from responding
to a selector that is defined in the
category.

And their solution:

To resolve this issue, the static
library should pass the -ObjC option
to the linker. This flag causes the
linker to load every object file in
the library that defines an
Objective-C class or category. While
this option will typically result in a
larger executable (due to additional
object code loaded into the
application), it will allow the
successful creation of effective
Objective-C static libraries that
contain categories on existing
classes.

and there is also recommendation in iPhone Development FAQ:

How do I link all the Objective-C
classes in a static library? Set the
Other Linker Flags build setting to
-ObjC.

and flags descriptions:

-all_load Loads all members of static archive libraries.

-ObjC Loads all members of static archive libraries that implement an
Objective-C class or category.

-force_load (path_to_archive) Loads all members of the specified static
archive library. Note: -all_load
forces all members of all archives to
be loaded. This option allows you to
target a specific archive.

*we can use force_load to reduce app binary size and to avoid conflicts which all_load can cause in some cases.

Yes, it works with *.a files added to the project.
Yet I had troubles with lib project added as direct dependency. But later I found that it was my fault - direct dependency project possibly was not added properly. When I remove it and add again with steps:

  1. Drag&drop lib project file in app project (or add it with Project->Add to project…).
  2. Click on arrow at lib project icon - mylib.a file name shown, drag this mylib.a file and drop it into Target -> Link Binary With Library group.
  3. Open target info in fist page (General) and add my lib to dependencies list

after that all works OK. "-ObjC" flag was enough in my case.

I also was interested with idea from http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html blog. Author say he can use category from lib without setting -all_load or -ObjC flag. He just add to category h/m files empty dummy class interface/implementation to force linker use this file. And yes, this trick do the job.

But author also said he even not instantiated dummy object. Mm… As I've found we should explicitly call some "real" code from category file. So at least class function should be called.
And we even need not dummy class. Single c function do the same.

So if we write lib files as:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

and if we call useMyLib(); anywhere in App project
then in any class we can use logSelf category method;

[self logSelf];

And more blogs on theme:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

牵强ㄟ 2024-09-03 21:01:28

Vladimir 的回答实际上非常好,但是,我想在这里提供更多背景知识。也许有一天有人会发现我的回复并可能会发现它有帮助。

编译器将源文件(.c、.cc、.cpp、.m)转换为目标文件(.o)。每个源文件有一个目标文件。目标文件包含符号、代码和数据。目标文件不能直接被操作系统使用。

现在,当构建动态库(.dylib)、框架、可加载包(.bundle)或可执行二进制文件时,这些目标文件由链接器链接在一起,以生成操作系统认为“可用”的东西,例如它可以直接加载到特定的内存地址。

然而,在构建静态库时,所有这些目标文件都被简单地添加到一个大的存档文件中,因此静态库的扩展名(.a 表示存档)。因此 .a 文件只不过是对象 (.o) 文件的存档。想象一下未经压缩的 TAR 存档或 ZIP 存档。复制单个 .a 文件比复制一大堆 .o 文件更容易(类似于 Java,将 .class 文件打包到 .jar 存档中以便于分发)。

将二进制文件链接到静态库(= 存档)时,链接器将获取存档中所有符号的表,并检查二进制文件引用了哪些符号。只有包含引用符号的目标文件才会被链接器实际加载并被链接过程考虑。例如,如果您的存档有 50 个目标文件,但只有 20 个包含二进制文件使用的符号,则只有这 20 个由链接器加载,其他 30 个在链接过程中完全被忽略。

这对于 C 和 C++ 代码非常有效,因为这些语言尝试在编译时执行尽可能多的操作(尽管 C++ 也有一些仅限运行时的功能)。然而,Obj-C 是一种不同类型的语言。 Obj-C 严重依赖于运行时功能,并且许多 Obj-C 功能实际上只是运行时功能。 Obj-C 类实际上具有与 C 函数或全局 C 变量相当的符号(至少在当前的 Obj-C 运行时)。链接器可以查看某个类是否被引用,因此可以确定某个类是否正在使用。如果您使用静态库中目标文件中的类,则链接器将加载该目标文件,因为链接器会看到正在使用的符号。类别是仅运行时的功能,类别不是类或函数之类的符号,这也意味着链接器无法确定类别是否正在使用。

如果链接器加载包含 Obj-C 代码的目标文件,则它的所有 Obj-C 部分始终是链接阶段的一部分。因此,如果加载包含类别的目标文件,因为其中的任何符号都被视为“正在使用”(无论是类、函数还是全局变量),类别也会被加载,并且在运行时可用。然而,如果目标文件本身未加载,则其中的类别在运行时将不可用。 类别的目标文件从不加载,因为它不包含链接器永远认为“正在使用”的符号 ”。这就是整个问题所在。

已经提出了几种解决方案,现在您知道所有这些如何一起发挥作用,让我们再看看建议的解决方案:

  1. 一个解决方案是将 -all_load 添加到链接器调用中。实际上,它告诉链接器以下“加载所有存档的所有目标文件,无论您是否看到任何正在使用的符号”。当然,这会起作用;但它也可能会产生相当大的二进制文件。

  2. 另一个解决方案是将 -force_load 添加到链接器调用中,包括存档的路径。此标志的工作方式与 -all_load 完全相同,但仅适用于指定的存档。当然这也行。

  3. 最流行的解决方案是将 -ObjC 添加到链接器调用中。该标志告诉链接器“如果您发现它们包含任何 Obj-C 代码,则从所有存档中加载所有目标文件”。 “任何 Obj-C 代码”都包含类别。这也可以工作,并且不会强制加载不包含 Obj-C 代码的目标文件(这些文件仍然仅按需加载)。

  4. 另一个解决方案是相当新的 Xcode 构建设置执行单对象预链接。如果启用,所有目标文件(请记住,每个源文件都有一个)会合并到一个目标文件中(这不是真正的链接,因此名称为 PreLink),并且此单个目标文件 (有时也称为“主目标文件”)然后添加到存档中。如果现在主对象文件的任何符号被认为正在使用,则整个主对象文件被认为正在使用,因此它的所有 Objective-C 部分总是被加载。由于类是普通符号,因此使用此类静态库中的单个类就足以获取所有类别。

  5. 最终的解决方案是弗拉基米尔在他的答案最后添加的技巧。将“假符号”放入仅声明类别的任何源文件中。如果您想在运行时使用任何类别,请确保在编译时以某种方式引用假符号,因为这会导致链接器加载目标文件,从而导致所有 Obj-C 加载里面的代码。例如,它可以是一个具有空函数体的函数(调用时不会执行任何操作),也可以是访问的全局变量(例如,一旦读取或写入一次,全局 int 就足够了) 。与上述所有其他解决方案不同,此解决方案将运行时可用类别的控制权转移给已编译的代码(如果希望它们链接并可用,则它访问符号,否则它不访问符号,链接器将忽略它)。

这就是大家。

哦,等等,还有一件事:
链接器有一个名为 -dead_strip 的选项。这个选项有什么作用?如果链接器决定加载目标文件,则目标文件的所有符号都将成为链接二进制文件的一部分,无论是否使用它们。例如,一个目标文件包含 100 个函数,但二进制文件仅使用其中一个,所有 100 个函数仍会添加到二进制文件中,因为目标文件要么作为一个整体添加,要么根本不添加。链接器通常不支持部分添加目标文件。

但是,如果您告诉链接器“死条”,链接器将首先将所有目标文件添加到二进制文件中,解析所有引用,最后扫描二进制文件中未使用的符号(或仅由不在其中的其他符号使用)使用)。作为优化阶段的一部分,所有未使用的符号都会被删除。在上面的例子中,99个未使用的功能再次被删除。如果您使用 -load_all-force_loadPerform Single-Object Prelink 等选项,这将非常有用,因为这些选项可以轻松增大二进制大小在某些情况下,死剥离将再次删除未使用的代码和数据。

死剥离对于 C 代码非常有效(例如,按预期删除未使用的函数、变量和常量),对于 C++ 也非常有效(例如,删除未使用的类)。它并不完美,在某些情况下,即使可以删除某些符号,也不会删除它们,但在大多数情况下,它对于这些语言来说效果很好。

那么 Obj-C 呢?忘了它吧! Obj-C 没有死区剥离。由于 Obj-C 是一种运行时功能语言,因此编译器无法在编译时判断某个符号是否真正在使用。例如,如果没有代码直接引用 Obj-C 类,则该类未被使用,对吗?错误的!您可以动态构建包含类名的字符串,请求该名称的类指针并动态分配该类。例如,

MyCoolClass * mcc = [[MyCoolClass alloc] init];

我也可以写

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

在这两种情况下 mmc 是对类“MyCoolClass”的对象的引用,但在第二个中没有对该类的直接引用代码示例(甚至不是静态字符串形式的类名)。一切都只在运行时发生。即使类实际上是真正的符号。对于类别来说情况更糟,因为它们甚至不是真正的符号。

因此,如果您有一个包含数百个对象的静态库,但大多数二进制文件只需要其中的几个,您可能不喜欢使用上面的解决方案 (1) 到 (4)。否则,您最终会得到包含所有这些类的非常大的二进制文件,即使它们中的大多数从未使用过。对于类,您通常根本不需要任何特殊的解决方案,因为类具有真正的符号,只要您直接引用它们(不像第二个代码示例中那样),链接器就会自行很好地识别它们的用法。不过,对于类别,请考虑解决方案 (5),因为它可以仅包含您真正需要的类别。

例如,如果您想要 NSData 的类别,例如向其添加压缩/解压缩方法,您将创建一个头文件:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

和一个实现文件

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

现在只需确保代码中的任何位置 import_NSData_Compression()被称为。调用地点或调用频率并不重要。实际上根本不需要调用它,如果链接器这么认为就足够了。例如,您可以将以下代码放在项目中的任何位置:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

您不必在代码中调用 importCategories(),该属性将使编译器和链接器相信它被调用,即使在情况并非如此。

最后的提示:
如果将 -whyload 添加到最终的链接调用,链接器将在构建日志中打印由于使用了哪个符号而从哪个库加载了哪个对象文件。它只会打印考虑使用的第一个符号,但这不一定是该目标文件使用的唯一符号。

The answer from Vladimir is actually pretty good, however, I'd like to give some more background knowledge here. Maybe one day somebody finds my reply and may find it helpful.

The compiler transforms source files (.c, .cc, .cpp, .m) into object files (.o). There is one object file per source file. Object files contain symbols, code and data. Object files are not directly usable by the operating system.

Now when building a dynamic library (.dylib), a framework, a loadable bundle (.bundle) or an executable binary, these object files are linked together by the linker to produce something the operating system considers "usable", e.g. something it can directly load to a specific memory address.

However when building a static library, all these object files are simply added to a big archive file, hence the extension of static libraries (.a for archive). So an .a file is nothing than an archive of object (.o) files. Think of a TAR archive or a ZIP archive without compression. It's just easier to copy a single .a file around than a whole bunch of .o files (similar to Java, where you pack .class files into a .jar archive for easy distribution).

When linking a binary to a static library (= archive), the linker will get a table of all symbols in the archive and check which of these symbols are referenced by the binaries. Only the object files containing referenced symbols are actually loaded by the linker and are considered by the linking process. E.g. if your archive has 50 object files, but only 20 contain symbols used by the binary, only those 20 are loaded by the linker, the other 30 are entirely ignored in the linking process.

This works quite well for C and C++ code, as these languages try to do as much as possible at compile time (though C++ also has some runtime-only features). Obj-C, however, is a different kind of language. Obj-C heavily depends on runtime features and many Obj-C features are actually runtime-only features. Obj-C classes actually have symbols comparable to C functions or global C variables (at least in current Obj-C runtime). A linker can see if a class is referenced or not, so it can determine a class being in use or not. If you use a class from an object file in a static library, this object file will be loaded by the linker because the linker sees a symbol being in use. Categories are a runtime-only feature, categories aren't symbols like classes or functions and that also means a linker cannot determine if a category is in use or not.

If the linker loads an object file containing Obj-C code, all Obj-C parts of it are always part of the linking stage. So if an object file containing categories is loaded because any symbol from it is considered "in use" (be it a class, be it a function, be it a global variable), the categories are loaded as well and will be available at runtime. Yet if the object file itself is not loaded, the categories in it will not be available at runtime. An object file containing only categories is never loaded because it contains no symbols the linker would ever consider "in use". And this is the whole problem here.

Several solutions have been proposed and now that you know how all this plays together, let's have another look on the proposed solution:

  1. One solution is to add -all_load to the linker call. Actually it tells the linker the following "Load all object files of all archives regardless if you see any symbol in use or not'. Of course, that will work; but it may also produce rather big binaries.

  2. Another solution is to add -force_load to the linker call including the path to the archive. This flag works exactly like -all_load, but only for the specified archive. Of course this will work as well.

  3. The most popular solution is to add -ObjC to the linker call. This flag tells the linker "Load all object files from all archives if you see that they contain any Obj-C code". And "any Obj-C code" includes categories. This will work as well and it will not force loading of object files containing no Obj-C code (these are still only loaded on demand).

  4. Another solution is the rather new Xcode build setting Perform Single-Object Prelink. If enabled, all the object files (remember, there is one per source file) are merged together into a single object file (that is not real linking, hence the name PreLink) and this single object file (sometimes also called a "master object file") is then added to the archive. If now any symbol of the master object file is considered in use, the whole master object file is considered in use and thus all Objective-C parts of it are always loaded. And since classes are normal symbols, it's enough to use a single class from such a static library to also get all the categories.

  5. The final solution is the trick Vladimir added at the very end of his answer. Place a "fake symbol" into any source file declaring only categories. If you want to use any of the categories at runtime, make sure you somehow reference the fake symbol at compile time, as this causes the object file to be loaded by the linker and thus also all Obj-C code in it. E.g. it could be a function with an empty function body (which will do nothing when being called) or it could be a global variable accessed (e.g. a global int once read or once written, this is sufficient). Unlike all other solutions above, this solution shifts control about which categories are available at runtime to the compiled code (if it wants them to be linked and available, it accesses the symbol, otherwise it doesn't access the symbol and the linker will ignore it).

That's all folks.

Oh, wait, there's one more thing:
The linker has an option named -dead_strip. What does this option do? If the linker decided to load an object file, all symbols of the object file become part of the linked binary, whether they are used or not. E.g. an object file contains 100 functions, but only one of them is used by the binary, all 100 functions are still added to the binary because object files are either added as a whole or they are not added at all. Adding an object file partially is usually not supported by linkers.

However, if you tell the linker to "dead strip", the linker will first add all the object files to the binary, resolve all the references and finally scan the binary for symbols not in use (or only in use by other symbols not in use). All the symbols found to be not in use are then removed as part of the optimization stage. In the example above, the 99 unused functions are removed again. This is very useful if you use options like -load_all, -force_load or Perform Single-Object Prelink because these options can easily blow up binary sizes dramatically in some cases and the dead stripping will remove unused code and data again.

Dead stripping works very well for C code (e.g. unused functions, variables and constants are removed as expected) and it also works quite good for C++ (e.g. unused classes are removed). It is not perfect, in some cases some symbols are not removed even though it would be okay to remove them, but in most cases it works quite well for these languages.

What about Obj-C? Forget about it! There is no dead stripping for Obj-C. As Obj-C is a runtime-feature language, the compiler cannot say at compile time whether a symbol is really in use or not. E.g. an Obj-C class is not in use if there is no code directly referencing it, correct? Wrong! You can dynamically build a string containing a class name, request a class pointer for that name and dynamically allocate the class. E.g. instead of

MyCoolClass * mcc = [[MyCoolClass alloc] init];

I could also write

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

In both cases mmc is a reference to an object of the class "MyCoolClass", but there is no direct reference to this class in the second code sample (not even the class name as a static string). Everything happens only at runtime. And that's even though classes are actually real symbols. It's even worse for categories, as they are not even real symbols.

So if you have a static library with hundreds of objects, yet most of your binaries only need a few of them, you may prefer not to use the solutions (1) to (4) above. Otherwise you end up with very big binaries containing all these classes, even though most of them are never used. For classes you usually don't need any special solution at all since classes have real symbols and as long as you reference them directly (not as in the second code sample), the linker will identify their usage pretty well on its own. For categories, though, consider solution (5), as it makes it possible to only include the categories you really need.

E.g. if you want a category for NSData, e.g. adding a compression/decompression method to it, you'd create a header file:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

and an implementation file

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

Now just make sure that anywhere in your code import_NSData_Compression() is called. It doesn't matter where it is called or how often it is called. Actually it doesn't really have to be called at all, it's enough if the linker thinks so. E.g. you could put the following code anywhere in your project:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

You don't have to ever call importCategories() in your code, the attribute will make the compiler and linker believe that it is called, even in case it is not.

And a final tip:
If you add -whyload to the final link call, the linker will print in the build log which object file from which library it did load because of which symbol in use. It will only print the first symbol considered in use, but that is not necessarily the only symbol in use of that object file.

一笑百媚生 2024-09-03 21:01:28

此问题已在 LLVM 中修复。该修复程序作为 LLVM 2.9 的一部分提供。包含该修复程序的第一个 Xcode 版本是随 LLVM 3.0 提供的 Xcode 4.2。 使用 XCode 4.2 时不再需要使用 -all_load-force_load 仍然需要 -ObjC

This issue has been fixed in LLVM. The fix ships as part of LLVM 2.9 The first Xcode version to contain the fix is Xcode 4.2 shipping with LLVM 3.0. The usage of -all_load or -force_load is no longer needed when working with XCode 4.2 -ObjC is still needed.

摇划花蜜的午后 2024-09-03 21:01:28

在编译静态库时,您需要执行以下操作才能完全解决此问题:

转到 Xcode Build Settings 并将 Perform Single-Object Prelink 设置为 YES 或
构建配置文件中的GENERATE_MASTER_OBJECT_FILE = YES

默认情况下,链接器为每个 .m 文件生成一个 .o 文件。因此类别会获得不同的 .o 文件。当链接器查看静态库 .o 文件时,它不会创建每个类的所有符号的索引(运行时会创建,无论如何)。

该指令将要求链接器将所有对象打包到一个大的 .o 文件中,从而强制处理静态库的链接器获取所有类类别的索引。

希望能澄清这一点。

Here's what you need to do to resolve this problem completely when compiling your static library:

Either go to Xcode Build Settings and set Perform Single-Object Prelink to YES or
GENERATE_MASTER_OBJECT_FILE = YES in your build configuration file.

By default,the linker generates an .o file for each .m file. So categories gets different .o files. When the linker looks at a static library .o files, it doesn't create an index of all symbols per class (Runtime will, doesn't matter what).

This directive will ask the linker to pack all objects together into one big .o file and by this it forces the linker that process the static library to get index all class categories.

Hope that clarifies it.

一抹微笑 2024-09-03 21:01:28

每当静态库链接讨论出现时,很少提到的一个因素是,您还必须在构建阶段中包含类别本身 - >复制文件并编译静态库本身的源代码

Apple 在最近发布的在中使用静态库也没有强调这一事实iOS 也可以。

我花了一整天的时间尝试了 -objC 和 -all_load 等的各种变体..但没有任何结果.. 这个问题引起了我的注意。 (不要误会我的意思......你仍然需要做 -objC 的事情......但它不仅仅是这样)。

另一个一直对我有帮助的动作是我总是首先自己构建包含的静态库..然后我构建封闭的应用程序..

One factor that is rarely mentioned whenever the static library linking discussion comes up is the fact that you must also include the categories themselves in the build phases->copy files and compile sources of the static library itself.

Apple also doesn't emphasize this fact in their recently published Using Static Libraries in iOS either.

I spent a whole day trying all sorts of variations of -objC and -all_load etc.. but nothing came out of it.. this question brought that issue to my attention. (don't get me wrong.. you still have to do the -objC stuff.. but it's more than just that).

also another action that has always helped me is that I always build the included static library first on its own.. then i build the enclosing application..

川水往事 2024-09-03 21:01:28

您可能需要在静态库的“公共”标头中包含该类别:#import“MyStaticLib.h”

You probably need to have the category in you're static library's "public" header: #import "MyStaticLib.h"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文