{"id":2610,"date":"2022-08-30T15:26:18","date_gmt":"2022-08-30T15:26:18","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2014\/02\/04\/c-as-principal-class-or-a-cocoa-app-without-objc-collection-of-common-programming-errors\/"},"modified":"2022-08-30T15:26:18","modified_gmt":"2022-08-30T15:26:18","slug":"c-as-principal-class-or-a-cocoa-app-without-objc-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2022\/08\/30\/c-as-principal-class-or-a-cocoa-app-without-objc-collection-of-common-programming-errors\/","title":{"rendered":"C As Principal Class Or; A Cocoa App Without ObjC-Collection of common programming errors"},"content":{"rendered":"<p>Okay, here is a substantially rewritten answer that seems to work for me.<\/p>\n<p>So, your problems are pretty unrelated to the principal class. You should leave the principal class as <code>NSApplication<\/code>.<\/p>\n<p>The major problem, as I alluded to before, is that you don&#8217;t register an appropriate application delegate with NSApplication. Changing the principal class will not fix this. A NIB file can set the application delegate, but that&#8217;s really overkill for this problem.<\/p>\n<p>However, there are actually several problems:<\/p>\n<ol>\n<li>\n<p>Your (posted) code is written using some iOS classes and methods that don&#8217;t quite line up with the OS X versions.<\/p>\n<\/li>\n<li>\n<p>You have to make sure that your AppDelegate class is registered in the system, and then you have to manually initialize NSApplication and set its application delegate.<\/p>\n<\/li>\n<li>\n<p>Linking turns out to be <strong>very very<\/strong> important here. You need to have some external symbol that makes the linker drag the Foundation kit and the AppKit into memory. Otherwise, there&#8217;s no easy way to register classes like <code>NSObject<\/code> with the Objective-C runtime.<\/p>\n<\/li>\n<\/ol>\n<h2>iOS v OSX classes<\/h2>\n<p>OSX application delegates derive from <code>NSObject<\/code>, not from <code>UIResponder<\/code>, so the line:<\/p>\n<pre><code>AppDelClass = objc_allocateClassPair((Class) objc_getClass(\"UIResponder\"), \"AppDelegate\", 0);\n<\/code><\/pre>\n<p>should read:<\/p>\n<pre><code>AppDelClass = objc_allocateClassPair((Class) objc_getClass(\"NSObject\"), \"AppDelegate\", 0);\n<\/code><\/pre>\n<p>Also, OSX application delegates respond to different messages than iOS application delegates. In particular, they need to respond to the <code>applicationDidFinishLaunching:<\/code> selector (which takes an <code>NSNotifier<\/code> object of type <code>id<\/code>).<\/p>\n<p>The line<\/p>\n<pre><code>class_addMethod(AppDelClass, sel_getUid(\"application:didFinishLaunchingWithOptions:\"), (IMP) AppDel_didFinishLaunching, \"i@:@@\");\n<\/code><\/pre>\n<p>should read:<\/p>\n<pre><code>class_addMethod(AppDelClass, sel_getUid(\"applicationDidFinishLaunching:\"), (IMP) AppDel_didFinishLaunching, \"i@:@\");\n<\/code><\/pre>\n<p>Notice that the parameters (<code>\"i@:@@\"<\/code> and <code>\"i@:@\"<\/code>) are different.<\/p>\n<h2>Setting up NSApplication<\/h2>\n<p>There are two choices for registering <code>AppDelegate<\/code> with the Objective-C runtime:<\/p>\n<ol>\n<li>\n<p>Either declare your <code>initAppDel<\/code> method with <code>__attribute__((constructor))<\/code>, which forces it to be called by the setup code before <code>main<\/code>, or<\/p>\n<\/li>\n<li>\n<p>Call it yourself before you instantiate the object.<\/p>\n<\/li>\n<\/ol>\n<p>I generally don&#8217;t trust <code>__attribute__<\/code> labels. They&#8217;ll probably stay the same, but Apple might change them. I&#8217;ve chosen to call <code>initAppDel<\/code> from <code>main<\/code>.<\/p>\n<p>Once you register your <code>AppDelegate<\/code> class with the system, it basically works like an Objective-C class. You instantiate it like an Objective-C class, and you can pass it around like an <code>id<\/code>. It actually is an <code>id<\/code>.<\/p>\n<p>To make sure that AppDelegate is run as the application delegate, you have to set up <code>NSAppliction<\/code>. Because you&#8217;re rolling your own application delegate, you really cannot use <code>NSApplicationMain<\/code> to do this. It actually turned out to not be that hard:<\/p>\n<pre><code>void init_app(void)\n{\n  objc_msgSend(\n      objc_getClass(\"NSApplication\"), \n      sel_getUid(\"sharedApplication\"));\n\n  if (NSApp == NULL)\n  {\n    fprintf(stderr,\"Failed to initialized NSApplication... terminating...\\n\");\n    return;\n  }\n\n  id appDelObj = objc_msgSend(\n      objc_getClass(\"AppDelegate\"), \n      sel_getUid(\"alloc\"));\n  appDelObj = objc_msgSend(appDelObj, sel_getUid(\"init\"));\n\n  objc_msgSend(NSApp, sel_getUid(\"setDelegate:\"), appDelObj);\n  objc_msgSend(NSApp, sel_getUid(\"run\"));\n}\n<\/code><\/pre>\n<h2>Linking and the Objective-C runtime<\/h2>\n<p>So, here&#8217;s the real meat of the problem, at least for me. All of the above will fail, utterly and completely, unless you actually have <code>NSObject<\/code> and <code>NSApplication<\/code> registered in the Objective-C runtime.<\/p>\n<p>Usually, if you&#8217;re working in Objective-C, the compiler tells the linker that it needs that. It does this by putting a bunch of special unresolved symbols in the <code>.o<\/code> file. If I compile the file SomeObj.m:<\/p>\n<pre><code>#import \n\n@interface SomeObject : NSObject\n@end\n\n@implementation SomeObject\n@end\n<\/code><\/pre>\n<p>Using <code>clang -c SomeObj.m<\/code>, and then look at the symbols using <code>nm SomeObj.o<\/code>:<\/p>\n<pre><code>0000000000000000 s L_OBJC_CLASS_NAME_\n                 U _OBJC_CLASS_$_NSObject\n00000000000000c8 S _OBJC_CLASS_$_SomeObject\n                 U _OBJC_METACLASS_$_NSObject\n00000000000000a0 S _OBJC_METACLASS_$_SomeObject\n                 U __objc_empty_cache\n                 U __objc_empty_vtable\n0000000000000058 s l_OBJC_CLASS_RO_$_SomeObject\n0000000000000010 s l_OBJC_METACLASS_RO_$_SomeObject\n<\/code><\/pre>\n<p>You&#8217;ll see all those nice <code>_OBJC_CLASS_$_<\/code> symbols with a <code>U<\/code> to the left, indicating that the symbols are unresolved. When you link this file, the linker takes this and then realizes that it has to load the Foundation framework to resolve the references. This forces the Foundation framework to register all of its classes with the Objective-C runtime. Something similar is required if your code needs the AppKit framework.<\/p>\n<p>If I compile your AppDelegate code, which I&#8217;ve renamed &#8216;AppDelegate_orig.c&#8217; with <code>clang -c AppDelegate_orig.c<\/code> and then run <code>nm<\/code> on it:<\/p>\n<pre><code>00000000000001b8 s EH_frame0\n000000000000013c s L_.str\n0000000000000145 s L_.str1\n000000000000014b s L_.str2\n0000000000000150 s L_.str3\n0000000000000166 s L_.str4\n0000000000000172 s L_.str5\n000000000000017e s L_.str6\n00000000000001a9 s L_.str7\n0000000000000008 C _AppDelClass\n0000000000000000 T _AppDel_didFinishLaunching\n00000000000001d0 S _AppDel_didFinishLaunching.eh\n                 U _class_addMethod\n00000000000000c0 t _initAppDel\n00000000000001f8 s _initAppDel.eh\n                 U _objc_allocateClassPair\n                 U _objc_getClass\n                 U _objc_msgSend\n                 U _objc_registerClassPair\n                 U _sel_getUid\n<\/code><\/pre>\n<p>You&#8217;ll see that there are no unresolved symbols that would force the Foundation or AppKit frameworks to link. This means that all my calls to <code>objc_getClass<\/code> would return NULL, which means that the whole thing comes crashing down.<\/p>\n<p>I don&#8217;t know what the rest of your code looks like, so this may not be an issue for you, but solving this problem let me compile a modified AppDelegate.c file <strong>by itself<\/strong> into a (not very functional) OSX application.<\/p>\n<p>The secret here is to find an external symbol that requires the linker to bring in Foundation and AppKit. That turned out to be relatively easy. The AppKit provides a global variable <code>NSApp<\/code> which hold the application&#8217;s instance of <code>NSApplication<\/code>. The AppKit framework relies on the Foundation framework, so we get that for free. Simply declaring an external reference to this was enough:<\/p>\n<pre><code>extern id NSApp;\n<\/code><\/pre>\n<p>(NB: You have to actually use the variable somewhere, or the compiler may optimize it away, and you&#8217;ll lose the frameworks that you need.)<\/p>\n<h2>Code and screenshot:<\/h2>\n<p>Here is my version of AppDelegate.c. It includes <code>main<\/code> and should set everything up. The result isn&#8217;t all that exciting, but it does open a tiny window on the screen.<\/p>\n<pre><code>#include \n#include \n\n#include \n#include \n\nextern id NSApp;\n\nstruct AppDel\n{\n    Class isa;\n    id window;\n};\n\n\n\/\/ This is a strong reference to the class of the AppDelegate\n\/\/ (same as [AppDelegate class])\nClass AppDelClass;\n\nBOOL AppDel_didFinishLaunching(struct AppDel *self, SEL _cmd, id notification) {\n    self-&gt;window = objc_msgSend(objc_getClass(\"NSWindow\"),\n      sel_getUid(\"alloc\"));\n\n    self-&gt;window = objc_msgSend(self-&gt;window, \n      sel_getUid(\"init\"));\n\n    objc_msgSend(self-&gt;window, \n      sel_getUid(\"makeKeyAndOrderFront:\"),\n      self);\n\n    return YES;\n}\n\nstatic void initAppDel() \n{\n  AppDelClass = objc_allocateClassPair((Class)\n    objc_getClass(\"NSObject\"), \"AppDelegate\", 0);\n\n  class_addMethod(AppDelClass, \n      sel_getUid(\"applicationDidFinishLaunching:\"), \n      (IMP) AppDel_didFinishLaunching, \"i@:@\");\n\n  objc_registerClassPair(AppDelClass);\n}\n\nvoid init_app(void)\n{\n  objc_msgSend(\n      objc_getClass(\"NSApplication\"), \n      sel_getUid(\"sharedApplication\"));\n\n  if (NSApp == NULL)\n  {\n    fprintf(stderr,\"Failed to initialized NSApplication...  terminating...\\n\");\n    return;\n  }\n\n  id appDelObj = objc_msgSend(\n      objc_getClass(\"AppDelegate\"), \n      sel_getUid(\"alloc\"));\n  appDelObj = objc_msgSend(appDelObj, sel_getUid(\"init\"));\n\n  objc_msgSend(NSApp, sel_getUid(\"setDelegate:\"), appDelObj);\n  objc_msgSend(NSApp, sel_getUid(\"run\"));\n}\n\n\nint main(int argc, char** argv)\n{\n  initAppDel();\n  init_app();\n  return EXIT_SUCCESS;\n}\n<\/code><\/pre>\n<p>Compile, install, and run like this:<\/p>\n<pre><code>clang -g -o AppInC AppDelegate.c -lobjc -framework Foundation -framework AppKit\nmkdir -p AppInC.app\/Contents\/MacOS\ncp AppInC AppInC.app\/Contents\/MacOS\/\ncp Info.plist AppInC.app\/Contents\/\nopen .\/AppInC.app\n<\/code><\/pre>\n<p>Info.plist is:<\/p>\n<pre><code>\n\n\n\n        CFBundleDevelopmentRegion\n        en\n        CFBundleExecutable\n        AppInC\n        CFBundleIconFile\n        \n        CFBundleIdentifier\n        com.foo.AppInC\n        CFBundleInfoDictionaryVersion\n        6.0\n        CFBundleName\n        AppInC\n        CFBundlePackageType\n        APPL\n        CFBundleShortVersionString\n        1.0\n        CFBundleSignature\n        ????\n        CFBundleVersion\n        1\n        LSApplicationCategoryType\n        public.app-category.games\n        LSMinimumSystemVersion\n        \n        NSPrincipalClass\n        NSApplication\n\n\n<\/code><\/pre>\n<p>With a screenshot:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.stack.imgur.com\/sHKAW.jpg\" \/><\/p>\n<p id=\"rop\"><small>Originally posted 2014-02-04 09:25:45. <\/small><\/p>","protected":false},"excerpt":{"rendered":"<p>Okay, here is a substantially rewritten answer that seems to work for me. So, your problems are pretty unrelated to the principal class. You should leave the principal class as NSApplication. The major problem, as I alluded to before, is that you don&#8217;t register an appropriate application delegate with NSApplication. Changing the principal class will [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2610","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/2610","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=2610"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/2610\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=2610"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=2610"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=2610"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}