나는 Objective-C 전에 Learn C를 읽는가?
일반적으로 일부 Obj-C 코드를 순수한 C 코드로 바꿉니다 (결국 원하는대로 혼합 할 수 있습니다.
이것이 사실입니까?
C 프로그래밍 언어로 iPhone 앱을 빌드 할 수 있습니까?
나는 Objective-C 전에 Learn C를 읽는가?
일반적으로 일부 Obj-C 코드를 순수한 C 코드로 바꿉니다 (결국 원하는대로 혼합 할 수 있습니다.
이것이 사실입니까?
C 프로그래밍 언어로 iPhone 앱을 빌드 할 수 있습니까?
답변:
젠장, 시간이 좀 걸렸지 만 알았습니다.
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);
}
View.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의 저장소에서 그것을 얻을 수 있습니다 여기에 :
@"AppDelegate
상수 NSString이며 C 전용 컴파일러로 컴파일되지 않습니다. CFSTR("AppDelegate")
대신 사용하십시오 .
당신이 철저에 정통한하지 않는, 그러나, C에서 완전히 프로그램을 작성 이론적으로는 가능하므로 오브젝티브 C는 C 언어의 상위 집합입니다 OpenGL ES
당신은 적어도 수행해야합니다, 일부 objC을 ( 심지어 리치의 샘플은있다 const NSString * ) 그렇지 않으면 뷰를 직접 작성해야합니다.
위의 내용은 완전히 틀 렸습니다. 나를 내가 (소스 맥으로 포팅 있도록 놀라게 리치,이 높은 목표를 달성 해요 가정 해 봅시다 여기 ). 아래 파일에는 헤더가 없으며 Cocoa에 연결되지 않으며 프로젝트에 펜촉이 없습니다.
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;
}
View.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);
}
prefix.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
나는 Objective-C 전에 Learn C를 읽는가?
일반적으로 일부 Obj-C 코드를 순수한 C 코드로 바꿉니다 (결국 원하는대로 혼합 할 수 있습니다.
이것이 사실입니까?
C 프로그래밍 언어로 iPhone 앱을 만들 수 있습니까?
인용구는 사실이지만 귀하의 질문에 대한 대답은 아니오입니다.
다른 질문에 대한 답변자 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 코드입니다 내에서 이 방법은 있지만,이 메소드를 포함하는 클래스입니다 같은 방법 자체가 목표 - C 코드입니다.
따라서 Mecki가 말한 것을 수행하는 것이 가능하지만 (실제로 Richard J. Ross III가 보여 주듯이 기술적으로 가능하지만 많은 타이핑이 가능합니다) 전체 Cocoa Touch 프로그램을 순수한 C로 작성할 수는 없습니다.
실제로 여기에 게시 된 일부 코드는 C로 작성되었지만 여전히 objective-C 코드를 호출합니다. :) 그가 요청했을 때 원래 포스터의 시나리오에 실제로 맞는지 모르겠습니다.
C 프로그래밍 언어로 iPhone 앱을 빌드 할 수 있습니까?
그러나 나는 일반적으로 말하기와 GUI가있는 앱의 경우 OpenGL (C 인 GUI)로 GUI를 작성해야한다고 사람들에게 동의합니다.
나는 그것이 대부분의 게임이하는 것이라고 생각합니다. C에서 iPhone의 I / O (예 : 터치 스크린)에 액세스 할 수 있는지 확실하지 않습니다.
마지막으로, 바위 위에 코드를 작성한 사람들! :)
objc_msgSend()
initWithFrame:
메소드 구현은 C 함수이기 때문에 호출하는 사실은 중요하지 않습니다.