Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wants to merge 1 commit into from

Conversation

ohhorob
Copy link

@ohhorob ohhorob commented May 17, 2011

  • 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

- 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
@johnezang
Copy link
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
Copy link
Author

ohhorob commented May 17, 2011

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
Copy link
Author

ohhorob commented May 17, 2011

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
Copy link
Author

ohhorob commented May 18, 2011

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
Copy link
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
Copy link
Author

ohhorob commented May 18, 2011

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

@johnezang
Copy link
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
Copy link
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 added a commit that referenced this pull request May 21, 2011
, 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.
@johnezang
Copy link
Owner

@ohhorob, I just committed a change (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?

@ghost ghost assigned johnezang May 21, 2011
johnezang added a commit that referenced this pull request May 21, 2011
…meInitialization(). Missed the JSONDecoder +load stuff on the last commit. Related to issue #23.
@johnezang
Copy link
Owner

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

@ohhorob
Copy link
Author

ohhorob commented May 24, 2011

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
Copy link

0xced commented May 25, 2011

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 pushed a commit to jasongregori/JSONKit that referenced this pull request Sep 23, 2011
…ang#23.  For issue johnezang#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.
jasongregori pushed a commit to jasongregori/JSONKit that referenced this pull request Sep 23, 2011
…meInitialization(). Missed the JSONDecoder +load stuff on the last commit. Related to issue johnezang#23.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants