赞
踩
在OC工程中中有一个main.m的文件,App启动时首先初始化所有的类,然后调用main.m中的main函数,可以把App的启动过程简单的分为两个阶段:(1)从类的初始化到main函数的执行(2)从main函数的执行到application:didFinishLaunchingWithOptions的执行。
mian函数的实现如下:
int main(int argc,char* argv[]){
@autorelasepool {
return UIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegate class]))
}
}
调用UIApplicationMain函数,并返回。
通常来讲,main函数返回就代表着程序的退出,但实际上该函数并不会返回,也就是说App不会退出,主要原因在于RunLoop的存在,其内部是一个do-while的循环,保证了程序一直运行。
App日志的统计类似这样的一个需求:如果要记录App的行为日志,但是又不想要每个页面对应的VC都处理这种琐碎的事情,而是更希望这些VC做一些它们自己负责的交互与业务逻辑的事情,那么这种需求就可以用刚刚提到的思路来解决。
自定义一个继承于UIApplication的类—TestApplication,在main方法中将其指定为第3个参数。
int main(int argc,char* argv[]){
@autorelasepool {
return UIApplicationMain(argc,argv,NSStringFromClass([TestApplication class]),NSStringFromClass([AppDelegate class]))
}
}
因为UIApplication类中的与Event相关的API,所以可以利用这些API,完成记录行为日志的任务,如下:
-(void)sendEvent:(UIEvent*)event {
//在这里处理一些统一的逻辑
[super sendEvent:event];
}
-(BOOL) sendAction:(SEL)action to:(nullable id) target from:(nullable id) sender forEvent:(nullable UIEvent*)event {
//在这里处理一些统一的逻辑,例如:记录行为日志
return [super sendAction:action to:target from sender forEvent:event];
}
sendAction:to:from:forEvent:这个方法可以很好的解决现有的需求,例如要统计登录页面中点击登录按钮的这个行为,可以通过target参数判断页面,通过sender判断触发事件的按钮,通过action判断触发的事件名称,有了这些信息就可以判断出一个行为是否是登录页面的点击登录按钮行为,从而将该行为记录下来,或者上报给服务器。
这种需求也可以利用runtime和OC的消息转发机制可以将该需求更加灵活的实现。
@implementataion TestApplication
-(BOOL) sendAction:(SEL)action to:(nullable id) target from (nullable id)sender forEvent:(nullable UIEvent*)event {
if ([target isKindOfClass:[LoginViewController class]]&&[sender isKindOfClass:[UIButton class]]&&[NSStringFromSelector(action) isEqualToString:@"handle login"]){
//记录登录日志行为或者上报服务器
}
return [super sendAction:action to:target from sender forEvent:event];
}
@end
当代码走到UIApplicationMain函数时,就会走到AppDelegate类里面。AppDelegate类声明周期中很重要的一个函数:application:didFinishLaunchingWithOptions:会被调用。
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions {
self.windwo = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];
RootViewController *rootController = [[RootViewController alloc] init];
self.window.rootViewController = rootController;
[self.window makeKeyAndVisible];
return YES;
}
-(BOOL)application:(UIApplication*)application didFinishingWithOptions:(NSDictionary*) launchOptions {
self.windwo = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];
RootViewController *rootController = [[RootViewController alloc] init];
[self.window addSubview:rootController.view];
[self.window makeKeyAndVisible];
return YES;
}
iOS8.0之后就不能在通过addSubView了。
Xcode的Single View Application模板引入Main.storyboard之后,在info.plist里面就多了一个Main storyboard file base name字段,用于指定sb文件名,在程序启动时该sb文件会加载。在模板创建的工程中该字段指定为Main。
如果不指定该值,那么就需要在application:didFinishLaunchingWithOptions:方法中手动配置如下内容。
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions {
self.windwo = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds];
RootViewController *rootController = [[RootViewController alloc] init];
self.window.rootViewController = rootController;
[self.window makeKeyAndVisible];
return YES;
}
如果指定了该字段,虽然application:didFinishLaunchingWithOptions:里面什么也没写,但是系统会自动创建一个window,然后根据Main storyboard file base name传入的sb文件名实例化一个VC,作为window的rootViewController。系统会找到sb文件并且设置为Is Initial View Controller的VC,并将其实例化。如果Is Initial View Controller被取消,那么运行成功会发现屏幕是黑色的,这个黑色就是window的颜色。
Swift工程里面并没有一个main.swift文件,但是AppDelegate里面多了一个@UIApplicationMain。@UIApplicationMain会让编译器自动合成一个App的入口,相当于原来的main函数由编译器生成。
如果我们想要修改main函数,首先要注释掉@UIApplicationMain,然后建立一个main.swift的文件,删除里面所有的代码,然后添加如下代码:
import UIKit
import Foundation
UIApplicationMain(CommandLine.argc,UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to:UnsafeMutablePointer<Int8>.self,capacity:In t(CommandLine.argc)),nil,NSStringFromClass(AppDelegate.self))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。