objective-c - C作为主要类(class);或没有ObjC的 cocoa 应用

标签 objective-c c macos cocoa objective-c-runtime

对于那些怀疑我的理智/浪费时间/能力/动机的人来说,这个东西是"Foundation With A Spoon"项目的一个端口,现在因为它是一个全iC iOS应用程序而臭名昭著。
所以,我有一个C文件,渴望去成为一个全C Mac应用程序的主要类,然而,一系列限制因素阻止了应用程序的启动。目前,该项目只是一个m a in.m和一个名为appdelegate.c的类,因此我在info.plist中输入了“appdelegate”作为主体类的名称,令我完全惊讶的是,日志打印了:
无法找到类:AppDeaveT,退出
这将在iOS中很好地工作,因为main函数接受委托类的名称,并自动处理它,但是NSpApple Stand()不接受这样的参数。
现在,我知道这源于C中没有<指令>的事实,而这正是操作系统所要寻找的,所以我写了一个简单的cc子类,并将它作为主类提供给子进程,并且它非常成功地启动了。我的问题是,如何将c文件设置为mac应用程序中的主要类并使其正确启动?
编辑:解决了!该文件可能是.m(出于某种原因,框架错误),但类对的分配足以忽略不计。您可以下载基于c的mac应用程序here的源代码。快乐挖掘!
代码已经被编写、签名、密封和盖戳,但我所需要的只是围绕PLIST中的NSnpRealPaldCar要求。

最佳答案

好吧,这里有一个实质性的重写答案
我。
所以,你的问题与主要课程无关。你
应将主要类保留为NSApplication
我之前提到的主要问题是你不注册
具有nsapplication的适当应用程序委托。更改
主类不会修复此问题。nib文件可以设置应用程序
委托,但这对这个问题来说实在是太过分了。
然而,实际上存在几个问题:
您的(发布的)代码是使用一些ios类和方法编写的,这些类和方法
不太符合OSX版本。
您必须确保您的AppDeaveT类注册为
该系统,然后您必须手动初始化NSpApple和
设置其应用程序委托。
链接在这里变得非常重要。你需要
有一些使链接器拖动基础工具包的外部符号
并将AppKIT导入内存中。否则,没有简单的注册方式
使用Objest-C运行时的类NSObject
iOS V OSX类
OSX应用程序委托派生自NSObject,而不是
UIResponder,因此行:

AppDelClass = objc_allocateClassPair((Class) objc_getClass("UIResponder"), "AppDelegate", 0);

应改为:
AppDelClass = objc_allocateClassPair((Class) objc_getClass("NSObject"), "AppDelegate", 0);

此外,osx应用程序委托与ios应用程序委托响应不同的消息。尤其是,它们需要响应applicationDidFinishLaunching:选择器(它接受NSNotifier类型的对象)。
线路
class_addMethod(AppDelClass, sel_getUid("application:didFinishLaunchingWithOptions:"), (IMP) AppDel_didFinishLaunching, "i@:@@");

应改为:
class_addMethod(AppDelClass, sel_getUid("applicationDidFinishLaunching:"), (IMP) AppDel_didFinishLaunching, "i@:@");

注意,参数(id"i@:@@")是不同的。
设置nsapplication
用Objut-C运行时注册“cc>”有两种选择:
要么用"i@:@"声明你的AppDelegate方法,
它强制设置代码在initAppDel之前调用它,或者
在实例化对象之前自己调用它。
我一般不信任__attribute__((constructor))标签。他们可能会保持不变,但苹果可能会改变它们。我已选择从main调用__attribute__
一旦你用系统注册了initAppDel类,它基本上是
像一个目标C类一样工作。您可以像objective-c类一样实例化它,并像main一样传递它。它实际上是一个AppDelegate
要确保appdelegate作为应用程序委托运行,必须设置id。因为您正在滚动自己的应用程序委托,所以您确实不能使用id来执行此操作。事实证明并没有那么难:
void init_app(void)
{
  objc_msgSend(
      objc_getClass("NSApplication"), 
      sel_getUid("sharedApplication"));

  if (NSApp == NULL)
  {
    fprintf(stderr,"Failed to initialized NSApplication... terminating...\n");
    return;
  }

  id appDelObj = objc_msgSend(
      objc_getClass("AppDelegate"), 
      sel_getUid("alloc"));
  appDelObj = objc_msgSend(appDelObj, sel_getUid("init"));

  objc_msgSend(NSApp, sel_getUid("setDelegate:"), appDelObj);
  objc_msgSend(NSApp, sel_getUid("run"));
}

链接和Objut-C运行时
所以,至少对我来说,这才是问题的症结所在。所有的
除非你真的
NSApplictionNSApplicationMain在Objtovi-C运行时注册。
通常,如果在objective-c中工作,编译器会告诉链接器
它需要这个。它通过放一堆特别的
NSObject文件中的未解析符号。如果我编译文件someobj.m:
#import <Foundation/NSObject.h>

@interface SomeObject : NSObject
@end

@implementation SomeObject
@end

使用NSApplication,然后使用.o查看符号:
0000000000000000 s L_OBJC_CLASS_NAME_
                 U _OBJC_CLASS_$_NSObject
00000000000000c8 S _OBJC_CLASS_$_SomeObject
                 U _OBJC_METACLASS_$_NSObject
00000000000000a0 S _OBJC_METACLASS_$_SomeObject
                 U __objc_empty_cache
                 U __objc_empty_vtable
0000000000000058 s l_OBJC_CLASS_RO_$_SomeObject
0000000000000010 s l_OBJC_METACLASS_RO_$_SomeObject

你会看到那些漂亮的符号
左边,表示符号未解析。当你链接
这个文件,链接器接受这个,然后意识到它必须加载。
用于解析引用的基础框架。这迫使
用Objto-C注册所有类的基础框架
运行时。如果您的代码需要appkit,则需要类似的东西
框架。
如果我编译了你的appdelegate代码
“appdelegate\u orig.c”和clang -c SomeObj.m,然后运行
nm SomeObj.o在上面:
00000000000001b8 s EH_frame0
000000000000013c s L_.str
0000000000000145 s L_.str1
000000000000014b s L_.str2
0000000000000150 s L_.str3
0000000000000166 s L_.str4
0000000000000172 s L_.str5
000000000000017e s L_.str6
00000000000001a9 s L_.str7
0000000000000008 C _AppDelClass
0000000000000000 T _AppDel_didFinishLaunching
00000000000001d0 S _AppDel_didFinishLaunching.eh
                 U _class_addMethod
00000000000000c0 t _initAppDel
00000000000001f8 s _initAppDel.eh
                 U _objc_allocateClassPair
                 U _objc_getClass
                 U _objc_msgSend
                 U _objc_registerClassPair
                 U _sel_getUid

您将看到没有未解析的符号会强制
要链接的基础或AppKit框架。这意味着我所有的电话
to_OBJC_CLASS_$_将返回null,这意味着
摔下来了。
我不知道你剩下的代码是什么样子,所以这可能不是
对你来说是个问题,但是解决这个问题让我编译一个
appdelegate.c文件本身进入一个(不是很实用的)OSX
申请。
这里的秘密是找到需要链接器的外部符号
把基金会和appkit带来。结果是相对的
容易的。AppKIT提供了一个全局变量U,它保存
应用程序的clang -c AppDelegate_orig.c实例。AppKit框架依赖于
在基础框架上,我们可以免费得到。简单地声明
一个外部参照就足够了:
extern id NSApp;

(注意:您必须在某个地方实际使用变量,或者编译器
可以优化它,你就会失去你需要的框架。
代码和屏幕截图:
这是我的appdelegate.c版本,它包含nm并且应该设置
一切顺利。结果并不那么令人兴奋,但它确实打开了
屏幕上的小窗口。
#include <stdio.h>
#include <stdlib.h>

#include <objc/runtime.h>
#include <objc/message.h>

extern id NSApp;

struct AppDel
{
    Class isa;
    id window;
};


// This is a strong reference to the class of the AppDelegate
// (same as [AppDelegate class])
Class AppDelClass;

BOOL AppDel_didFinishLaunching(struct AppDel *self, SEL _cmd, id notification) {
    self->window = objc_msgSend(objc_getClass("NSWindow"),
      sel_getUid("alloc"));

    self->window = objc_msgSend(self->window, 
      sel_getUid("init"));

    objc_msgSend(self->window, 
      sel_getUid("makeKeyAndOrderFront:"),
      self);

    return YES;
}

static void initAppDel() 
{
  AppDelClass = objc_allocateClassPair((Class)
    objc_getClass("NSObject"), "AppDelegate", 0);

  class_addMethod(AppDelClass, 
      sel_getUid("applicationDidFinishLaunching:"), 
      (IMP) AppDel_didFinishLaunching, "i@:@");

  objc_registerClassPair(AppDelClass);
}

void init_app(void)
{
  objc_msgSend(
      objc_getClass("NSApplication"), 
      sel_getUid("sharedApplication"));

  if (NSApp == NULL)
  {
    fprintf(stderr,"Failed to initialized NSApplication...  terminating...\n");
    return;
  }

  id appDelObj = objc_msgSend(
      objc_getClass("AppDelegate"), 
      sel_getUid("alloc"));
  appDelObj = objc_msgSend(appDelObj, sel_getUid("init"));

  objc_msgSend(NSApp, sel_getUid("setDelegate:"), appDelObj);
  objc_msgSend(NSApp, sel_getUid("run"));
}


int main(int argc, char** argv)
{
  initAppDel();
  init_app();
  return EXIT_SUCCESS;
}

编译、安装和运行如下:
clang -g -o AppInC AppDelegate.c -lobjc -framework Foundation -framework AppKit
mkdir -p AppInC.app/Contents/MacOS
cp AppInC AppInC.app/Contents/MacOS/
cp Info.plist AppInC.app/Contents/
open ./AppInC.app

信息列表是:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>en</string>
        <key>CFBundleExecutable</key>
        <string>AppInC</string>
        <key>CFBundleIconFile</key>
        <string></string>
        <key>CFBundleIdentifier</key>
        <string>com.foo.AppInC</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>AppInC</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>1.0</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>1</string>
        <key>LSApplicationCategoryType</key>
        <string>public.app-category.games</string>
        <key>LSMinimumSystemVersion</key>
        <string></string>
        <key>NSPrincipalClass</key>
        <string>NSApplication</string>
</dict>
</plist>

屏幕截图:

关于objective-c - C作为主要类(class);或没有ObjC的 cocoa 应用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11319170/

相关文章:

iphone - UIView 内部的 UIScrollView

ios - 扫描特定 CBUUID 时,Core Bluetooth 未找到外围设备

iphone - 如何在iPhone应用程序中进行图像编辑?

c - 递增变量直到溢出的循环结构?

c - 宏定义

c - 在 ARM macOS 上,当显式 raise() 信号时,某些返回地址在堆栈上会出现乱码

Python MacOS 错误 : Unable to revert mtime:/Library/Fonts

objective-c - cocoa /Objective-C : Access a (Boolean) variable in different Classes

Python文件输出添加奇怪的字符

objective-c - NSTask 在 xCode 问题中启动