纯 C 写个 iOS App(误)
2020/4/20 23:18:03
本文主要是介绍纯 C 写个 iOS App(误),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
iOS应用启动
一个 iOS app 首先是由 main.m
内的 main 函数开始的. 现在就先创建 Single View App 项目, 然后把所有的 .m
文件都删掉, 建一个 main.c
文件.
通常我们看到的 main.m
的内的代码是这样的
// 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 版本的
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])
.
现在已经抄作业抄了一个 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
类型的返回值, 但是它永远不会返回.
然后这玩意的前两个参数就不管了, 就是处理一下 main
函数传进来的参数, 第三个参数是需要传入 UIApplication
或者其子类的名称, 这里传 nil
就默认用 UIApplication
.
我们需要关注的是最后一个参数, 这个参数让我们传一个代理类的字符串, 就是给应用设置个代理, 也就是讲接下来我们要实现一个代理类.
所以我们现在来创建个 AppDelegate.c
的文件. 继续照之前的套路走, 先看 AppDelegate.m
代码, AppDelegate
这个类有个 window
的属性, 有下面这个函数
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { return YES; } 复制代码
我们先得实现一个 AppDelegate
class 才行. 一般稍微了解过 NSObject
定义的都晓得, 每个类有个 isa
用来标记这个类是什么, 具体怎样就不解释了, 反正很多 runtime 以及 header
文件的定义都能找到.
除了搞个 class, 我们还要实现那个 application:didFinishLaunchingWithOptions:
函数
// 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 来各种调用函数, 这个拿来玩玩就好了. 好吧, 先这样吧.
打广告
欢迎关注我的公众号
声明
转载请注明出处
掘金 juejin.im/post/5e9b32…
知乎 zhuanlan.zhihu.com/p/50059153
这篇关于纯 C 写个 iOS App(误)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值