一個 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 來各類調用函數, 這個拿來玩玩就行了. 好吧, 先這樣吧.