Mischen von C-Funktionen in einer Objective-C-Klasse

Lesezeit: 5 Minuten

Benutzeravatar von Richard Stelling
Richard Stelling

Ich schreibe eine Objective-C-Klasse, aber sie verwendet eine in C geschriebene API. Dies ist meistens in Ordnung, da das Mischen von C-Aufrufen mit Objective-C-Aufrufen nur wenige Probleme verursacht.

Einer der API-Aufrufe erfordert jedoch eine Rückrufmethode (Beispiel):

success = CFHostSetClient(host, MyCFHostClientCallBack, &context);

Wo MyCFHostClientCallBack ist eine C-Funktion, die wie folgt definiert ist:

static void MyCFHostClientCallBack(CFHostRef host, CFHostInfoType typeInfo, const CFStreamError *error, void *info);
  1. Kann/wie kann ich stattdessen eine Objective-C-Methode aufrufen?
  2. Kann/sollte ich C-Funktionen mit meinen Objective-C-Aufrufen mischen?
  3. Wie mische ich C-Funktionen mit Objective-C-Methoden?

Benutzeravatar von Richard Stelling
Richard Stelling

Das Mischen von C- und Objective-C-Methoden und -Funktionen ist möglich, hier ist ein einfaches Beispiel, das die SQLite-API in einer iPhone-App verwendet: (Kursseite)

Laden Sie die herunter ZIP-Datei (09_MySQLiteTableView.zip)

C-Funktionen müssen außerhalb deklariert werden @implementation in einer Objective-C-Datei (.m).

int MyCFunction(int num, void *data)
{
     //code here...
}

@implementation

- (void)MyObjectiveCMethod:(int)number withData:(NSData *)data
{
      //code here
}

@end

Da die C-Funktion außerhalb von liegt @implementation es kann keine Methoden wie aufrufen

[self doSomething]

und hat keinen Zugriff auf Ivars.

Dies kann umgangen werden, solange die Rückruffunktion eine dauert userInfo oder context Typparameter, normalerweise vom Typ void*. Dies kann verwendet werden, um jedes Objective-C-Objekt an die C-Funktion zu senden.

Wie im Beispielcodekann dies mit normalen Objective-C-Operationen manipuliert werden.

Lesen Sie außerdem diese Antwort: Mischen von C-Funktionen in einer Objective-C-Klasse

  • C-Funktionen müssen nicht außerhalb liegen @implementation.

    Benutzer557219

    17. April 2011 um 12:11 Uhr


  • @Bavarous: Der Compiler lässt Sie mit Funktionen im Inneren davonkommen @implementationaber es ist immer noch nicht Teil der @implementation, da Funktionen nicht zu einer Klasse gehören können. Aus Stilgründen lasse ich es außen vor @implementationund empfehlen Sie anderen, dasselbe zu tun, um dies deutlich zu machen.

    – Peter Hosey

    1. Dezember 2011 um 23:32 Uhr

  • Tatsächlich haben C-Funktionen innerhalb von @implementation-Blöcken die einzigartige Eigenschaft, direkt auf private und geschützte ivars zugreifen zu können. Aus meiner eigenen Erfahrung ist es daher zu einer starken Redewendung geworden, C-Funktionen, die zu einer Klasse “gehören”, innerhalb der entsprechenden Implementierung zu platzieren. Ich würde empfehlen, Ihre Antwort zu aktualisieren.

    – Josh Rosen

    2. Dezember 2011 um 0:00 Uhr


  • Zur Verdeutlichung haben sie Zugriff auf private Ivars, wenn sie Zugriff auf eine Instanz der Klasse erhalten. ZB int MyCFunction(MyClass* self, int num, void *data). Dies ähnelt sehr dem, was hinter den Kulissen vor sich geht, wenn Ihre -MyObjectiveCMethod:withData: objc-Methode kompiliert wird.

    – Josh Rosen

    2. Dezember 2011 um 0:13 Uhr

  • c-Funktionen können tatsächlich auch innerhalb des Implementierungsbereichs definiert werden, und niemand würde sich beschweren. Und sie können auf keinen Fall innerhalb oder außerhalb der @-Implementierung auf self zugreifen, einfach weil sie keine Methoden des Objekts sind.

    – Matteo Gobbi

    30. Januar 2019 um 22:20 Uhr

Um Objective-C-Code von einem C-Callback aufzurufen, würde ich so etwas verwenden:

void * refToSelf;
int cCallback()
{
    [refToSelf someMethod:someArg];
}

@implementation SomeClass
- (id) init
{
     self = [super init];
     refToSelf = self;
}
- (void) someMethod:(int) someArg
{
}

  • Nur eine Ergänzung: Viele Callbacks erlauben die Angabe von “Benutzerdaten”; In diesem Fall könnte der Objektzeiger als Benutzerdaten verwendet werden (und Sie brauchen die globale Variable refToSelf nicht mehr). Es kann sein, dass Ihre Callback-Funktion “Benutzerdaten” im Info-Zeiger unterstützt.

    – Aschefang

    29. April 2009 um 12:38 Uhr

  • Ich erhalte Warnungen zu einem Speicherleck zur Laufzeit, wenn ARC mit dem obigen Code aktiviert ist. stackoverflow.com/questions/11840943/…

    – docchang

    7. August 2012 um 7:13 Uhr

  • Dies ist eine fehlerhafte Implementierung. Es wird davon ausgegangen, dass Sie nur eine Instanz von ComeClass haben. void *refToSelf; ist eine statische/globale Variable. Jedes Mal, wenn Sie [[SomeClass alloc] init]wird Ihr refToSelf von der zuletzt erstellten Instanz überschrieben. Der cCallback ruft NICHT die richtige SomeClass auf, sondern immer die zuletzt erstellte. Darüber hinaus stürzt es ab, wenn die zuletzt erstellte SomeClass freigegeben wird (zumindest im obigen Code), da Sie refToSelf beim Dealloc der SomeClass nicht löschen.

    – Motti Schoner

    3. Dezember 2014 um 10:22 Uhr


  • Um das zu ergänzen, was @ashcatch gesagt hat: Wenn Ihr Rückruf a gegeben wird void *userDatastellen Sie sicher, dass Sie es in einen Zeiger auf Ihre Objective-C-Klasseninstanz mit umwandeln (__bridge id)(userData) Warnungen und Lecks zu vermeiden.

    – VinceFior

    12. Juli 2015 um 8:45 Uhr


  • Wenn eine Klasse Singleton ist, ist dies in Ordnung. aber es ist kein Singleton, stimme der Antwort von @MottiShneor zu.

    – damithH

    7. Dezember 2015 um 4:23 Uhr


Benutzeravatar von Peter Hosey
Peter Hosey

Kann/wie kann ich stattdessen eine Objective-C-Methode aufrufen?

Sie können nicht.

Kann/sollte ich die C-Funktion mit meinem Objective-C-Aufruf mischen?

Ja. Schreiben Sie eine C-Funktion und verwenden Sie diese als Callback für die CF-Funktion.

Wie mische ich C-Funktionen mit Objective-C-Methoden?

Sie können einstellen self als die info Zeiger in Ihrer Kontextstruktur. Das wird an den Callback weitergegeben. Setzen Sie dann im Rückruf die info Zeiger zurück zu id:

MyClass *self = (id)info;

Sie können dann senden self Mitteilungen. Sie können jedoch immer noch nicht direkt auf Instanzvariablen zugreifen, da sich eine C-Funktion außerhalb von befindet @implementation Sektion. Sie müssen sie zu Eigenschaften machen. Sie können dies mit einem tun Klassenerweiterung. (Im Gegensatz zu dem, was dieses Dokument sagt, würden Sie die Erweiterung nicht darin deklarieren @implementationaber in derselben Datei damit, im Allgemeinen direkt darüber.)

Was ich in dieser Situation immer als hilfreich empfunden habe, ist, einen Obj-C-Wrapper auf der C-API zu erstellen. Implementieren Sie, was Sie für die Verwendung von C-Funktionen benötigen, und bauen Sie eine Objective-C-Klasse (oder zwei) darauf auf, damit die Außenwelt nichts davon sieht. Im Falle eines Rückrufs wie diesem könnten Sie beispielsweise eine C-Funktion erstellen, die Obj-C-Delegatmethoden für andere Objekte aufruft.

.m Call-Funktion im Inneren .c:

  • CrifanLib.h
#ifndef CrifanLib_h
#define CrifanLib_h

#include <stdio.h>

void fileModeToStr(mode_t mode, char * modeStrBuf);

#endif /* CrifanLib_h */
  • CrifanLib.c
#include "CrifanLib.h"

#include <stdbool.h>

void fileModeToStr(mode_t mode, char * modeStrBuf) {
    // buf must have at least 10 bytes
    const char chars[] = "rwxrwxrwx";
    for (size_t i = 0; i < 9; i++) {
//        buf[i] = (mode & (1 << (8-i))) ? chars[i] : '-';
        bool hasSetCurBit = mode & (1 << (8-i));
        modeStrBuf[i] = hasSetCurBit ? chars[i] : '-';
    }
    modeStrBuf[9] = '\0';
}

angerufen von Objective-C‘s .m:

#include “CrifanLib.h"

@interface JailbreakDetectionViewController ()

@end

@implementation JailbreakDetectionViewController
…

char* statToStr(struct stat* statInfo){
    char stModeStr[10];
    fileModeToStr(statInfo->st_mode, stModeStr);
...
}

...

erledigt.

1403620cookie-checkMischen von C-Funktionen in einer Objective-C-Klasse

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

Privacy policy