一个 iOS app 首先是由 main.m
内的 main 函数开始的. 如今就先建立 Single View App 项目, 而后把全部的 .m
文件都删掉, 建一个 main.c
文件.
一般咱们看到的 main.m
的内的代码是这样的app
// main.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
复制代码
那先照着这个写就行了, 可是这个 @autoreleasepool
这个怎么处理?
咱们晓得这是个语法糖, 在 ARC 出来以后编译器就不让咱们使用 NSAutoreleasePool, 原先是这样的函数
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release];
复制代码
那就仿照着这玩意写成 C 版本的ui
int main(int argc, char **argv) {
id pool = objc_msgSend(
objc_msgSend((id) objc_getClass("NSAutoreleasePool"),
sel_registerName("alloc")),
sel_registerName("init"));
UIApplicationMain(argc, argv, nil, CFSTR("AppDelegate"));
objc_msgSend(pool, sel_registerName("drain"));
}
复制代码
CFSTR
这个宏能够从 C 字符串建立一个 CFString
的引用(CFStringRef
), 这玩意能够用来代替咱们这里的 NSStringFromClass([AppDelegate class])
.spa
如今已经抄做业抄了一个 main.c
, 不过还有个问题, UIApplicationMain
这个函数从哪里跑出来的.
这个是一个用于建立咱们应用实例的函数, 可是咱们无法直接使用它, 由于它是在 UIApplication.h
文件, 不过咱们能够这样搞(这里顺便把 runtime 那些头文件补上吧)代理
#include <CoreFoundation/CoreFoundation.h>
#include <objc/runtime.h>
#include <objc/message.h>
extern int UIApplicationMain(int, ...);
int main(int argc, char **argv) {
id pool = objc_msgSend(
objc_msgSend((id) objc_getClass("NSAutoreleasePool"),
sel_registerName("alloc")),
sel_registerName("init"));
UIApplicationMain(argc, argv, nil, CFSTR("AppDelegate"));
objc_msgSend(pool, sel_registerName("drain"));
}
复制代码
这里再讲一下 UIApplicationMain
这个函数, 它虽然有 int
类型的返回值, 可是它永远不会返回.code
而后这玩意的前两个参数就无论了, 就是处理一下 main
函数传进来的参数, 第三个参数是须要传入 UIApplication
或者其子类的名称, 这里传 nil
就默认用 UIApplication
.
咱们须要关注的是最后一个参数, 这个参数让咱们传一个代理类的字符串, 就是给应用设置个代理, 也就是讲接下来咱们要实现一个代理类.cdn
因此咱们如今来建立个 AppDelegate.c
的文件. 继续照以前的套路走, 先看 AppDelegate.m
代码, AppDelegate
这个类有个 window
的属性, 有下面这个函数blog
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
复制代码
咱们先得实现一个 AppDelegate
class 才行. 通常稍微了解过 NSObject
定义的都晓得, 每一个类有个 isa
用来标记这个类是什么, 具体怎样就不解释了, 反正不少 runtime 以及 header
文件的定义都能找到.字符串
除了搞个 class, 咱们还要实现那个 application:didFinishLaunchingWithOptions:
函数get
// AppDelegate.c
#include <objc/runtime.h>
#include <objc/message.h>
#include <CoreGraphics/CoreGraphics.h>
typedef struct AppDelegate {
Class isa;
id window;
} AppDelegate;
Class AppDelegateClass;
BOOL applicationDidFinishLaunchingWithOptions( AppDelegate *self, SEL _cmd, void *application, void *options) {
self->window = objc_msgSend((id) objc_getClass("UIWindow"), sel_getUid("alloc"));
self->window = objc_msgSend(self->window, sel_getUid("initWithFrame:"),
(struct CGRect) {0, 0, 320, 568});
id viewController = objc_msgSend(
objc_msgSend((id) objc_getClass("UIViewController"), sel_getUid("alloc")),
sel_getUid("init"));
id view = objc_msgSend(
objc_msgSend((id) objc_getClass("View"), sel_getUid("alloc")),
sel_getUid("initWithFrame:"),
(struct CGRect) {0, 0, 320, 568});
objc_msgSend(objc_msgSend(viewController, sel_getUid("view")), sel_getUid("addSubview:"), view);
objc_msgSend(self->window, sel_getUid("setRootViewController:"), viewController);
objc_msgSend(self->window, sel_getUid("makeKeyAndVisible"));
return YES;
}
__attribute__((constructor))
static void initAppDelegate() {
AppDelegateClass = objc_allocateClassPair((Class) objc_getClass("UIResponder"), "AppDelegate", 0);
class_addIvar(AppDelegateClass, "window", sizeof(id), 0, "@");
// - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
class_addMethod(AppDelegateClass, sel_registerName("application:didFinishLaunchingWithOptions:"), (IMP) applicationDidFinishLaunchingWithOptions, "i@:@@");
objc_registerClassPair(AppDelegateClass);
}
复制代码
这里经过 __attribute__((constructor))
这个编译属性让这个函数在 main
函数以前走. 经过 runtime 搞了个 AppDelegateClass
出来. 因为我比较穷, 手机仍是 iPhone 5s, 因此设了 (struct CGRect) {0, 0,320, 568})
.
经过引入 CoreGraphics.h
才可让编译经过 CGRect
.
如今了解到建立一个 class 的套路以后, 这里在 applicationDidFinishLaunchingWithOptions
使用到了 View
class, 咱们就建立一个 View.c 文件来自定义视图什么
// View.c
#include <objc/runtime.h>
#include <CoreGraphics/CoreGraphics.h>
Class ViewClass;
extern CGContextRef UIGraphicsGetCurrentContext();
void viewDrawRect(id self, SEL _cmd, CGRect rect) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColor(context, (CGFloat[]) {1, 0, 0, 1});
CGContextAddRect(context, (struct CGRect) {0, 0, 320, 568});
CGContextFillPath(context);
}
__attribute__((constructor))
static void initView() {
ViewClass = objc_allocateClassPair((Class) objc_getClass("UIView"), "View", 0);
class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP) viewDrawRect, "v@:");
objc_registerClassPair(ViewClass);
}
复制代码
这里直接用 CoreGraphics
来绘制视图.
而后编译执行看看效果, 应该是一个空白的红色视图. 若是编译出错了, 多是如今的 Xcode 禁止 objc_msgSend
函数的调用, 在 Build Settings 启用它就行了.
忘了还有个事要作, 那就是把这几个东西导入到项目中
其实就是经过 runtime 来各类调用函数, 这个拿来玩玩就行了. 好吧, 先这样吧.