iOS 앱을 순수하게 C로 작성하는 방법
보통 나는 일부 Obj-C 코드를 순수한 C 코드로 교체한다(결국 원하는 만큼 혼합할 수 있고, Obj-C 방법의 내용은 전적으로 순수한 C 코드일 수 있다).
이게 사실이야?
순수하게 C프로그래밍 언어로 아이폰 앱을 만들 수 있을까?
젠장, 시간이 좀 걸렸지만 알아냈어
main.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);
}
뷰.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);
}
추하지만 효과가 있다.
이걸 다운로드하고 싶으면 여기 내 드롭박스에서 받아가면 돼.
내 GitHub 저장소에서 구할 수 있다.
목표-C는 C언어의 상위 집합이기 때문에 이론적으로는 C언어에 완전히 정통하지 않는 한 프로그램 전체를 C로 작성하는 것이 가능하다.OpenGL ES
, 적어도 objC는 좀 해야 할 것이다.
Even Rich의 샘플에는 항상 NSString*이 들어 있다. (), 그렇지 않으면 직접 의견을 작성해야 할 것이다.
좋아, 위 내용은 완전히 틀렸다.내가 말하건대, 나는 리치가 이 고상한 목표를 달성한 것에 놀랐다. 그래서 나는 그것을 맥(여기 출처)으로 옮겼다.아래 파일에는 헤더가 없고 코코아에 연결되지 않으며 프로젝트에는 니브가 없다.
앱델레게이트
#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;
}
보기.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);
}
접두사.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
보통 나는 일부 Obj-C 코드를 순수한 C 코드로 교체한다(결국 원하는 만큼 혼합할 수 있고, Obj-C 방법의 내용은 전적으로 순수한 C 코드일 수 있다).
이게 사실이야?
순수하게 C프로그래밍 언어로 아이폰 앱을 만들 수 있을까?
인용된 구절은 사실이지만, 당신의 질문에 대한 대답은 '아니오'이다.
그 다른 질문에 대해 Mecki가 무엇에 대해 이야기하고 있었는지 설명하기 위해:
- (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)
이 방법 안에는 순수한 C 코드만 있을 뿐, 방법 자체는 이 방법을 포함하는 클래스와 마찬가지로 Objective-C 코드다.
그래서 메키가 말한 대로 하는 것은 가능하지만, 당신은 (실용적으로—리처드 J. 로스 3세가 보여준 것처럼, 기술적으로는 가능하지만 꽤 많은 타이핑을 하는) 순수한 C로 코코아 터치 프로그램 전체를 쓸 수는 없다.
실제로 여기에 게시된 코드 중 일부는 C로 표기되어 있지만, 여전히 목표-C 코드 :)라고 부르고 있다.그가 물어봤을 때 원작 포스터의 시나리오와 실제로 맞는지는 모르겠다.
순수하게 C프로그래밍 언어로 아이폰 앱을 만들 수 있을까?
하지만 나는 일반적으로 말해서 GUI가 있는 앱은 OpenGL (즉, C)로 GUI를 써야 한다는 사람들의 말에 동의한다.
대부분의 게임들은 그렇게 하는 것 같아, 그렇지?C에서 아이폰의 I/O(예를 들어 터치스크린)에 접속할 수 있는지 모르겠지만.
마지막이지만 중요한 것은 락 위에 코드를 쓴 사람들! :)
참조URL: https://stackoverflow.com/questions/10289890/how-to-write-ios-app-purely-in-c
'programing' 카테고리의 다른 글
왜 C연합이 필요한가? (0) | 2022.05.10 |
---|---|
C에서 NULL 포인터를 해제하는 것이 좋은 관행인가? (0) | 2022.05.10 |
Vue.js 템플릿 루프 - 비동기식 공리 호출 (0) | 2022.05.10 |
Vue의 v-on 지시어에 여러 이벤트 바인딩 (0) | 2022.05.10 |
VueJS - v-bind:스타일 + 호버 (0) | 2022.05.10 |