Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Though obviously broken, that code <em>should</em> compile without warning in all cases simply because the compiler doesn't have enough information to know how to warn. When compiled correctly, it generates a completely different linker error only in 64-bit (which is fallout from the new Objective-C ABI, not directly from non-fragile ivars).</p> <p>If you add <code>int main() {}</code> to foo.m and then compile it with the command line <code>gcc -arch x86_64 foo.m -lobjc</code>, the link errors go away because the objc runtime library provides the empty vtable symbols required to complete the link.</p> <p>During compilation, think of each .m file as an isolated compilation unit. When the compiler compiles a .m file, it only has knowledge of what is in that .m file, what is provided by anything imported in that .m file, and -- if a project is configured for it -- what is defined in the project's precompiled header.</p> <p>Thus, when you say in bar.m:</p> <pre><code>@interface foo { float baz; } @end @implementation foo (category) - (float)blah { return baz; } @end int main() {} </code></pre> <p>The compiler has no notion of the declaration in foo.m. The generated code describes a category on class foo that accesses the ivar baz. If that class doesn't exist at link time, an error will be tossed in Now, given your foo.m and bar.m with my addition of a main function as above, let's try some different compilations:</p> <pre><code>gcc -arch i386 foo.m -lobjc Undefined symbols: "_main", referenced from: start in crt1.10.6.o ld: symbol(s) not found collect2: ld returned 1 exit status </code></pre> <p>Makes sense because we didn't define a main() function in foo.m. 64 bit compilation does the same.</p> <pre><code>gcc -arch i386 bar.m -lobjc </code></pre> <p>Compiles <em>and links</em> without warning. To understand why, look at the generated symbols (deleted about a dozen irrelevant ones):</p> <pre><code>nm -a a.out 00001f52 t -[foo(category) blah] 00000000 A .objc_category_name_foo_category </code></pre> <p>So, the binary contains a category named <code>category</code> on class <code>foo</code>. No link error because the linker doesn't actually try to resolve categories. It assumes that the class <code>foo</code> will magically appear before the category is resolved at runtime.</p> <p>You can follow along with the runtime's class/category resolution with an ivar:</p> <pre><code>env OBJC_PRINT_CLASS_SETUP=YES ./a.out objc[498]: CONNECT: pending category 'foo (category)' objc[498]: CONNECT: class 'Object' now connected (root class) objc[498]: CONNECT: class 'Protocol' now connected objc[498]: CONNECT: class 'List' now connected </code></pre> <p>So, the category was marked as pending. The runtime will hook it up as soon as <code>foo</code> comes into existence!</p> <p>Now, 64 bit...</p> <pre><code>gcc -arch x86_64 bar.m -lobjc Undefined symbols: "_OBJC_IVAR_$_foo.baz", referenced from: -[foo(category) blah] in ccvX4uIk.o "_OBJC_CLASS_$_foo", referenced from: l_OBJC_$_CATEGORY_foo_$_category in ccvX4uIk.o objc-class-ref-to-foo in ccvX4uIk.o ld: symbol(s) not found </code></pre> <p>The link errors are because the modern Objective-C ABI actually causes proper symbols to be emitted for instance variables and categories for a variety of reasons, including adding metadata that can help validate programs (as it did in this case).</p> <p>No compilation errors (which is correct behavior) and the link errors make sense. Now, how about linking the two together?</p> <p>In the 32 bit case, everything compiles and links without error. Thus, we'll need to look at the symbols and at the ObjC debugging spew to see what is going on:</p> <pre><code>gcc -arch i386 bar.m foo.m -lobjc nm -a a.out 00001e0f t -[foo method] 00001dea t -[foo(category) blah] 00000000 A .objc_category_name_foo_category 00003070 S .objc_class_name_foo env OBJC_PRINT_CLASS_SETUP=YES ./a.out objc[530]: CONNECT: attaching category 'foo (category)' objc[530]: CONNECT: class 'Object' now connected (root class) objc[530]: CONNECT: class 'Protocol' now connected objc[530]: CONNECT: class 'List' now connected objc[530]: CONNECT: class 'foo' now connected (root class) </code></pre> <p>Aha! Now there is a class <code>foo</code> and the runtime connects the category to the class upon startup. Obviously, the method returning the <code>baz</code> ivar is going to fail spectacularly.</p> <p>The 64 bit linker fails, though:</p> <pre><code>gcc -arch x86_64 bar.m foo.m -lobjc Undefined symbols: "_OBJC_IVAR_$_foo.baz", referenced from: -[foo(category) blah] in ccBHNqzm.o ld: symbol(s) not found collect2: ld returned 1 exit status </code></pre> <p>With the addition of the symbols for instance variables, the linker can now catch situations where a class has been redeclared incorrectly (as was done in the <code>@interface</code> of bar.m).</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload