简介
由于更熟悉OC一点,所以新工程仍然用OC,但是常见的第三方库,比如SVProgressHUD都会报错,说是找不到window
然后再试了一下,就会发现以前理所当然的[[[UIApplication sharedApplication] delegate] window]都会报错。怎么回事?
SceneDelegate
企业微信截图_0516fa97-ec90-40e0-adf8-46599ea57c67.png-
如图所示,除了通常的AppDelegate,又多了SceneDelegate
-
很重要的window属性,从往常的AppDelegate移到了SceneDelegate
-
这个又继承了苹果蠢猪式的升级,连基本的兼容性都不考虑了。苹果的工程师质量是一代不如一代啊
-
引入SceneDelegate,是学习Android的多进程。随着手机屏幕越来越大,也算有点道理。不过,把手机整得和PC一样,真的好吗?无脑抄袭Android,有未来吗?
-
吐槽再多也没用。不管未来有多辉煌,至少现在,所谓的“多进程”,没有丝毫收益,只有麻烦,直接旁路掉是最省事的做法。
如何找回windows
- window被移到了SceneDelegate中
#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow * window;
@end
- 在AppDelegate把移走的window加回来
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
// 与SceneDelegate中的window是同一个;
// 很多第三方库,比如SVProgressHUD要用到;
// 自己以view的方式做弹窗也要用到;
@property (nonatomic, strong) UIWindow *window;
@end
- 在SceneDelegate.m进行桥接,将两个window联系起来
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
/// 兼容以前的用法
[UIApplication sharedApplication].delegate.window = self.window;
}
入口点
-
把SceneDelegate和AppDelegate连接起来后,就可以沿用以前的做法,在AppDelegate.m中进行程序初始化操作。
-
可以直接在AppDelegate.m写初始化操作,也可以新建一个文件,把散落的代码集中在一起,保持AppDelegate.m的简洁。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/// 初始化
[WGBInitial start];
return YES;
}
初始化方法
-
本工程比较简单,初始化的内容不多,只有一些三方库的初始化,一些辅助接口的数据访问。
-
为了简洁,仍然是封装成静态方法
#import "WGBInitial.h"
#import <Sentry/Sentry.h>
@implementation WGBInitial
/// 开始初始化;从AppDelegate迁移到这里
+ (void)start {
/// 初始化内容放入子线程中,防止拖慢启动速度
NSOperationQueue *initialQueue = [[NSOperationQueue alloc] init];
initialQueue.name = @"初始化队列";
[initialQueue addOperationWithBlock:^{
NSLog(@"子线程初始开始……");
/// 友盟统计
[WGBStatistics setupYouMeng];
/// 腾讯Bugly
[WGBStatistics setupBugly];
/// AvoidCrash
[WGBStatistics setupAvoidCrash];
/// 阿里云鉴权参数
[WGBUploader getStsInfo];
/// 需要放主线程的初始化
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"主线程初始开始……");
/// 初始化Sentry
[WGBStatistics setupSentry];
}];
}];
}
@end
关于多线程
-
以前一直用GCD,也一直很好用;关于GCD可以参考下面的文章:
OC多线程GCD -
这次尝试用NSOperationQueue,感觉更简洁;
以前一直不用,估计是被那么多的概念搞怕了,习惯性使用GCD;所以,准备简化使用NSOperationQueue,当做今后多线程的主要使用方式。
-
GCD中有串行队列,并行队列;同步执行,异步执行;这些内容不能迁移到NSOperationQueue中;这样做的话就导致了复杂性,不好用。
-
简单处理的话,就用两种区分:
(1)如果是耗时工作,并且不涉及界面,就用NSOperationQueue *queue = [[NSOperationQueue alloc] init];
(2)如果涉及到界面,就用主线程:[NSOperationQueue mainQueue]
至于
queue.maxConcurrentOperationCount = 1;
千万不要用,没有意义,还增加了理解上的复杂程度
-
NSBlockOperation
和NSInvocationOperation
也增加了使用的复杂性,选择困难症困扰很多人。
简单起见,果断扔掉
NSInvocationOperation
,只考虑NSBlockOperation
-
至于继承
NSOperation
进行自定义,80%的情况都不要有这种考虑,大多数情况只是自寻烦恼。 -
真正使用的时候,设置都不需要考虑
NSBlockOperation
这个概念,直接使用如下方法就可以了。
- (void)addOperationWithBlock:(void (^)(void))block;
- 至于依赖关系,80%的情况也不需要考虑,直接使用如下方法将更简单
- (void)addBarrierBlock:(void (^)(void))barrier
这个相当于
dispatch_barrier_async
,使用得当,完全可以替代dispatch_group
-
小结:综上所述,只要理解了
NSOperationQueue
这个概念,只需要使用2个方法就能解决80%的多线程需求,比直接使用GCD简单多了
网友评论