Workaround for JSONKit used in a static library in the device runtime #23

Closed
wants to merge 1 commit into
from

Projects

None yet

3 participants

@ohhorob
  • NSObject class methods +load and +initialize are not sent to JSONKit classes when they're packaged in a static library
  • Testing the static variables before requiring them forces the class to "load" just in time
@ohhorob ohhorob Workaround for JSONKit used in a static library in the device runtime
- NSObject class methods +load and +initialize are not sent to JSONKit classes when they're packaged in a static library
- Testing the static variables before requiring them forces the class to "load" just in time
5bf0921
@johnezang
Owner

Can you give some more details about when this happens? The proposed fixes should not be required, regardless of whether or not JSONKit is compiled directly in to the app, built as a dynamic library, or a static library. If this is really happening, there's a bug in some part of the compiler/linker toolchain, or the OS linker / objc runtime.

What version of the OS(s) do you observe this behavior on?

Can you give an example of how you build JSONKit as a static library, and how you link it to your application?

@ohhorob

I have the JSONKit source compiling into my static library target. The header is copied as Public, but the JKDictionary nor JKArray get a +load or +initialize message when the app is run on the device. Other than these load-time messages, everything works perfectly.

When run on the simulator, the +load and +initialize are being sent by the runtime (or loader?), and it works without the changes. I tried using +initialize to see if that would get the job done, but that message doesn't come through either.

I'll pull together an example project this evening and put it in a public repo on my GitHub account.

@ohhorob

Hi John, I finally got around to whipping up the demo project.. https://github.com/ohhorob/JSONKit-in-framework-demo

I've only done a quick and dirty workaround that allows me to keep working on my other project. I'm sure if you considered other strategies you would come up with a better workaround. And if you have a better understanding of the device runtime, it may be that a fix without code changes may be available?

Rob.

@ohhorob

I've also put together a question on Stack Overflow, in case it is a problem with the static lib/framework packaging rather than the Objective-C runtime. http://stackoverflow.com/questions/6047903/voidload-message-not-sent-to-framework-class-in-device-runtime

@johnezang
Owner

This may be a "better" solution. Can you give it a try and see if it works? It's harmless if the static variables it sets get set more than once- they will (at least, should) always get set to the exact same values.

Place this near jk_min (~ line 1081 or so). It doesn't matter exactly where you put it, just as long as it's after the declarations for the static variables it updates.

extern void jk_hackInit(void) __attribute__ ((constructor));

void jk_hackInit(void) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at +load time may be less than ideal.

  _JKArrayClass        = objc_getClass("JKArray");
  _JKArrayInstanceSize = jk_max(16UL, class_getInstanceSize(_JKArrayClass));

  _JKDictionaryClass        = objc_getClass("JKDictionary");
  _JKDictionaryInstanceSize = jk_max(16UL, class_getInstanceSize(_JKDictionaryClass));

  [pool release]; pool = NULL;
}
@ohhorob

That works well. I'm curious as to the runtime behaviour that triggers the execution of that method...

@johnezang
Owner

That works well. I'm curious as to the runtime behaviour that triggers the execution of that method...

The way I suggested works by using special features of the linker so that it executes a block of code when the executable starts. The block of code executes before main() gets called.

You tell the compiler that you want a function called when the executable starts by applying the special __attribute__ ((constructor)) attribute to the function prototype. This tells the compiler that that function needs to be marked in a way so that the linker knows that that is one of the functions that is queued to be called at run time initialization.

The linker is supposed to do the same thing for all +load methods in a class. It shouldn't matter whether or not the file is part of a static library or not.

The following might be useful: Technical Q&A QA1490 Building Objective-C static libraries with categories. The Tech Q&A suggests that you should pass -ObjC -all_load to the linker, or in Xcode by setting the Other Linker Flags to -ObjC -all_load. The addition of -all_load is recommend to work around some linker bugs when linking 64-bit and iOS targets.

Maybe you could try and remove all the work arounds and see if -ObjC -all_load manages to fix the problem. If it does, then it's definitely a bug in the compiler tool chain, and not with JSONKit per se.

@johnezang
Owner

I just saw in your stackoverflow question that you tried the -ObjC -all_load linker flags and still no joy.

According to your stackoverflow question, you opened a bug report with Apple regarding this issue: # 9461567, which I'm including in here for completeness sake.

@johnezang johnezang added a commit that referenced this pull request May 21, 2011
@johnezang Workarounds for issue #19 (the clang stuff) and issue #23. For issue #23
, the code in the collection classes `+load` was removed and placed in a function with the `__attribute__ ((constructor))` attribute.  This is to work around an apparent bug when building JSONKit as a static library for iOS targets.  @ohhorob also opened a bug with apple- # 9461567.
f403357
@johnezang
Owner

@ohhorob, I just committed a change (johnezang/JSONKit@f403357) which removes the +load code and replaces it with the __attribute__ ((constructor)) suggestion above.

Can you verify that the committed code "fixes" the problem for you?

@johnezang johnezang was assigned May 21, 2011
@johnezang johnezang added a commit that referenced this pull request May 21, 2011
@johnezang Moved the +load logic from JSONDecoder in to jk_collectionClassLoadTi…
…meInitialization(). Missed the JSONDecoder +load stuff on the last commit. Related to issue #23.
cab1ea8
@johnezang
Owner

Commit johnezang/JSONKit@cab1ea8 removes the +load code from JSONDecoder and adds it to jk_collectionClassLoadTimeInitialization().

@ohhorob

Thanks John, JSONKit is now functioning properly when packaged in a static library/framework as of commit cab1ea8ed3449beeb6301a7c47f88c5827e9e47d .

@ohhorob ohhorob closed this May 24, 2011
@0xced

cross posted on Stack Overflow

The +load methods are not called because you did not actually create a static library but a Relocatable Object File bundle. If you create the static framework with either make-fmwk or the iOS Universal Framework template then the load methods will be called as expected.

@jasongregori jasongregori added a commit to jasongregori/JSONKit that referenced this pull request Sep 23, 2011
@johnezang Workarounds for issue #19 (the clang stuff) and issue #23. For issue #23
, the code in the collection classes `+load` was removed and placed in a function with the `__attribute__ ((constructor))` attribute.  This is to work around an apparent bug when building JSONKit as a static library for iOS targets.  @ohhorob also opened a bug with apple- # 9461567.
09e1139
@jasongregori jasongregori added a commit to jasongregori/JSONKit that referenced this pull request Sep 23, 2011
@johnezang Moved the +load logic from JSONDecoder in to jk_collectionClassLoadTi…
…meInitialization(). Missed the JSONDecoder +load stuff on the last commit. Related to issue #23.
a2642b9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment