So schreiben Sie eine iOS-App rein in C

Lesezeit: 15 Minuten

Benutzeravatar von 001
001

Ich habe hier gelesen C vor Objective-C lernen?

Normalerweise ersetze ich dann etwas Obj-C-Code durch reinen C-Code (schließlich kann man sie beliebig mischen, der Inhalt einer Obj-C-Methode kann vollständig reiner C-Code sein)

Ist das wahr?

Ist es möglich, eine iPhone-App rein in der Programmiersprache C zu erstellen?

  • @thilo es ist möglich … mit der objc-Laufzeit

    – Richard J.Ross III

    23. April 2012 um 23:50 Uhr

  • Möglich? Ja. Und völlig sinnlos. Nahezu alle APIs und Muster des iOS-Systems werden von Objective-C- und Objective-C-APIs abgeleitet. Sie werden Ihre Zeit verschwenden; Wenn Sie lernen möchten, wie man iOS programmiert, beginnen Sie mit Objective-C und holen Sie sich C auf dem Weg dorthin.

    – bum

    24. April 2012 um 0:13 Uhr

  • Ein echter Programmierer würde dies mit ARM-Assembler tun.

    – Christoph Johnson

    25. April 2012 um 16:46 Uhr

  • @bbum Ich würde nicht sagen, dass es sinnlos ist. Als ich mein Spiel auf den PC portierte, war ich mehr als glücklich, dass alles in C++ geschrieben war (ja, es ist auch möglich, alles in C++ zu machen). Ich könnte mein Spiel in ein paar Tagen portieren, wenn ich Obj-c überall verwenden würde, würde es Monate dauern.

    – fbafelipe

    28. Juni 2012 um 2:52 Uhr

  • Ich habe nicht im Entferntesten vorgeschlagen, dass Objective-C überall eine Voraussetzung ist. Eine gängige Architektur ist eine portable C++-Engine mit einer manchmal sehr dünnen Schicht von object-c darüber. Die vollständige Vermeidung von OBJC ist Zeitverschwendung; Sie verwenden es, um auf alle Arten von Standard-iOS-Funktionen zuzugreifen, die sogar ein tragbares Spiel nutzen könnte.

    – bum

    1. Juli 2012 um 13:13 Uhr

Benutzeravatar von Richard J. Ross III
Richard J. Ross III

Verdammt, es hat eine Weile gedauert, aber ich habe es verstanden:

Haupt c:

#include <CoreFoundation/CoreFoundation.h>

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

// This is a hack. Because we are writing in C, we cannot out and include 
// <UIKit/UIKit.h>, as that uses Objective-C constructs.
// however, neither can we give the full function declaration, like this:
// int UIApplicationMain (int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);
// So, we rely on the fact that for both the i386 & ARM architectures, 
// the registers for parameters passed in remain the same whether or not 
// you are using VA_ARGS. This is actually the basis of the objective-c 
// runtime (objc_msgSend), so we are probably fine here,  this would be
// the last thing I would expect to break.
extern int UIApplicationMain(int, ...);

// Entry point of the application. If you don't know what this is by now, 
// then you probably shouldn't be reading the rest of this post.
int main(int argc, char *argv[])
{
    // Create an @autoreleasepool, using the old-stye API. 
    // Note that while NSAutoreleasePool IS deprecated, it still exists 
    // in the APIs for a reason, and we leverage that here. In a perfect 
    // world we wouldn't have to worry about this, but, remember, this is C.
    id autoreleasePool = objc_msgSend(objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")), sel_registerName("init"));

    // Notice the use of CFSTR here. We cannot use an objective-c string 
    // literal @"someStr", as that would be using objective-c, obviously.
    UIApplicationMain(argc, argv, nil, CFSTR("AppDelegate"));

    objc_msgSend(autoreleasePool, sel_registerName("drain"));
}

AppDelegate.c:

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

// This is equivalent to creating a @class with one public variable named 'window'.
struct AppDel
{
    Class isa;

    id window;
};

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

// this is the entry point of the application, same as -application:didFinishLaunchingWithOptions:
// note the fact that we use `void *` for the 'application' and 'options' fields, as we need no reference to them for this to work. A generic id would suffice here as well.
BOOL AppDel_didFinishLaunching(struct AppDel *self, SEL _cmd, void *application, void *options)
{
    // we +alloc and -initWithFrame: our window here, so that we can have it show on screen (eventually).
    // this entire method is the objc-runtime based version of the standard View-Based application's launch code, so nothing here really should surprise you.
    // one thing important to note, though is that we use `sel_getUid()` instead of @selector().
    // this is because @selector is an objc language construct, and the application would not have been created in C if I used @selector.
    self->window = objc_msgSend(objc_getClass("UIWindow"), sel_getUid("alloc"));
    self->window = objc_msgSend(self->window, sel_getUid("initWithFrame:"), (struct CGRect) { 0, 0, 320, 480 });

    // here, we are creating our view controller, and our view. note the use of objc_getClass, because we cannot reference UIViewController directly in C.
    id viewController = objc_msgSend(objc_msgSend(objc_getClass("UIViewController"), sel_getUid("alloc")), sel_getUid("init"));

    // creating our custom view class, there really isn't too much 
    // to say here other than we are hard-coding the screen's bounds, 
    // because returning a struct from a `objc_msgSend()` (via 
    // [[UIScreen mainScreen] bounds]) requires a different function call
    // and is finicky at best.
    id view = objc_msgSend(objc_msgSend(objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (struct CGRect) { 0, 0, 320, 480 });

    // here we simply add the view to the view controller, and add the viewController to the window.
    objc_msgSend(objc_msgSend(viewController, sel_getUid("view")), sel_getUid("addSubview:"), view);
    objc_msgSend(self->window, sel_getUid("setRootViewController:"), viewController);

    // finally, we display the window on-screen.
    objc_msgSend(self->window, sel_getUid("makeKeyAndVisible"));

    return YES;
}

// note the use of the gcc attribute extension (constructor). 
// Basically, this lets us run arbitrary code before program startup,
// for more information read here: http://stackoverflow.com/questions/2053029
__attribute__((constructor))
static void initAppDel()
{
    // This is objc-runtime gibberish at best. We are creating a class with the 
    // name "AppDelegate" that is a subclass of "UIResponder". Note we do not need
    // to register for the UIApplicationDelegate protocol, that really is simply for 
    // Xcode's autocomplete, we just need to implement the method and we are golden.
    AppDelClass = objc_allocateClassPair(objc_getClass("UIResponder"), "AppDelegate", 0);

    // Here, we tell the objc runtime that we have a variable named "window" of type 'id'
    class_addIvar(AppDelClass, "window", sizeof(id), 0, "@");

    // We tell the objc-runtime that we have an implementation for the method
    // -application:didFinishLaunchingWithOptions:, and link that to our custom 
    // function defined above. Notice the final parameter. This tells the runtime
    // the types of arguments received by the function.
    class_addMethod(AppDelClass, sel_getUid("application:didFinishLaunchingWithOptions:"), (IMP) AppDel_didFinishLaunching, "i@:@@");

    // Finally we tell the runtime that we have finished describing the class and 
    // we can let the rest of the application use it.
    objc_registerClassPair(AppDelClass);
}

Ansicht.c

#include <objc/runtime.h>

// This is a strong reference to the class of our custom view,
// In case we need it in the future.
Class ViewClass;

// This is a simple -drawRect implementation for our class. We could have 
// used a UILabel  or something of that sort instead, but I felt that this 
// stuck with the C-based mentality of the application.
void View_drawRect(id self, SEL _cmd, struct CGRect rect)
{
    // We are simply getting the graphics context of the current view, 
    // so we can draw to it
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Then we set it's fill color to white so that we clear the background.
    // Note the cast to (CGFloat []). Otherwise, this would give a warning
    //  saying "invalid cast from type 'int' to 'CGFloat *', or 
    // 'extra elements in initializer'. Also note the assumption of RGBA.
    // If this wasn't a demo application, I would strongly recommend against this,
    // but for the most part you can be pretty sure that this is a safe move 
    // in an iOS application.
    CGContextSetFillColor(context, (CGFloat []){ 1, 1, 1, 1 });

    // here, we simply add and draw the rect to the screen
    CGContextAddRect(context, (struct CGRect) { 0, 0, 320, 480 });
    CGContextFillPath(context);

    // and we now set the drawing color to red, then add another rectangle
    // and draw to the screen
    CGContextSetFillColor(context, (CGFloat []) { 1, 0, 0, 1 });
    CGContextAddRect(context, (struct CGRect) { 10, 10, 20, 20 });
    CGContextFillPath(context);
}

// Once again we use the (constructor) attribute. generally speaking, 
// having many of these is a very bad idea, but in a small application 
// like this, it really shouldn't be that big of an issue.
__attribute__((constructor))
static void initView()
{
    // Once again, just like the app delegate, we tell the runtime to 
    // create a new class, this time a subclass of 'UIView' and named 'View'.
    ViewClass = objc_allocateClassPair(objc_getClass("UIView"), "View", 0);

    // and again, we tell the runtime to add a function called -drawRect: 
    // to our custom view. Note that there is an error in the type-specification
    // of this method, as I do not know the @encode sequence of 'CGRect' off 
    // of the top of my head. As a result, there is a chance that the rect 
    // parameter of the method may not get passed properly.
    class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP) View_drawRect, "v@:");

    // And again, we tell the runtime that this class is now valid to be used. 
    // At this point, the application should run and display the screenshot shown below.
    objc_registerClassPair(ViewClass);    
}

