RQExerciseModule.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. //
  2. // RQExerciseModule.m
  3. // JSJP
  4. //
  5. // Created by 张嵘 on 2021/8/12.
  6. //
  7. #import "RQExerciseModule.h"
  8. /// 刷新TableView
  9. NSString * const RQRefreshExerciseTableViewNotification = @"RQRefreshExerciseTableViewNotification";
  10. /// 答题结果
  11. NSString * const RQAnswerResultsNotification = @"RQAnswerResultsNotification";
  12. /// 答题结果 key
  13. NSString * const RQAnswerResultsKey = @"RQAnswerResultsKey";
  14. /// 练习模式Key
  15. NSString * const RQExerciseTypeKey = @"RQExerciseTypeKey";
  16. /// 考试页面弹窗弹出类型Key
  17. NSString * const RQExerciseAlertTypeKey = @"RQExerciseAlertTypeKey";
  18. /// 取消收藏成功通知
  19. NSString * const RQCancelCollectNotification = @"RQCancelCollectNotification";
  20. /// 取消收藏题目Key
  21. NSString * const RQCancelCollectExerciseModelKey = @"RQCancelCollectExerciseModelKey";
  22. /// 取消错题成功通知
  23. NSString * const RQCancelWrongNotification = @"RQCancelWrongNotification";
  24. /// 取消错题题目Key
  25. NSString * const RQCancelWrongExerciseModelKey = @"RQCancelWrongExerciseModelKey";
  26. /// 跳转到下一题通知
  27. NSString * const RQAutoJumpNextNotification = @"RQAutoJumpNextNotification";
  28. @interface RQExerciseModule ()
  29. @property (nonatomic, readwrite, strong) SCLAlertView *alert;
  30. @property (nonatomic, readwrite, strong) UITextView *messageTextView;
  31. @property (nonatomic, readwrite, strong) SCLButton *rePlayButton;
  32. @property (nonatomic, readwrite, strong) SCLButton *closeButton;
  33. /// 考试交卷
  34. @property (nonatomic, readwrite, assign) BOOL handUpIsShow;
  35. /// 考试暂停
  36. @property (nonatomic, readwrite, assign) BOOL suspendIsShow;
  37. /// 考试恢复
  38. @property (nonatomic, readwrite, assign) BOOL resumeIsShow;
  39. @property (nonatomic, readwrite, assign) BOOL isShow;
  40. @end
  41. @implementation RQExerciseModule
  42. static id rq_RQExerciseModule = nil;
  43. #pragma mark - init
  44. + (instancetype)sharedInstance {
  45. static dispatch_once_t onceToken;
  46. dispatch_once(&onceToken, ^{
  47. rq_RQExerciseModule = [[self alloc] init];
  48. });
  49. return rq_RQExerciseModule;
  50. }
  51. - (instancetype)init {
  52. if (self = [super init]) {
  53. [[RQNotificationCenter rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(id x) {
  54. }];
  55. [[RQNotificationCenter rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil] subscribeNext:^(id x) {
  56. if (RQ_Exercise_Module.timer) {
  57. [RQ_Exercise_Module.timer setFireDate:[NSDate distantFuture]];
  58. NSDictionary *dic = @{
  59. @"questionArr" : RQ_Exercise_Module.questionArr,
  60. };
  61. [RQ_Exercise_Module showAlertWithRQExerciseAlertType:RQExerciseAlertType_Exam_Resume valueDic:dic confirmAction:^(__kindof QMUIDialogViewController *dialogViewController) {
  62. if (RQ_Exercise_Module.timer) {
  63. [RQ_Exercise_Module.timer setFireDate:[NSDate distantPast]];
  64. }
  65. } cancelAction:nil];
  66. }
  67. }];
  68. }
  69. return self;
  70. }
  71. #pragma mark - PublicMethods
  72. /**
  73. 弹出alertController,并且有多action按钮,分别有处理事件
  74. @param alertType 弹窗类型
  75. @param dic 参数
  76. @param confirmTitle 左边按钮的title
  77. @param cancelTitle 右边按钮的title
  78. @param confirmAction 左边按钮的点击事件
  79. @param cancelAction 右按钮的点击事件
  80. */
  81. - (void)showAlertWithRQExerciseAlertType:(RQExerciseAlertType)alertType valueDic:(NSDictionary * _Nullable )dic confirmAction:(void (^__nullable)(__kindof QMUIDialogViewController * _Nonnull dialogViewController))confirmAction cancelAction:(void (^__nullable)(__kindof QMUIDialogViewController * _Nonnull dialogViewController))cancelAction {
  82. @weakify(self)
  83. if (alertType == RQExerciseAlertType_Exam_Resume) {
  84. if (self.handUpIsShow || self.suspendIsShow) {
  85. return;
  86. }
  87. }
  88. QMUIDialogViewController *dialogViewController = [[QMUIDialogViewController alloc] init];
  89. dialogViewController.headerSeparatorColor = nil;
  90. dialogViewController.headerViewBackgroundColor = UIColor.whiteColor;
  91. dialogViewController.headerViewHeight = (alertType != RQExerciseAlertType_Exam_Suspend)? RQ_FIT_HORIZONTAL(30.f) + 24.f : 0.f;
  92. NSArray *questionArr = @[];
  93. if ([dic.allKeys containsObject:@"questionArr"]) {
  94. if (!RQObjectIsNil(dic[@"questionArr"])) {
  95. questionArr = dic[@"questionArr"];
  96. }
  97. }
  98. NSInteger totalNum = questionArr.count;
  99. NSInteger errorNum = RQ_Exercise_Module.errorArr.count;
  100. NSInteger rightNum = RQ_Exercise_Module.correctArr.count;
  101. NSInteger hasdoNum = errorNum + rightNum;
  102. NSInteger undoNum = totalNum - hasdoNum;
  103. NSInteger perScore = 100 / totalNum;
  104. NSInteger score = rightNum * perScore;
  105. CGFloat progress = (hasdoNum * 1.00) / (totalNum * 1.00);
  106. BOOL isSuccess = (score >= 90);
  107. NSUInteger sec = RQ_Exercise_Module.count%60;
  108. NSUInteger min = RQ_Exercise_Module.count/60;
  109. NSString* sec0,*min0;
  110. sec0 = sec<10 ? @"0" : @"";
  111. min0 = min<10 ? @"0" : @"";
  112. /// 头部区域
  113. QMUILabel *titleLabel = [[QMUILabel alloc] qmui_initWithFont:RQSemiboldFont(21) textColor:RQ_MAIN_TEXT_COLOR_1];
  114. if (alertType == RQExerciseAlertType_Exam_HandUp) {
  115. titleLabel.textColor = RQ_MAIN_TEXT_COLOR_GREEN;
  116. titleLabel.text = @"考试合格";
  117. titleLabel.textColor = RQ_MAIN_TEXT_COLOR_RED;
  118. titleLabel.text = @"考试不合格";
  119. } else if (alertType == RQExerciseAlertType_Exam_Resume) {
  120. titleLabel.textColor = RQ_MAIN_TEXT_COLOR_1;
  121. titleLabel.text = @"为您恢复计时";
  122. } else if (alertType == RQExerciseAlertType_Exam_Konw) {
  123. titleLabel.textColor = RQ_MAIN_TEXT_COLOR_1;
  124. titleLabel.text = @"模考须知";
  125. } else {
  126. titleLabel.textColor = RQ_MAIN_TEXT_COLOR_1;
  127. titleLabel.text = @"";
  128. }
  129. titleLabel.textAlignment = NSTextAlignmentCenter;
  130. if (alertType != RQExerciseAlertType_Exam_Suspend) {
  131. [dialogViewController.headerView addSubview:titleLabel];
  132. [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  133. make.top.mas_offset(24.f);
  134. make.centerX.mas_offset(0);
  135. make.size.mas_equalTo(CGSizeMake(RQ_SCREEN_WIDTH - 80.f, RQ_FIT_HORIZONTAL(30.f)));
  136. }];
  137. }
  138. /// 内容区域
  139. // UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, RQ_SCREEN_WIDTH - 80.f, 36)];
  140. // contentView.backgroundColor = UIColorWhite;
  141. // dialogViewController.contentView = contentView;
  142. if (alertType == RQExerciseAlertType_Exam_HandUp) {
  143. RQExerciseExamAlertView *exerciseExamAlertView = [RQExerciseExamAlertView exerciseExamAlertView];
  144. exerciseExamAlertView.myImageView.image = isSuccess? RQImageNamed(@"及格") : RQImageNamed(@"不及格图");
  145. CGFloat imageHeight = ((RQ_SCREEN_WIDTH - 80.f) * (147.f / 290.f)) * (133.51f / 147.f);
  146. exerciseExamAlertView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH - 80.f, 4.f + imageHeight + 16.f + RQ_FIT_HORIZONTAL(60.f) + 24.f);
  147. dialogViewController.contentView = exerciseExamAlertView;
  148. exerciseExamAlertView.myTimeLabel.text = [NSString stringWithFormat:@"剩余时间:%@%lu:%@%lu",min0,(unsigned long)min,sec0,(unsigned long)sec];
  149. exerciseExamAlertView.errorLabel.text = [NSString qmui_stringWithNSInteger:errorNum];
  150. exerciseExamAlertView.unDoLabel.text = [NSString qmui_stringWithNSInteger:undoNum];
  151. exerciseExamAlertView.scoreLabel.text = [NSString qmui_stringWithNSInteger:score];
  152. } else if (alertType == RQExerciseAlertType_Exam_Suspend) {
  153. RQExerciseExamAlertHudView *exerciseExamAlertHudView = [RQExerciseExamAlertHudView exerciseExamAlertHudView];
  154. CGFloat progressWidth = (RQ_SCREEN_WIDTH - 80.f) * (132.f / 290.f);
  155. exerciseExamAlertHudView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH - 80.f, 24.f + progressWidth + 16.f + RQ_FIT_HORIZONTAL(24.f) + 24.f);
  156. dialogViewController.contentView = exerciseExamAlertHudView;
  157. [exerciseExamAlertHudView.myProgressView setProgress:progress];
  158. exerciseExamAlertHudView.questionNumLabel.text = [NSString stringWithFormat:@"%ld/%ld题",hasdoNum,totalNum];
  159. exerciseExamAlertHudView.myTimeLabel.text = [NSString stringWithFormat:@"剩余时间:%@%lu:%@%lu",min0,(unsigned long)min,sec0,(unsigned long)sec];
  160. } else if (alertType == RQExerciseAlertType_Exam_Resume) {
  161. RQExerciseExamAlertHudView *exerciseExamAlertHudView = [RQExerciseExamAlertHudView exerciseExamAlertHudView];
  162. CGFloat progressWidth = (RQ_SCREEN_WIDTH - 80.f) * (132.f / 290.f);
  163. exerciseExamAlertHudView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH - 80.f, 24.f + progressWidth + 16.f + RQ_FIT_HORIZONTAL(24.f) + 24.f);
  164. dialogViewController.contentView = exerciseExamAlertHudView;
  165. [exerciseExamAlertHudView.myProgressView setProgress:progress];
  166. exerciseExamAlertHudView.questionNumLabel.text = [NSString stringWithFormat:@"%ld/%ld题",hasdoNum,totalNum];
  167. exerciseExamAlertHudView.myTimeLabel.text = [NSString stringWithFormat:@"剩余时间:%@%lu:%@%lu",min0,(unsigned long)min,sec0,(unsigned long)sec];
  168. } else if (alertType == RQExerciseAlertType_Exam_Konw) {
  169. CGFloat textHeight = [@"温馨提示:模拟考试不能修改答案,每错1题扣1分,错题累计超过10道,考试不通过,系统自动提醒交卷。" heightForFont:[UIFont systemFontOfSize:13] width:RQ_SCREEN_WIDTH - 80.f - 24.f - 32.f] + 16.f;
  170. if ([dic.allKeys containsObject:@"questionArr"]) {
  171. if (!RQObjectIsNil(dic[@"questionArr"])) {
  172. questionArr = dic[@"questionArr"];
  173. }
  174. }
  175. RQExerciseExamKnowAlertView *exerciseExamKnowAlertView = [RQExerciseExamKnowAlertView exerciseExamKnowAlertView];
  176. exerciseExamKnowAlertView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH - 80.f, 24.f + 104.f + 16.f + textHeight + 24.f);
  177. exerciseExamKnowAlertView.examCarTypeLabel.text = [RQ_YDTQuestion_Module getCarTypeExamNameWithCarType:RQ_YDTQuestion_Module.carType];
  178. switch (RQ_YDTQuestion_Module.carType) {
  179. case RQHomePageCarType_Car:
  180. case RQHomePageCarType_Bus:
  181. case RQHomePageCarType_Truck:
  182. case RQHomePageCarType_Motorcycle:
  183. exerciseExamKnowAlertView.examStandardLabel.text = [NSString stringWithFormat:@"%ld题,45分钟",questionArr.count];
  184. exerciseExamKnowAlertView.examSuccessStandardLabel.text = [NSString stringWithFormat:@"90分及格(满分100)"];
  185. break;
  186. case RQHomePageCarType_ZGZ_JLY:
  187. case RQHomePageCarType_ZGZ_KY:
  188. case RQHomePageCarType_ZGZ_HY:
  189. exerciseExamKnowAlertView.examStandardLabel.text = [NSString stringWithFormat:@"%ld题,60分钟",questionArr.count];
  190. exerciseExamKnowAlertView.examSuccessStandardLabel.text = [NSString stringWithFormat:@"80分及格(满分100)"];
  191. break;
  192. case RQHomePageCarType_ZGZ_WXP:
  193. exerciseExamKnowAlertView.examStandardLabel.text = [NSString stringWithFormat:@"%ld题,60分钟",questionArr.count];
  194. exerciseExamKnowAlertView.examSuccessStandardLabel.text = [NSString stringWithFormat:@"90分及格(满分100)"];
  195. break;
  196. case RQHomePageCarType_ZGZ_CZC:
  197. case RQHomePageCarType_ZGZ_WYC:
  198. exerciseExamKnowAlertView.examStandardLabel.text = [NSString stringWithFormat:@"%ld题,50分钟",questionArr.count];
  199. exerciseExamKnowAlertView.examSuccessStandardLabel.text = [NSString stringWithFormat:@"40分及格(满分50)"];
  200. break;
  201. default:
  202. break;
  203. }
  204. dialogViewController.contentView = exerciseExamKnowAlertView;
  205. }
  206. /// 尾部区域
  207. dialogViewController.footerSeparatorColor = nil;
  208. dialogViewController.footerViewBackgroundColor = UIColor.whiteColor;
  209. dialogViewController.footerViewHeight = RQ_FIT_HORIZONTAL(40.f) + 30.f;
  210. dialogViewController.footerView.hidden = NO;
  211. CGFloat btnWidth = (RQ_SCREEN_WIDTH - 80.f - (16 * 2) - 16) / 2.f;
  212. NSString *confirmTitle = @"确定";
  213. NSString *cancelTitle = @"取消";
  214. if (alertType == RQExerciseAlertType_Exam_HandUp) {
  215. confirmTitle = @"现在交卷";
  216. cancelTitle = @"继续答题";
  217. } else if (alertType == RQExerciseAlertType_Exam_Resume) {
  218. confirmTitle = @"继续答题";
  219. cancelTitle = @"";
  220. } else if (alertType == RQExerciseAlertType_Exam_Suspend) {
  221. confirmTitle = @"继续考试";
  222. cancelTitle = @"重新考试";
  223. } else if (alertType == RQExerciseAlertType_Exam_Konw) {
  224. confirmTitle = @"知道了";
  225. cancelTitle = @"";
  226. } else {
  227. confirmTitle = @"现在交卷";
  228. cancelTitle = @"继续答题";
  229. }
  230. QMUIButton *confirmBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  231. [confirmBtn setTitle:confirmTitle textColor:UIColor.whiteColor Font:15 fotState:UIControlStateNormal];
  232. [confirmBtn setBackgroundColor:RQ_MAIN_COLOR];
  233. [confirmBtn setBlockForControlEvents:UIControlEventTouchUpInside block:^(id _Nonnull sender) {
  234. [dialogViewController hide];
  235. @strongify(self)
  236. switch (alertType) {
  237. case RQExerciseAlertType_Exam_HandUp:
  238. self.handUpIsShow = NO;
  239. break;
  240. case RQExerciseAlertType_Exam_Suspend:
  241. self.suspendIsShow = NO;
  242. break;
  243. case RQExerciseAlertType_Exam_Resume:
  244. self.resumeIsShow = NO;
  245. break;
  246. default:
  247. break;
  248. }
  249. if (confirmAction) {
  250. confirmAction(dialogViewController);
  251. }
  252. }];
  253. [dialogViewController.footerView addSubview:confirmBtn];
  254. [confirmBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  255. make.top.mas_offset(0);
  256. if (RQStringIsNotEmpty(cancelTitle)){make.right.mas_offset(-16);} else {make.centerX.mas_offset(0);}
  257. make.size.mas_equalTo(CGSizeMake(btnWidth, RQ_FIT_HORIZONTAL(40.f)));
  258. confirmBtn.layer.cornerRadius = RQ_FIT_HORIZONTAL(40.f) / 2.f;
  259. confirmBtn.layer.masksToBounds = YES;
  260. }];
  261. QMUIButton *cancleBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  262. [cancleBtn setTitle:cancelTitle textColor:[UIColor qmui_colorWithHexString:@"#5C6066"] Font:15 fotState:UIControlStateNormal];
  263. cancleBtn.layer.borderColor = [UIColor qmui_colorWithHexString:@"#707070"].CGColor;
  264. cancleBtn.layer.borderWidth = 1;
  265. cancleBtn.layer.cornerRadius = RQ_FIT_HORIZONTAL(40.f) / 2.f;
  266. cancleBtn.layer.masksToBounds = YES;
  267. [cancleBtn setBlockForControlEvents:UIControlEventTouchUpInside block:^(id _Nonnull sender) {
  268. [dialogViewController hide];
  269. @strongify(self)
  270. switch (alertType) {
  271. case RQExerciseAlertType_Exam_HandUp:
  272. self.handUpIsShow = NO;
  273. break;
  274. case RQExerciseAlertType_Exam_Suspend:
  275. self.suspendIsShow = NO;
  276. break;
  277. case RQExerciseAlertType_Exam_Resume:
  278. self.resumeIsShow = NO;
  279. break;
  280. default:
  281. break;
  282. }
  283. if (cancelAction) {
  284. cancelAction(dialogViewController);
  285. }
  286. }];
  287. if (RQStringIsNotEmpty(cancelTitle)) {
  288. [dialogViewController.footerView addSubview:cancleBtn];
  289. [cancleBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  290. make.top.mas_offset(0);
  291. make.left.mas_offset(16);
  292. make.size.mas_equalTo(CGSizeMake(btnWidth, RQ_FIT_HORIZONTAL(40.f)));
  293. }];
  294. }
  295. switch (alertType) {
  296. case RQExerciseAlertType_Exam_HandUp:
  297. self.handUpIsShow = YES;
  298. break;
  299. case RQExerciseAlertType_Exam_Suspend:
  300. self.suspendIsShow = YES;
  301. break;
  302. case RQExerciseAlertType_Exam_Resume:
  303. self.resumeIsShow = YES;
  304. break;
  305. default:
  306. break;
  307. }
  308. [dialogViewController show];
  309. }
  310. - (void)startTimer {
  311. [self.timer invalidate];
  312. self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(_timerValueChanged:) userInfo:nil repeats:YES];
  313. [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
  314. RQ_Exercise_Module.beginDate = [NSDate date];
  315. }
  316. #pragma mark - PrivateMethods
  317. - (void)_timerValueChanged:(NSTimer *)timer {
  318. RQ_Exercise_Module.count --;
  319. if (RQ_Exercise_Module.count == 0) {
  320. [RQ_Exercise_Module.timer invalidate];
  321. RQ_Exercise_Module.timer = nil;
  322. return;
  323. }
  324. }
  325. - (NSInteger)exerciseFontSize {
  326. id object = [RQ_COMMON_MANAGER getObjectWithKey:@"ExerciseFontSize"];
  327. if (RQObjectIsNil(object)) {
  328. return 17;
  329. } else {
  330. NSInteger myExerciseFontSize = [object integerValue];
  331. return myExerciseFontSize;
  332. }
  333. }
  334. - (void)setExerciseFontSize:(NSInteger)exerciseFontSize {
  335. [RQ_COMMON_MANAGER saveObjectWithObject:[NSNumber numberWithInteger:exerciseFontSize] ForKey:@"ExerciseFontSize"];
  336. }
  337. - (BOOL)isRightAutoJumpToNext {
  338. id object = [RQ_COMMON_MANAGER getObjectWithKey:@"isRightAutoJumpToNext"];
  339. if (RQObjectIsNil(object)) {
  340. return NO;
  341. } else {
  342. BOOL myIsRightAutoJumpToNext = [object boolValue];
  343. return myIsRightAutoJumpToNext;
  344. }
  345. }
  346. - (void)setIsRightAutoJumpToNext:(BOOL)isRightAutoJumpToNext {
  347. [RQ_COMMON_MANAGER saveObjectWithObject:[NSNumber numberWithBool:isRightAutoJumpToNext] ForKey:@"isRightAutoJumpToNext"];
  348. }
  349. - (BOOL)isRightAutoRemoveError {
  350. id object = [RQ_COMMON_MANAGER getObjectWithKey:@"isRightAutoRemoveError"];
  351. if (RQObjectIsNil(object)) {
  352. return NO;
  353. } else {
  354. BOOL myIsRightAutoRemoveError = [object boolValue];
  355. return myIsRightAutoRemoveError;
  356. }
  357. }
  358. - (void)setIsRightAutoRemoveError:(BOOL)isRightAutoRemoveError {
  359. [RQ_COMMON_MANAGER saveObjectWithObject:[NSNumber numberWithBool:isRightAutoRemoveError] ForKey:@"isRightAutoRemoveError"];
  360. }
  361. - (BOOL)isExerciseSound {
  362. id object = [RQ_COMMON_MANAGER getObjectWithKey:@"isExerciseSound"];
  363. if (RQObjectIsNil(object)) {
  364. return NO;
  365. } else {
  366. BOOL myIsExerciseSound = [object boolValue];
  367. return myIsExerciseSound;
  368. }
  369. }
  370. - (void)setIsExerciseSound:(BOOL)isExerciseSound {
  371. [RQ_COMMON_MANAGER saveObjectWithObject:[NSNumber numberWithBool:isExerciseSound] ForKey:@"isExerciseSound"];
  372. }
  373. - (NSArray *)questionArr {
  374. if (!_questionArr) {
  375. _questionArr = @[];
  376. }
  377. return _questionArr;
  378. }
  379. - (NSArray *)correctArr {
  380. if (!_correctArr) {
  381. _correctArr = @[];
  382. }
  383. return _correctArr;
  384. }
  385. - (NSArray *)errorArr {
  386. if (!_errorArr) {
  387. _errorArr = @[];
  388. }
  389. return _errorArr;
  390. }
  391. @end