Es ist hässlich, aber es funktioniert.

Wenn Sie diese herunterladen möchten, Sie können es von meiner Dropbox bekommen hier

Sie können es aus meinem GitHub-Repository herunterladen hier:

Bildschirmfoto

  • Groß. Um das Erlernen von Objective-C zu vermeiden (was meiner Meinung nach der Kern der Frage war), müssen Sie jetzt die Implementierungsdetails und die C-Level-API der Objective-C-Laufzeit lernen.

    – Thilo

    24. April 2012 um 1:48 Uhr


  • @XCool Ich habe einen Link in den Beitrag eingefügt, wo Sie es bekommen können. Ich habe ein paar kleinere Änderungen, wie das Umbenennen von .m-Dateien in .c-Dateien, aber ansonsten ist es weitgehend gleich.

    – Richard J.Ross III

    25. April 2012 um 13:40 Uhr

  • Wenn Sie sich entschließen, dies gemäß mehreren Vorschlägen in Assembler umzuwandeln, stellen Sie sicher, dass Sie dies in ARM (reguläre und Thumb-Befehlssätze!) und in x86 tun, damit es im Simulator funktioniert. Vielleicht auch PowerPC, wenn Sie es auf Mac OS X v10.4 portieren möchten.

    – Adam Rosenfield

    26. April 2012 um 22:46 Uhr

  • Für was es wert ist, die (CGFloat []) ist kein Cast: Es ist Teil eines zusammengesetzten Literals (eine C99-Funktion).

    – David

    8. Juli 2012 um 9:06 Uhr

  • @Benj Class ist eine Typedef, die in definiert ist objc/runtime.hfür die objc_class struct, die ein undurchsichtiger Zeiger ist.

    – Richard J.Ross III

    25. Oktober 2016 um 23:30 Uhr

Benutzeravatar von CodaFi
CodaFi

Objective-C ist eine Obermenge der C-Sprache, daher ist es theoretisch möglich, ein Programm vollständig in C zu schreiben, es sei denn, Sie sind gründlich damit vertraut OpenGL ESSie müssen mindestens tun etwas objC (Sogar Richs Beispiel enthält einen konstanten NSString*), sonst müssen Sie die Ansichten selbst schreiben.

OK, das oben ist völlig falsch. Lassen Sie mich sagen, ich bin erstaunt, dass Rich dieses hohe Ziel erreicht hat, also habe ich es auf den Mac portiert (source hier). Die folgenden Dateien haben keine Header, verlinken nicht auf Cocoa, noch hat das Projekt einen Nib:

AppDelegate.m

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

extern id NSApp;

struct AppDel
{
    Class isa;

    //Will be an NSWindow later, for now, it's id, because we cannot use pointers to ObjC classes
    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) {
    //alloc NSWindow
    self->window = objc_msgSend(objc_getClass("NSWindow"),
                                sel_getUid("alloc"));
    //init NSWindow
    //Adjust frame.  Window would be about 50*50 px without this
    //specify window type.  We want a resizeable window that we can close.
    //use retained backing because this thing is small anyhow
    //return no because this is the main window, and should be shown immediately
    self->window = objc_msgSend(self->window,
                                sel_getUid("initWithContentRect:styleMask:backing:defer:"),(NSRect){0,0,1024,460}, (NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask),NSBackingStoreRetained,NO);

    //send alloc and init to our view class.  Love the nested objc_msgSends!
    id view = objc_msgSend(objc_msgSend(objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (struct CGRect) { 0, 0, 320, 480 });

    // here we simply add the view to the window.
    objc_msgSend(self->window, sel_getUid("setContentView:"), view);
    objc_msgSend(self->window, sel_getUid("becomeFirstResponder"));

    //makeKeyOrderFront: NSWindow to show in bottom left corner of the screen
    objc_msgSend(self->window,
                 sel_getUid("makeKeyAndOrderFront:"),
                 self);
    return YES;
}

static void initAppDel()
{
    //Our appDelegate should be NSObject, but if you want to go the hard route, make this a class pair of NSApplication and try initing those awful delegate methods!
    AppDelClass = objc_allocateClassPair((Class)
                                         objc_getClass("NSObject"), "AppDelegate", 0);
    //Change the implementation of applicationDidFinishLaunching: so we don't have to use ObjC when this is called by the system.
    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"));
}

//there doesn't need to be a main.m because of this little beauty here.
int main(int argc, char** argv)
{
    //Initialize a valid app delegate object just like [NSApplication sharedApplication];
    initAppDel();
    //Initialize the run loop, just like [NSApp run];  this function NEVER returns until the app closes successfully.
    init_app();
    //We should close acceptably.
    return EXIT_SUCCESS;
}

Ansicht.m

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

// This is a strong reference to the class of our custom view,
// In case we need it in the future.
Class ViewClass;


// This is a simple -drawRect implementation for our class. We could have
// used a UILabel  or something of that sort instead, but I felt that this
// stuck with the C-based mentality of the application.
void View_drawRect(id self, SEL _cmd, CGRect rect)
{
    //make a red NSColor object with its convenience method
    id red  = objc_msgSend(objc_getClass("NSColor"), sel_getUid("redColor"));

    // fill target rect with red, because this is it!
    NSRect rect1 = NSMakeRect ( 21,21,210,210 );
    objc_msgSend(red, sel_getUid("set"));
    NSRectFill ( rect1 );
}

// Once again we use the (constructor) attribute. generally speaking,
// having many of these is a very bad idea, but in a small application
// like this, it really shouldn't be that big of an issue.
__attribute__((constructor))
static void initView()
{

    // Once again, just like the app delegate, we tell the runtime to
    // create a new class, this time a subclass of 'UIView' and named 'View'.
    ViewClass = objc_allocateClassPair((Class) objc_getClass("NSView"), "View", 0);

    // and again, we tell the runtime to add a function called -drawRect:
    // to our custom view. Note that there is an error in the type-specification
    // of this method, as I do not know the @encode sequence of 'CGRect' off
    // of the top of my head. As a result, there is a chance that the rect
    // parameter of the method may not get passed properly.
    class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP) View_drawRect, "v@:");

    // And again, we tell the runtime that this class is now valid to be used.
    // At this point, the application should run and display the screenshot shown below.
    objc_registerClassPair(ViewClass);
}

Präfix.pch

//
// Prefix header for all source files of the 'CBasedMacApp' target in the 'CBasedMacApp' project
//

#ifdef __OBJC__
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#endif

Geben Sie hier die Bildbeschreibung ein

  • Stimmt nicht, Sie können die objc-Laufzeit verwenden, um eine App in C zu erstellen, geben Sie mir ein paar Minuten, und ich zeige es Ihnen

    – Richard J.Ross III

    23. April 2012 um 23:51 Uhr

  • Ja, und Sie können ein Fundament mit einem Löffel graben, aber das macht es weder zu einer guten Idee noch zu einem furchtbaren Effekt.

    – bum

    23. April 2012 um 23:52 Uhr

  • @MahmoudAl-Qudsi Ich habe nicht aufgegeben 🙂

    – Richard J.Ross III

    24. April 2012 um 0:37 Uhr

  • Nun, die Fähigkeit könnte auch nützlich sein, wenn Sie in der Shawshank-Erlösung sind …

    – Hejazzman

    25. April 2012 um 7:38 Uhr


  • Ja. Was mich ärgert, ist, wenn es den modernen Laufzeitcode nicht gäbe, würde dies auf jedem Mac mit einem X im Softwarenamen funktionieren.

    – CodaFi

    4. Juli 2012 um 15:20 Uhr

Benutzeravatar von Peter Hosey
Peter Hosey

Ich habe hier gelesen C vor Objective-C lernen?

Normalerweise ersetze ich dann etwas Obj-C-Code durch reinen C-Code (schließlich kann man sie beliebig mischen, der Inhalt einer Obj-C-Methode kann vollständig reiner C-Code sein)

Ist das wahr?

Könnte ich eine iPhone-App nur in der Programmiersprache C erstellen?

Die zitierte Passage ist wahr, aber die Antwort auf Ihre Frage lautet nein.

Um zu veranschaulichen, worüber der Antwortende Mecki bei dieser anderen Frage sprach:

- (void) drawRect:(CGRect)dirtyRect { //Objective-C

    CGContextRef context = UIGraphicsGetCurrentContext();  //C
    CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0); //C
    CGContextFillRect(context, dirtyRect);                 //C

} //Objective-C (balances above “- (void) drawRect:…” line)

Es gibt nichts als reinen C-Code innerhalb diese Methode, aber die Methode selbst ist Objective-C-Code, ebenso wie die Klasse, die diese Methode enthält.

Es ist also möglich, das zu tun, was Mecki gesagt hat, aber Sie können (praktisch – wie Richard J. Ross III gezeigt hat, es ist technisch möglich, aber ziemlich viel Tipparbeit) kein ganzes Cocoa Touch-Programm in reinem C schreiben.

Tatsächlich ruft ein Teil des hier geposteten Codes, obwohl er in C geschrieben ist, immer noch Ziel-C-Code auf :). Ich weiß nicht, ob das tatsächlich zu dem Szenario aus dem Originalplakat passt, als er fragte

Ist es möglich, eine iPhone-App rein in der Programmiersprache C zu erstellen?

aber ich würde den Leuten zustimmen, die sagen, dass Sie im Allgemeinen und für eine App mit einer GUI Ihre GUI in OpenGL (das ist C) schreiben müssten.

Ich denke, das machen die meisten Spiele, oder? Obwohl ich nicht sicher bin, ob in C Zugriff auf die E / A des iPhones (z. B. der Touchscreen) besteht.

Last but not least, die Jungs, die den obigen Code geschrieben haben, rocken! 🙂

1427520cookie-checkSo schreiben Sie eine iOS-App rein in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy