Commit f48d9566 by 刘卓鑫

防止crash

parent 80ef91a1
//
++ /dev/null
//
// ZXChooseSchoolViewController.h
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ZXNewSchool.h"
typedef void(^selectSchool)(ZXNewCampus *campus);
@interface ZXChooseSchoolViewController : UIViewController
@property (nonatomic, copy) selectSchool select;
- (instancetype) initWithSchoolName:(selectSchool) selectSchool;
@end
//
++ /dev/null
//
// ZXChooseSchoolViewController.m
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import "ZXChooseSchoolViewController.h"
#import "enlargeClickRegionBtn.h"
#import "JXLayoutButton.h"
#import "ZXShowABCView.h"
#import "ZXNewSchool.h"
@interface ZXChooseSchoolViewController ()<UITableViewDelegate, UITableViewDataSource> {
UITextField *field;
}
@property (nonatomic, strong) UITableView *myTableView;
@property (nonatomic, strong) UITableView *indexListView;
@property (nonatomic, strong) NSMutableArray *listDataSource;
@end
@implementation ZXChooseSchoolViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createNabar];
[self.view addSubview:self.myTableView];
[self.view addSubview:self.indexListView];
[self tableHeader];
}
- (void) tableHeader {
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 50 * k6Scale)];
header.backgroundColor = [UIColor whiteColor];
UIView *hhh = [[UIView alloc] initWithFrame:CGRectMake(20 * k6Scale, 7 * k6Scale, 275 * k6Scale, 36 * k6Scale)];
hhh.layer.cornerRadius = 4;
hhh.layer.masksToBounds = YES;
hhh.layer.borderColor = RGB(234, 234, 234).CGColor;
hhh.layer.borderWidth = 1;
[header addSubview:hhh];
field = [[UITextField alloc] initWithFrame:CGRectMake(28 * k6Scale, 7 * k6Scale, 260 * k6Scale, 36 * k6Scale)];
[hhh addSubview:field];
[header addSubview:field];
field.font = kFontSize(14 * k6Scale);
field.textColor = RGB(111, 111, 111);
field.placeholder = @"请输入学校名称/首字母";
JXLayoutButton *searchBtn = [[JXLayoutButton alloc] init];
[header addSubview:searchBtn];
searchBtn.layoutStyle = JXLayoutButtonStyleLeftImageRightTitle;
searchBtn.midSpacing = 3;
[searchBtn setTitleColor:RGB(111, 111, 111) forState:UIControlStateNormal];
searchBtn.imageSize = CGSizeMake(17 * k6Scale, 17 * k6Scale);
[searchBtn setTitle:@"搜索" forState:UIControlStateNormal];
searchBtn.titleLabel.font = kFontSize(17 * k6Scale);
[searchBtn setImage:[UIImage imageNamed:@"login_pressed_search_icon"] forState:UIControlStateNormal];
[searchBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(header.mas_right).offset(-10 * k6Scale);
make.width.mas_equalTo(65 * k6Scale);
make.height.mas_equalTo(25 * k6Scale);
make.centerY.equalTo(field.mas_centerY);
}];
@weakify(self);
// [field.rac_textSignal subscribeNext:^(NSString *x) {
// @strongify(self);
// if (x.length==1 && [self isABC:x]) {
// NSString *A = x.uppercaseString;
// NSInteger index_T = 100;
// for (NSInteger index = 0; index < self.listDataSource.count; index++) {
// ZXBaseItem *item = self.listDataSource[index];
// if ([A isEqualToString:[item.py uppercaseString]]) {
// index_T = index;
// break;
// }
// }
// [self.myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:index_T] atScrollPosition:UITableViewScrollPositionTop animated:YES];
// } else {
// [self loadNewDataCompleted:^{
// for (int i = 0; i < self.listDataSource.count; i++) {
// ZXBaseItem *it = self.listDataSource[i];
// //这里触发检索
// NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"fdName LIKE[cd] '*%@*'", x]];
// NSArray *results = [it.baseSchool filteredArrayUsingPredicate:predicate];
// it.baseSchool = results;
//
// }
// dispatch_async(dispatch_get_main_queue(), ^{
// [self.myTableView reloadData];
// });
// }];
// }
// }];
__weak typeof(field) weakfield = field;
[[searchBtn rac_signalForControlEvents:UIControlEventTouchDown] subscribeNext:^(id y) {
@strongify(self);
[[UIApplication sharedApplication].keyWindow endEditing:YES];
NSString *x = weakfield.text;
if (x.length==1 && [self isABC:x]) {
NSString *A = x.uppercaseString;
ZXShowABCView *view = [[ZXShowABCView alloc] initWithFrame:[UIScreen mainScreen].bounds str:A];
[[UIApplication sharedApplication].keyWindow addSubview:view];
NSInteger index_T = 100;
for (NSInteger index = 0; index < self.listDataSource.count; index++) {
ZXBaseItem *item = self.listDataSource[index];
if ([A isEqualToString:[item.py uppercaseString]]) {
if (item.baseSchool != nil && item.baseSchool.count) {
index_T = index;
}
break;
}
}
if (index_T == 100) {
return ;
}
[self.myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:index_T] atScrollPosition:UITableViewScrollPositionTop animated:YES];
} else {
NSArray *arr = [ZXNewCampus findByCriteria:[NSString stringWithFormat:@"where areaName like '%%%@%%'", weakfield.text]];
[self sortResult:arr];
}
}];
self.myTableView.tableHeaderView = header;
}
-(BOOL)isABC:(NSString *)str{
NSRegularExpression *tLetterRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"[A-Za-z]" options:NSRegularExpressionCaseInsensitive error:nil];
int tLetterMatchCount = [tLetterRegularExpression numberOfMatchesInString:str options:NSMatchingReportProgress range:NSMakeRange(0, str.length)];
if(tLetterMatchCount>=1){
return YES;
}else{
return NO;
}
}
- (NSMutableArray *)listDataSource {
if (!_listDataSource) {
_listDataSource = [[NSMutableArray alloc] init];
}
return _listDataSource;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadNewData];
self.navigationController.navigationBar.hidden = YES;
}
#pragma mark - ========== 创建导航栏 ==========
- (void) createNabar {
[UIApplication sharedApplication].statusBarHidden = NO;
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
CGSize size = CGSizeMake(kWidth, kTopHeight);
// //设置颜色数组
UIColor *leftColor =
[UIColor colorWithHexString:@"#ff8809"];
UIColor *rightColor =
[UIColor colorWithHexString:@"#fe5b0b"];
//设置颜色
NSArray * colors = @[leftColor, rightColor];
// 设置导航条背景图片
UIImage * image=[BYHelp gradientColorImageFromColors:colors gradientType:1 imgSize:size];
UIImageView *nabar = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kTopHeight)];
nabar.userInteractionEnabled = YES;
nabar.image = image;
[self.view addSubview:nabar];
UILabel *naTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, kStatusBarHeight, kScreenWidth, kNavBarHeight)];
naTitle.backgroundColor = [UIColor clearColor];
naTitle.textAlignment = NSTextAlignmentCenter;
naTitle.textColor = [UIColor whiteColor];
naTitle.font = [UIFont boldSystemFontOfSize:17 * k6Scale];
naTitle.userInteractionEnabled = YES;
[nabar addSubview:naTitle];
naTitle.text = @"检索学校";
enlargeClickRegionBtn *back = [[enlargeClickRegionBtn alloc] init];
[back setImage:[UIImage imageNamed:@"nav_return"] forState:UIControlStateNormal];
[naTitle addSubview:back];
[back mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(naTitle.mas_left).offset(5 * k6Scale);
make.width.height.mas_equalTo(30 * k6Scale);
make.centerY.equalTo(naTitle);
}];
__weak typeof(self) weakSelf = self;
[[back rac_signalForControlEvents:UIControlEventTouchDown] subscribeNext:^(id x) {
[weakSelf.navigationController popViewControllerAnimated:YES];
}];
}
- (UITableView *)indexListView {
if (_indexListView== nil) {
CGFloat bottomMargin = 0;
if (iPhoneX) {
bottomMargin = 34;
}
_indexListView = [[UITableView alloc] initWithFrame:CGRectMake(kScreenWidth - 55 * k6Scale, kTopHeight + 80 * k6Scale, 55 * k6Scale, kScreenHeight-(kTopHeight + 120 * k6Scale) - bottomMargin) style:UITableViewStylePlain];
_indexListView.delegate = self;
_indexListView.dataSource = self;
_indexListView.backgroundColor = [UIColor clearColor];
_indexListView.separatorStyle = UITableViewCellSeparatorStyleNone;
if (@available(iOS 11.0, *)) {
_indexListView.estimatedRowHeight = 0;
_indexListView.estimatedSectionFooterHeight = 0;
_indexListView.estimatedSectionHeaderHeight = 0;
_indexListView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
_indexListView.separatorStyle = UITableViewCellSeparatorStyleNone;
_indexListView.showsVerticalScrollIndicator = NO;
}
return _indexListView;
}
#pragma mark - ========== UITableView/Delegate/Datasource ==========
- (UITableView *)myTableView {
if (_myTableView== nil) {
CGFloat bottomMargin = 0;
if (iPhoneX) {
bottomMargin = 34;
}
_myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kTopHeight, kScreenWidth, kScreenHeight-kTopHeight - bottomMargin) style:UITableViewStylePlain];
_myTableView.delegate = self;
_myTableView.dataSource = self;
_myTableView.backgroundColor = [UIColor whiteColor];
_myTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
//下拉刷新
self.myTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
if (@available(iOS 11.0, *)) {
_myTableView.estimatedRowHeight = 0;
_myTableView.estimatedSectionFooterHeight = 0;
_myTableView.estimatedSectionHeaderHeight = 0;
_myTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
_myTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_myTableView.showsVerticalScrollIndicator = NO;
}
return _myTableView;
}
#pragma mark - ========== 下拉刷新 ==========
- (void) loadNewData {
field.text = @"";
dispatch_async(dispatch_get_main_queue(), ^{
[ZXTool showLoadingAnimation];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[XMCenter sendRequest:^(XMRequest *request) {
request.url = [[NSString stringWithFormat:@"%@/area/queryAreaList", [kUserDefaults valueForKey:@"SX_MONEY"]] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
request.timeoutInterval = 5;
request.httpMethod = kXMHTTPMethodGET;
} onSuccess:^(id responseObject) {
CommonModel *common = [CommonModel mj_objectWithKeyValues:responseObject];
NSArray *arr = [ZXNewCampus mj_objectArrayWithKeyValuesArray:common.data];
[ZXNewCampus clearTable];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[ZXNewCampus saveObjects:arr];
});
[self sortResult:arr];
} onFailure:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[ZXTool hideLoadingAnimation];
[self.myTableView.mj_header endRefreshing];
});
} onFinished:^(id responseObject, NSError *error) {
}];
});
}
- (void) sortResult:(NSArray *) arr {
NSArray *result = [arr sortedArrayUsingComparator:^NSComparisonResult(ZXNewCampus *obj1, ZXNewCampus *obj2) {
return [obj1.initial caseInsensitiveCompare:obj2.initial];
}];
[self.listDataSource removeAllObjects];
NSString *charter = @"A";
NSMutableArray *list = [[NSMutableArray alloc] init];
for (int i = 0; i < result.count; i++) {
ZXNewCampus *item = result[i];
if ([item.initial isEqualToString:charter]) {
[list addObject:item];
} else {
ZXBaseItem *base = [[ZXBaseItem alloc] init];
base.py = charter;
base.baseSchool = list.mutableCopy;
[self.listDataSource addObject:base];
[list removeAllObjects];
charter = item.initial;
[list addObject:item];
}
if (i == result.count - 1) {
ZXBaseItem *base = [[ZXBaseItem alloc] init];
base.py = charter;
base.baseSchool = list.mutableCopy;
[self.listDataSource addObject:base];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[ZXTool hideLoadingAnimation];
[self.myTableView.mj_header endRefreshing];
[self.myTableView reloadData];
[self.indexListView reloadData];
});
}
//- (void) loadNewDataCompleted:(dispatch_block_t) completed {
//
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// [XMCenter sendRequest:^(XMRequest *request) {
// request.url = [[NSString stringWithFormat:@"%@/dcxy/api/campus/queryOrderList", [kUserDefaults valueForKey:@"Recharge"]] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// request.timeoutInterval = 5;
// request.httpMethod = kXMHTTPMethodGET;
// } onSuccess:^(id responseObject) {
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// CommonModel *common = [CommonModel mj_objectWithKeyValues:responseObject];
// NSArray *arr = [ZXBaseItem mj_objectArrayWithKeyValuesArray:common.data];
// [self.listDataSource removeAllObjects];
// [self.listDataSource addObjectsFromArray:arr];
// dispatch_async(dispatch_get_main_queue(), ^{
// completed();
// });
// });
// } onFailure:^(NSError *error) {
//
// } onFinished:^(id responseObject, NSError *error) {
//
// }];
// });
//}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.indexListView) {
return self.listDataSource.count;
}
ZXBaseItem *item = self.listDataSource[section];
return item.baseSchool.count;
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.indexListView) {
return 1;
}
return self.listDataSource.count;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
if (tableView == self.indexListView) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
ZXBaseItem *item = self.listDataSource[indexPath.row];
cell.textLabel.text = item.py;
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = RGB(191, 191, 191);
cell.textLabel.font = kFontSize(15 * k6Scale);
cell.contentView.backgroundColor = [UIColor clearColor];
return cell;
}
ZXBaseItem *item = self.listDataSource[indexPath.section];
ZXNewCampus *cam = item.baseSchool[indexPath.row];
cell.textLabel.text = cam.areaName;
return cell;
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (tableView == self.indexListView) {
return 0;
}
ZXBaseItem *item = self.listDataSource[section];
return item.baseSchool.count?35 * k6Scale:0;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (tableView == self.indexListView) {
return nil;
}
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 35 * k6Scale)];
header.backgroundColor = RGB(234, 234, 234);
UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(15 * k6Scale, 0, 200 * k6Scale, 35 * k6Scale)];
[header addSubview:l];
ZXBaseItem *item = self.listDataSource[section];
l.text = item.py;
return header;
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.indexListView) {
return 18 * k6Scale;
}
return 40 * k6Scale;
}
- (instancetype) initWithSchoolName:(selectSchool) selectSchool {
if (self = [super init]) {
self.select = selectSchool;
}
return self;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.indexListView) {
ZXBaseItem *Abc = self.listDataSource[indexPath.row];
ZXShowABCView *view = [[ZXShowABCView alloc] initWithFrame:[UIScreen mainScreen].bounds str:Abc.py];
[[UIApplication sharedApplication].keyWindow addSubview:view];
if (Abc.baseSchool.count == 0 || Abc.baseSchool == nil) {
return;
}
NSIndexPath *dayOne = [NSIndexPath indexPathForRow:0 inSection:indexPath.row];
[self.myTableView scrollToRowAtIndexPath:dayOne atScrollPosition:UITableViewScrollPositionTop animated:YES];
return;
}
ZXBaseItem *item = self.listDataSource[indexPath.section];
if (self.select) {
ZXNewCampus *campus = item.baseSchool[indexPath.row];
self.select(campus);
}
[self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
//
++ /dev/null
//
// ZXRegistorViewController.h
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ZXRegistorViewController : UIViewController
@end
//
++ /dev/null
//
// ZXRegistorViewController.m
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import "ZXRegistorViewController.h"
#import "enlargeClickRegionBtn.h"
#import "XieYIViewController.h"
#import "GraphicsCode.h"
#import "FetchCheckCodeModel.h"
#import "RegisterModel.h"
#import "ZXChooseSchoolViewController.h"
#import "ZXScollAreaChooseView.h"
#import "GetData.h"
#import "FetchAllSchoolModel.h"
#import "ZXNewSchool.h"
@interface ZXRegistorViewController ()<UIScrollViewDelegate>
@property (nonatomic, strong) dispatch_source_t timer;
@property (nonatomic, strong) UIView *naBar;
@property (nonatomic, strong) ZXNewCampus *currentSelecCampus;
@property (nonatomic, strong) enlargeClickRegionBtn *protocol;
@property (nonatomic, strong) enlargeClickRegionBtn *maleBtn;
@property (nonatomic, strong) enlargeClickRegionBtn *femaleBtn;
@property (nonatomic, strong) enlargeClickRegionBtn *maleIconBtn;
@property (nonatomic, strong) enlargeClickRegionBtn *femaleIconBtn;
@property (nonatomic, strong) UITextField *nameField;
@property (nonatomic, strong) UITextField *phoneField;
@property (nonatomic, strong) UITextField *violaField;
@property (nonatomic, strong) UILabel *reciveCode;
@property (nonatomic, strong) UITextField *pwdField;
@property (nonatomic, strong) UITextField *rePwdField;
@property (nonatomic, strong) UIButton *pwdSeeBtn;
@property (nonatomic, strong) UIButton *rePwdSeeBtn;
@property (nonatomic, strong) UITextField *schoolNameField;
//@property (nonatomic, strong) UITextField *schoolAreaNameField;
@property (strong, nonatomic) FetchCheckCodeModel *fetchCheckCodeModel;
@property (strong, nonatomic) RegisterModel *registerModel;
@end
@implementation ZXRegistorViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self createUI];
[self createNabar];
_fetchCheckCodeModel = [FetchCheckCodeModel new];
_registerModel = [RegisterModel new];
[self registNotificationAndKVO];
}
- (void)dealloc{
[self removeNotificationAndKVO];
}
-(void)registNotificationAndKVO{
[_fetchCheckCodeModel addObserver:self forKeyPath:@"isLoaded" options:NSKeyValueObservingOptionNew context:nil];
[_fetchCheckCodeModel addObserver:self forKeyPath:@"error" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)removeNotificationAndKVO{
[_fetchCheckCodeModel removeObserver:self forKeyPath:@"isLoaded"];
[_fetchCheckCodeModel removeObserver:self forKeyPath:@"error"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if (object == _fetchCheckCodeModel) {
if ([keyPath isEqualToString:@"isLoaded"]) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *S = [ZXTool getCurrentTime];
[self createTimerWithTimeIntervel:([ZXTool returnTimeintervelByDate:S] + 60)];
});
}
}
}
#pragma mark - ========== createNabar ==========
- (void) createNabar {
self.naBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kTopHeight)];
self.naBar.backgroundColor = [UIColor clearColor];
[self.view addSubview:self.naBar];
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.font = kFontSize(18 * k6Scale);
titleLabel.textColor = [UIColor whiteColor];
titleLabel.text = @"注册";
[self.view addSubview:titleLabel];
[titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.naBar.mas_top).offset(kStatusBarHeight);
make.width.mas_equalTo(200 * k6Scale);
make.height.mas_equalTo(44);
make.centerX.equalTo(self.naBar.mas_centerX);
}];
enlargeClickRegionBtn *back = [[enlargeClickRegionBtn alloc] init];
[back setImage:[UIImage imageNamed:@"zzzback"] forState:UIControlStateNormal];
[self.naBar addSubview:back];
[back mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.naBar.mas_left).offset(10 * k6Scale);
make.width.height.mas_equalTo(20 * k6Scale);
make.centerY.equalTo(titleLabel.mas_centerY);
}];
__weak typeof(self) weakSelf = self;
[[back rac_signalForControlEvents:UIControlEventTouchDown] subscribeNext:^(id x) {
[weakSelf.navigationController popViewControllerAnimated:YES];
}];
}
#pragma mark - ========== 创建UI ==========
- (void) createUI {
UIScrollView *header1 = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self.view addSubview:header1];
header1.backgroundColor = RGB(234, 234, 234);
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
header.backgroundColor = RGB(234, 234, 234);
[header1 addSubview:header];
CGFloat margin = 0;
if (iPhoneX) {
margin = 24;
}
UIImageView *top = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 221 * k6Scale + 24)];
[header addSubview:top];
top.image = [UIImage imageNamed:@"login_background"];
UIView *bg = [[UIView alloc] init];
bg.backgroundColor = [UIColor whiteColor];
[header addSubview:bg];
[bg mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(header.mas_left).offset(12 * k6Scale);
make.right.equalTo(header.mas_right).offset(-12 * k6Scale);
make.top.equalTo(header.mas_top).offset(kTopHeight + 60 * k6Scale);
make.bottom.equalTo(header.mas_bottom).offset(-10 * k6Scale);
}];
bg.layer.cornerRadius = 5;
bg.layer.masksToBounds = YES;
UIImageView *icon = [[UIImageView alloc] initWithFrame:CGRectMake(20 * k6Scale, 22 * k6Scale, 15 * k6Scale, 15 * k6Scale)];
[bg addSubview:icon];
icon.image = [UIImage imageNamed:@"login_Essential informationicon"];
UILabel *label1 = [[UILabel alloc] init];
[bg addSubview:label1];
label1.text = @"基本信息";
label1.font = kFontSize(15 * k6Scale);
label1.textColor = [UIColor colorWithHexString:@"#333333"];
[label1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(icon.mas_right).offset(5 * k6Scale);
make.centerY.equalTo(icon.mas_centerY);
}];
UIView *line1 = [[UIView alloc] init];
line1.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line1];
[line1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(icon.mas_bottom).offset(40 * k6Scale);
}];
self.nameField = [[UITextField alloc] init];
self.nameField.font = kFontSize(14 * k6Scale);
self.nameField.placeholder = @"请输入姓名";
[self.nameField setValue:@30 forKey:@"limit"];
[bg addSubview:self.nameField];
[self.nameField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line1.mas_left);
make.bottom.equalTo(line1.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
self.femaleIconBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.femaleIconBtn];
[self.femaleIconBtn setTitle:@"女" forState:UIControlStateNormal];
self.femaleIconBtn.titleLabel.font = kFontSize(14 * k6Scale);
[self.femaleIconBtn setTitleColor:[UIColor colorWithHexString:@"#666666"] forState:UIControlStateNormal];
[self.femaleIconBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(line1.mas_right).offset(-5 * k6Scale);
make.width.height.mas_equalTo(16 * k6Scale);
make.centerY.equalTo(self.nameField.mas_centerY);
}];
self.femaleBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.femaleBtn];
[self.femaleBtn setBackgroundImage:[UIImage imageNamed:@"login_Gender check"] forState:UIControlStateDisabled];
[self.femaleBtn setBackgroundImage:[UIImage imageNamed:@"login_Gender uncheck"] forState:UIControlStateNormal];
[self.femaleBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.femaleIconBtn.mas_left).offset(-4 * k6Scale);
make.width.height.mas_equalTo(15 * k6Scale);
make.centerY.equalTo(self.nameField.mas_centerY);
}];
self.maleIconBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.maleIconBtn];
[self.maleIconBtn setTitle:@"男" forState:UIControlStateNormal];
[self.maleIconBtn setTitleColor:[UIColor colorWithHexString:@"#666666"] forState:UIControlStateNormal];
self.maleIconBtn.titleLabel.font = kFontSize(14 * k6Scale);
[self.maleIconBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.femaleBtn.mas_left).offset(-20 * k6Scale);
make.width.height.mas_equalTo(16 * k6Scale);
make.centerY.equalTo(self.nameField.mas_centerY);
}];
self.maleBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.maleBtn];
[self.maleBtn setBackgroundImage:[UIImage imageNamed:@"login_Gender check"] forState:UIControlStateDisabled];
[self.maleBtn setBackgroundImage:[UIImage imageNamed:@"login_Gender uncheck"] forState:UIControlStateNormal];
[self.maleBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.maleIconBtn.mas_left).offset(-4 * k6Scale);
make.width.height.mas_equalTo(15 * k6Scale);
make.centerY.equalTo(self.nameField.mas_centerY);
}];
UIView *line2 = [[UIView alloc] init];
line2.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line2];
[line2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(line1.mas_bottom).offset(40 * k6Scale);
}];
UIView *line3 = [[UIView alloc] init];
line3.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line3];
[line3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(line2.mas_bottom).offset(40 * k6Scale);
}];
UIImageView *icon1 = [[UIImageView alloc] init];
[bg addSubview:icon1];
[icon1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.width.height.mas_equalTo(15 * k6Scale);
make.top.equalTo(line3.mas_bottom).offset(25 * k6Scale);
}];
icon1.image = [UIImage imageNamed:@"login_Password-icon"];
UILabel *label2 = [[UILabel alloc] init];
[bg addSubview:label2];
label2.text = @"设置密码";
label2.font = kFontSize(15 * k6Scale);
label2.textColor = [UIColor colorWithHexString:@"#333333"];
[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(icon1.mas_right).offset(5 * k6Scale);
make.centerY.equalTo(icon1.mas_centerY);
}];
UIView *line4 = [[UIView alloc] init];
line4.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line4];
[line4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(icon1.mas_bottom).offset(40 * k6Scale);
}];
UIView *line5 = [[UIView alloc] init];
line5.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line5];
[line5 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(line4.mas_bottom).offset(40 * k6Scale);
}];
UIImageView *icon2 = [[UIImageView alloc] init];
[bg addSubview:icon2];
[icon2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.width.height.mas_equalTo(15 * k6Scale);
make.top.equalTo(line5.mas_bottom).offset(25 * k6Scale);
}];
icon2.image = [UIImage imageNamed:@"login_School-icon"];
UILabel *label3 = [[UILabel alloc] init];
[bg addSubview:label3];
label3.text = @"学校信息";
label3.font = kFontSize(15 * k6Scale);
label3.textColor = [UIColor colorWithHexString:@"#333333"];
[label3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(icon2.mas_right).offset(5 * k6Scale);
make.centerY.equalTo(icon2.mas_centerY);
}];
UIView *line6 = [[UIView alloc] init];
line6.backgroundColor = RGB(243, 243, 243);
[bg addSubview:line6];
[line6 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
make.height.mas_equalTo(1 * k6Scale);
make.top.equalTo(icon2.mas_bottom).offset(40 * k6Scale);
}];
// UIView *line7 = [[UIView alloc] init];
// line7.backgroundColor = RGB(243, 243, 243);
// [bg addSubview:line7];
// [line7 mas_makeConstraints:^(MASConstraintMaker *make) {
// make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
// make.right.equalTo(bg.mas_right).offset(-20 * k6Scale);
// make.height.mas_equalTo(1 * k6Scale);
// make.top.equalTo(line6.mas_bottom).offset(40 * k6Scale);
// }];
self.protocol = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.protocol];
[self.protocol setBackgroundImage:[UIImage imageNamed:@"login_check-no"] forState:UIControlStateNormal];
[self.protocol setBackgroundImage:[UIImage imageNamed:@"login_check"] forState:UIControlStateSelected];
[self.protocol mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bg.mas_left).offset(20 * k6Scale);
make.top.equalTo(line6.mas_bottom).offset(20 * k6Scale);
make.width.height.mas_equalTo(14 * k6Scale);
}];
UILabel *protocolLabel = [[UILabel alloc] init];
[bg addSubview:protocolLabel];
protocolLabel.textColor = [UIColor colorWithHexString:@"#333333"];
protocolLabel.font = kFontSize(12 * k6Scale);
protocolLabel.attributedText = [ZXTool replaceWithStr:@"注册条款《多彩校园服务协议》" subStr:@"《多彩校园服务协议》" replaceStrFont:12 * k6Scale color:[UIColor colorWithHexString:@"#f88d33"] lineSpaceing:2];
[protocolLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.protocol.mas_right).offset(5 * k6Scale);
make.centerY.equalTo(self.protocol.mas_centerY);
}];
protocolLabel.userInteractionEnabled = YES;
UITapGestureRecognizer *checkProtocol = [[UITapGestureRecognizer alloc] init];
[protocolLabel addGestureRecognizer:checkProtocol];
[[checkProtocol rac_gestureSignal] subscribeNext:^(id x) {
XieYIViewController * VC=[XieYIViewController new];
[self.navigationController pushViewController:VC animated:NO];
}];
UIButton *OKbTN = [[UIButton alloc] init];
[OKbTN setBackgroundImage:[UIImage imageNamed:@"wisdomGezi-agreement_available_btn"] forState:UIControlStateNormal];
[OKbTN setTitle:@"确认" forState:UIControlStateNormal];
OKbTN.titleLabel.font = [UIFont boldSystemFontOfSize:16 * k6Scale];
[OKbTN setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[bg addSubview:OKbTN];
OKbTN.layer.cornerRadius = 5;
OKbTN.layer.masksToBounds = YES;
[OKbTN mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(285 * k6Scale);
make.height.mas_equalTo(44 * k6Scale);
make.top.equalTo(self.protocol.mas_bottom).offset(20 * k6Scale);
make.centerX.equalTo(bg.mas_centerX);
}];
self.phoneField = [[UITextField alloc] init];
self.phoneField.font = kFontSize(14 * k6Scale);
self.phoneField.placeholder = @"请输入注册的手机号";
self.phoneField.keyboardType = UIKeyboardTypeNumberPad;
[bg addSubview:self.phoneField];
[self.phoneField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line2.mas_left);
make.bottom.equalTo(line2.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
self.violaField = [[UITextField alloc] init];
self.violaField.font = kFontSize(14 * k6Scale);
self.violaField.keyboardType = UIKeyboardTypeNumberPad;
self.violaField.placeholder = @"请输入验证码";
[bg addSubview:self.violaField];
[self.violaField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line3.mas_left);
make.bottom.equalTo(line3.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
self.pwdField = [[UITextField alloc] init];
self.pwdField.font = kFontSize(14 * k6Scale);
self.pwdField.placeholder = @"密码由6-20位组成";
[bg addSubview:self.pwdField];
[self.pwdField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line4.mas_left);
make.bottom.equalTo(line4.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
self.rePwdField = [[UITextField alloc] init];
self.rePwdField.font = kFontSize(14 * k6Scale);
self.rePwdField.placeholder = @"确认密码";
[bg addSubview:self.rePwdField];
[self.rePwdField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line5.mas_left);
make.bottom.equalTo(line5.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
self.schoolNameField = [[UITextField alloc] init];
self.schoolNameField.font = kFontSize(14 * k6Scale);
self.schoolNameField.placeholder = @"请填写真实学校信息";
[bg addSubview:self.schoolNameField];
[self.schoolNameField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(line6.mas_left);
make.bottom.equalTo(line6.mas_bottom);
make.height.mas_equalTo(25 * k6Scale);
make.width.mas_equalTo(180 * k6Scale);
}];
// self.schoolAreaNameField = [[UITextField alloc] init];
// self.schoolAreaNameField.font = kFontSize(14 * k6Scale);
// self.schoolAreaNameField.placeholder = @"请选择所在校区";
// [bg addSubview:self.schoolAreaNameField];
// [self.schoolAreaNameField mas_makeConstraints:^(MASConstraintMaker *make) {
// make.left.equalTo(line7.mas_left);
// make.bottom.equalTo(line7.mas_bottom);
// make.height.mas_equalTo(25 * k6Scale);
// make.right.equalTo(line7.mas_right);
// }];
self.reciveCode = [[UILabel alloc] init];
self.reciveCode.text = @"获取验证码";
[bg addSubview:self.reciveCode];
self.reciveCode.font = kFontSize(13 * k6Scale);
self.reciveCode.textColor = [UIColor colorWithHexString:@"#f88d33"];
[self.reciveCode mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(line3.mas_right).offset(-5 * k6Scale);
make.top.equalTo(line2.mas_bottom).offset(10 * k6Scale);
make.height.mas_equalTo(20 * k6Scale);
}];
UIView *li = [[UIView alloc] init];
[bg addSubview:li];
li.backgroundColor = RGB(222, 222, 222);
[li mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(1);
make.right.equalTo(self.reciveCode.mas_left).offset(-6 * k6Scale);
make.centerY.equalTo(self.reciveCode.mas_centerY);
make.height.mas_equalTo(15 * k6Scale);
}];
//合并两个field信号量 ---- 映射出field的值(返回的是元组)
RACSignal *enableSingal = [[RACSignal combineLatest:@[self.nameField.rac_textSignal, self.phoneField.rac_textSignal, self.violaField.rac_textSignal, self.pwdField.rac_textSignal, self.rePwdField.rac_textSignal]] map:^id(id value) {
//这里简单过滤 -- 根据实际情况而定
return @([value[0] length] && [value[1] length] && [value[2] length] && [value[3] length] && [value[4] length]);
}];
OKbTN.rac_command = [[RACCommand alloc] initWithEnabled:enableSingal signalBlock:^RACSignal *(id input) {
//返回空的信号量
return [RACSignal empty];
}];
[OKbTN addTarget:self action:@selector(OKAction:) forControlEvents:UIControlEventTouchDown];
[self.femaleBtn addTarget:self action:@selector(femalClick:) forControlEvents:UIControlEventTouchDown];
[self.maleBtn addTarget:self action:@selector(maleClick:) forControlEvents:UIControlEventTouchDown];
[self.protocol addTarget:self action:@selector(readProtocol:) forControlEvents:UIControlEventTouchDown];
UITapGestureRecognizer *counting = [[UITapGestureRecognizer alloc] init];
self.reciveCode.userInteractionEnabled = YES;
[self.reciveCode addGestureRecognizer:counting];
@weakify(self);
[[counting rac_gestureSignal] subscribeNext:^(id x) {
@strongify(self);
if (self.phoneField.text.length == 0) {
[ZXTool showText:@"请输入手机号"];
return ;
}
[self reciveVioCode];
}];
self.pwdSeeBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.pwdSeeBtn];
[self.pwdSeeBtn setImage:[UIImage imageNamed:@"login_Not-showtwo"] forState:UIControlStateNormal];
[self.pwdSeeBtn setImage:[UIImage imageNamed:@"login_show"] forState:UIControlStateSelected];
[self.pwdSeeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(line4.mas_right).offset(-5 * k6Scale);
make.centerY.equalTo(self.pwdField.mas_centerY);
}];
self.rePwdSeeBtn = [[enlargeClickRegionBtn alloc] init];
[bg addSubview:self.rePwdSeeBtn];
[self.rePwdSeeBtn setImage:[UIImage imageNamed:@"login_Not-showtwo"] forState:UIControlStateNormal];
[self.rePwdSeeBtn setImage:[UIImage imageNamed:@"login_show"] forState:UIControlStateSelected];
[self.rePwdSeeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(line5.mas_right).offset(-5 * k6Scale);
make.centerY.equalTo(self.rePwdField.mas_centerY);
}];
[self.pwdSeeBtn addTarget:self action:@selector(pwdSee:) forControlEvents:UIControlEventTouchDown];
[self.rePwdSeeBtn addTarget:self action:@selector(rePwdSee:) forControlEvents:UIControlEventTouchDown];
// UIImageView *iiii = [[UIImageView alloc] init];
// [bg addSubview:iiii];
// [iiii mas_makeConstraints:^(MASConstraintMaker *make) {
// make.right.equalTo(line7.mas_right).offset(-5 * k6Scale);
// make.centerY.equalTo(self.schoolAreaNameField.mas_centerY);
// }];
// iiii.image = [UIImage imageNamed:@"login_drop-down-icon"];
self.pwdField.secureTextEntry = YES;
self.rePwdField.secureTextEntry = YES;
[self.schoolNameField addTarget:self action:@selector(chooseSchool) forControlEvents:UIControlEventEditingDidBegin];
// [self.schoolAreaNameField addTarget:self action:@selector(chooseSchoolArea) forControlEvents:UIControlEventEditingDidBegin];
[self maleClick:self.maleBtn];
}
#pragma mark - ========== 获取验证码 ==========
- (void) reciveVioCode {
GraphicsCode *codeView = [[GraphicsCode alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight) phoneNum:self.phoneField.text];
codeView.fetchType = 1;
codeView.callBack = ^(NSString * code){
};
[codeView show:self];
}
#pragma mark - ========== 选择学校 ==========
- (void) chooseSchool {
@weakify(self);
ZXChooseSchoolViewController *school = [[ZXChooseSchoolViewController alloc] initWithSchoolName:^(ZXNewCampus *campus) {
@strongify(self);
self.currentSelecCampus = campus;
self.schoolNameField.text = campus.areaName;
}];
[self.navigationController pushViewController:school animated:YES];
[self.schoolNameField resignFirstResponder];
}
#pragma mark - ========== 开/闭密码框 ==========
- (void) pwdSee:(UIButton *) btn {
btn.selected = !btn.selected;
self.pwdField.secureTextEntry = !btn.selected;
}
- (void) rePwdSee:(UIButton *) btn {
btn.selected = !btn.selected;
self.rePwdField.secureTextEntry = !btn.selected;
}
#pragma mark - ========== 性别选中==========
- (void) femalClick:(enlargeClickRegionBtn *) sender {
self.femaleBtn.enabled = NO;
self.femaleIconBtn.enabled = NO;
self.maleBtn.enabled = YES;
self.maleIconBtn.enabled = YES;
}
- (void) maleClick:(enlargeClickRegionBtn *) sender {
self.femaleBtn.enabled = YES;
self.femaleIconBtn.enabled = YES;
self.maleBtn.enabled = NO;
self.maleIconBtn.enabled = NO;
}
#pragma mark - ========== 同意/否决协议 ==========
- (void) readProtocol:(enlargeClickRegionBtn *) sender {
self.protocol.selected = !self.protocol.selected;
}
- (NSString*)remainingTimeMethodAction:(long long)endTime
{
//得到当前时间
NSDate *nowData = [NSDate date];
NSDate *endData=[NSDate dateWithTimeIntervalSince1970:endTime];
NSCalendar* chineseClendar = [ [ NSCalendar alloc ] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags =
NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit;
NSDateComponents *cps = [chineseClendar components:unitFlags fromDate:nowData toDate: endData options:0];
NSInteger Hour = [cps hour];
NSInteger Min = [cps minute];
NSInteger Sec = [cps second];
NSInteger Day = [cps day];
NSString *countdown;
countdown = [NSString stringWithFormat:@"%zi秒 重新获取", Sec];
if (Sec<=0) {
//如果已经小于1了 说明已经结束
//此时关闭timer
dispatch_cancel(self.timer);
self.timer = nil;
return @"获取验证码";
}
return countdown;
}
- (void) createTimerWithTimeIntervel:(long long) intervel {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//马上启动:DISPATCH_TIME_NOW
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0*NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1*NSEC_PER_SEC);
//设置启动时间和间隔
dispatch_source_set_timer(_timer, start, interval, 1);
@weakify(self);
dispatch_source_set_event_handler(_timer, ^{
@strongify(self);
NSString *text = [self remainingTimeMethodAction:intervel];
dispatch_async(dispatch_get_main_queue(), ^{
self.reciveCode.text = text;
});
});
//开启
dispatch_resume(_timer);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication].keyWindow endEditing:YES];
self.navigationController.navigationBar.hidden = YES;
[UIApplication sharedApplication].statusBarHidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBar.hidden = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - ========== 确认注册 ==========
- (void) OKAction:(UIButton *) sender {
if ([self.phoneField.text length] != 11) {
[ZXTool showText:@"请输入11位手机号"];
return;
}
if (![self.pwdField.text isEqualToString:self.rePwdField.text]) {
[ZXTool showText:@"两次密码不一致"];
return;
}
if (self.pwdField.text.length < 6 || self.pwdField.text.length > 20) {
[ZXTool showText:@"密码由6-20位组成"];
return;
}
if ([self.schoolNameField.text length] == 0) {
[ZXTool showText:@"请先选择学校"];
return;
}
if (self.femaleBtn.enabled && self.maleBtn.enabled) {
[ZXTool showText:@"请先选择性别!"];
return;
}
if(self.protocol.selected == NO) {
[ZXTool showText:@"请先阅读用户协议并同意"];
return;
}
NSDictionary *dic = @{@"customerName":self.nameField.text,
@"customerPhone":self.phoneField.text,
@"customerSex":self.maleBtn.enabled?@"2":@"1",
@"areaId":@(self.currentSelecCampus.id),
@"areaName":self.currentSelecCampus.areaName,
@"password":self.pwdField.text,
@"verificationCode":self.violaField.text
};
[ZXTool requestDataWithType:kXMHTTPMethodPOST url:[NSString stringWithFormat:@"%@/app/customer/register", [kUserDefaults objectForKey:@"NEW_PAY"]] para:dic completed:^(CommonModel *common, NSString *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[ZXTool hideLoadingAnimation];
if (error) {
[ZXTool showText:error];
} else {
[ZXTool showText:@"注册成功"];
[self.navigationController popViewControllerAnimated:YES];
}
});
}];
}
@end
//
//
// UserInfoViewController.h
// ColorfulSchool
//
// Created by Listen on 2017/8/10.
// Copyright © 2017年 Colorful Any Door. All rights reserved.
// 老版本分流
#import <UIKit/UIKit.h>
#import "UserCenterModel.h"
@interface Old_UserInfoViewController : UIViewController
@property (nonatomic)UserCenerModelItem * userCenterItem;
@end
//
//
// UserInfoViewController.m
// ColorfulSchool
//
// Created by Listen on 2017/8/10.
// Copyright © 2017年 Colorful Any Door. All rights reserved.
//
#import "Old_UserInfoViewController.h"
#import "ZHPickView.h"
#import "FetchAllSchoolModel.h"
#import "SaveUserInfoModel.h"
#import "EmotionAlertView.h"
@interface Old_UserInfoViewController ()<UITableViewDataSource,UITableViewDelegate,ZHPickViewDelegate,UITextFieldDelegate>{
NSMutableArray * dataArray;
NSMutableArray * sectionTitle;
UIButton * confirmBtn ;
UIView * choiceSex;
int CellTag;
}
/**
头像旁边名字
*/
@property (strong, nonatomic) IBOutlet UILabel *nameLabel;
/**
账号信息(手机号码)
*/
@property (strong, nonatomic) IBOutlet UILabel *countLabel;
/**
组table
*/
@property (strong, nonatomic) IBOutlet UITableView *tableView;
/**
男女转化字符串
*/
@property (nonatomic, strong)NSString * setString;
/**
入学时间选择器
*/
@property (nonatomic, strong)ZHPickView *inschooltimePicker;
/**
生日
*/
@property (nonatomic, strong)ZHPickView *birthdayPicker;
/**
学校
*/
@property (nonatomic, strong)ZHPickView *schoolPicker;
/**
校区
*/
@property (nonatomic, strong)ZHPickView *campusPicker;
@property (nonatomic, strong)NSArray *allDataArray;
@property (nonatomic, strong)NSMutableArray *schoolArray;
@property (nonatomic, strong)NSMutableArray *campusArray;
@property (nonatomic, strong)FetchAllSchoolModel * allSchoolModel;
@property (nonatomic, strong)SaveUserInfoModel * saveModel;
@property (nonatomic)EmotionAlertView * emotionAlert;
@end
@implementation Old_UserInfoViewController
- (void)dealloc{
[self removeNotificationAndKVO];
}
-(void)registNotificationAndKVO{
[self.allSchoolModel addObserver:self forKeyPath:@"isLoaded" options:NSKeyValueObservingOptionNew context:nil];
[self.allSchoolModel addObserver:self forKeyPath:@"error" options:NSKeyValueObservingOptionNew context:nil];
[self.saveModel addObserver:self forKeyPath:@"isLoaded" options:NSKeyValueObservingOptionNew context:nil];
[self.saveModel addObserver:self forKeyPath:@"error" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)removeNotificationAndKVO{
[self.allSchoolModel removeObserver:self forKeyPath:@"isLoaded"];
[self.allSchoolModel removeObserver:self forKeyPath:@"error"];
[self.saveModel removeObserver:self forKeyPath:@"isLoaded"];
[self.saveModel removeObserver:self forKeyPath:@"error"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if (object == self.allSchoolModel) {
if ([keyPath isEqualToString:@"isLoaded"]) {
[self.allSchoolModel.AllSchooListArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
SchoolItem * item =obj;
[_schoolArray addObject:item.schoolName];
}];
dispatch_async(dispatch_get_main_queue(), ^{
_schoolPicker=nil ;
_schoolPicker = [[ZHPickView alloc] initPickviewWithArray:_schoolArray isHaveNavControler:YES];
_schoolPicker.delegate = self;
_schoolPicker.frame = CGRectMake(0, kHeight-_schoolPicker.frame.size.height, kWidth, _schoolPicker.frame.size.height);
self.nameLabel.text=self.userCenterItem.username;
});
}
else{
}
}
if (object == self.saveModel) {
if ([keyPath isEqualToString:@"isLoaded"]) {
self.nameLabel.text=self.userCenterItem.username;
}
else{
}
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadData];
self.allSchoolModel=[FetchAllSchoolModel new];
self.saveModel=[SaveUserInfoModel new];
[self registNotificationAndKVO];
_schoolArray = [[NSMutableArray alloc]init];
_allDataArray = [[NSArray alloc]init];
[self.allSchoolModel fetChAllSchool];
self.tableView.delegate=self;
self.tableView.dataSource=self;
self.tableView.tableFooterView=[UIView new];
self.nameLabel.text=self.userCenterItem.username;
self.countLabel.text=self.userCenterItem.account;
[self creatPick];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden=YES;
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden=NO;
}
- (void)creatPick{
/*
生日选择器
*/
NSDate *date=[NSDate date];
_birthdayPicker = [[ZHPickView alloc] initDatePickWithDate:date datePickerMode:UIDatePickerModeDate isHaveNavControler:NO];
_birthdayPicker.delegate=self;
/*
入学时间选择器
*/
_inschooltimePicker = [[ZHPickView alloc] initDatePickWithDate:date datePickerMode:UIDatePickerModeDate isHaveNavControler:NO];
_inschooltimePicker.delegate=self;
}
-(UIView *)saveBtnView{
if (confirmBtn) {
[confirmBtn removeFromSuperview];
confirmBtn=nil;
}
confirmBtn = [[UIButton alloc]initWithFrame:CGRectMake(12, 10, kWidth-24, 45)];
[confirmBtn setBackgroundImage:[UIImage imageNamed:@"确定按钮"] forState:UIControlStateNormal];
confirmBtn.backgroundColor = [UIColor whiteColor];
[confirmBtn setTitle:@"保 存" forState:UIControlStateNormal];
[confirmBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
confirmBtn.titleLabel.font = [UIFont systemFontOfSize:17.0];
[confirmBtn addTarget:self action:@selector(confirmBtnFuc) forControlEvents:UIControlEventTouchUpInside];
confirmBtn.layer.cornerRadius = 4.0;
return confirmBtn;
}
-(void)loadData{
sectionTitle=[[NSMutableArray alloc]initWithObjects:@"基本资料",@"学校信息",nil];
NSArray* group1;
if ([self.userCenterItem.version isEqualToString:@"2"]) {
if ([self.userCenterItem.userType isEqualToString:@"teacher"]) {
//教师账号
group1=@[@"姓 名",@"性 别",@"出生日期",@"设备账号"];
}else{
//学生账号
group1=@[@"姓 名",@"性 别",@"出生日期",@"学 号",@"设备账号"];
}
}else{
if ([self.userCenterItem.userType isEqualToString:@"teacher"]) {
//教师账号
group1=@[@"姓 名",@"性 别",@"出生日期"];
}
else{
//学生账号
group1=@[@"姓 名",@"性 别",@"出生日期",@"学 号"];
}
}
NSArray * group2;
if ([self.userCenterItem.userType isEqualToString:@"teacher"]) {
group2=@[@"学 校",@"校 区"];
}else{
group2=@[@"学 校",@"校 区",@"入学时间"];
}
dataArray=[[NSMutableArray alloc]initWithObjects:group1,group2,nil];
}
#pragma -mark UITableView delegate
//
- (CGFloat)tableView:(UITableView *)tableView
heightForFooterInSection:(NSInteger)section
{
if (section==1) {
return 80;
}
return 0.1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 50;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return dataArray.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray * arr=dataArray[section];
return arr.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell * cell;//=[tableView dequeueReusableCellWithIdentifier:@"MCCMineCell"];
if (cell==nil) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MCCMineCell"];
}
NSArray * tempArr;
if (indexPath.section<dataArray.count) {
tempArr=dataArray[indexPath.section];
}
if (indexPath.row<tempArr.count) {
// 左
NSString * learnedText=(tempArr)[indexPath.row];
NSAttributedString * attributeStr= [BYHelp AttributeStringWithString:learnedText value:[UIColor colorWithHexString:@"#b4b4b4"] range:NSMakeRange(0,learnedText.length)];
cell.textLabel.attributedText=attributeStr;
}
cell.selectionStyle= UITableViewCellSelectionStyleNone;
cell.separatorInset=UIEdgeInsetsMake(0, 15, 0, 15);
//右;
if (indexPath.section==0) {
CellTag=1000;
UIView * view=[cell viewWithTag:CellTag+indexPath.row];
[view removeFromSuperview];
if (indexPath.row==0) {
UITextField * nameTF=[ZLControl createzlTextFieldfont:[UIFont systemFontOfSize:15] frame:CGRectMake(120, 0, kWidth-100, 44) secureEntry:NO clearButtonMode:UITextFieldViewModeNever TextAlignment:NSTextAlignmentLeft placehoder:@"" text:self.userCenterItem.username textColor:[UIColor colorWithHexString:@"#1e1e1e"]];
nameTF.tag=CellTag+indexPath.row;
nameTF.delegate=self;
[cell addSubview:nameTF];
}
if (indexPath.row==1) {
NSString * sexStr = self.userCenterItem.gender ;
if ([sexStr isEqualToString:@"Male"])
{
_setString= @"男";
}
if ([sexStr isEqualToString:@"Female"])
{
_setString = @"女";
}
UIButton * sexButton=[ZLControl createzlButtonWithtitleColor:[UIColor colorWithHexString:@"#1e1e1e"] target:self sel:@selector(choiceSexFuc) event:UIControlEventTouchUpInside backGroundImage:nil image:nil backGrondColor:nil font:nil frame:CGRectMake(120, 0, kWidth-100, 44) title:_setString];
sexButton.tag=CellTag+indexPath.row;
sexButton.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft ;
[cell addSubview:sexButton];
}
if (indexPath.row==2) {
UIButton * birthDayBtn=[ZLControl createzlButtonWithtitleColor:[UIColor colorWithHexString:@"#1e1e1e"] target:self sel:@selector(choiceBirthdayFuc) event:UIControlEventTouchUpInside backGroundImage:nil image:nil backGrondColor:nil font:nil frame:CGRectMake(120, 0, kWidth-100, 44) title:self.userCenterItem.birthday];
birthDayBtn.tag=CellTag+indexPath.row;
birthDayBtn.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft ;
[cell addSubview:birthDayBtn];
}
if ([self.userCenterItem.userType isEqualToString:@"teacher"]) {
//教师账号
if (indexPath.row==3) {
//设备账号
UILabel *userIdLabel=[ZLControl createzlLabelWithtextColor:[UIColor colorWithHexString:@"#b4b4b4"] frame:CGRectMake(120, 0, kWidth-100, 44) font:nil backGroundColor:[UIColor whiteColor] TextAlignment:NSTextAlignmentLeft text:self.userCenterItem.sbaccount];
userIdLabel.tag=CellTag+indexPath.row;
[cell addSubview:userIdLabel];
}
}else{
if (indexPath.row==3) {
UITextField * studenNo=[ZLControl createzlTextFieldfont:[UIFont systemFontOfSize:15] frame:CGRectMake(120, 0, kWidth-100, 44) secureEntry:NO clearButtonMode:UITextFieldViewModeNever TextAlignment:NSTextAlignmentLeft placehoder:@"" text:self.userCenterItem.studentNo textColor:[UIColor colorWithHexString:@"#1e1e1e"]];
studenNo.keyboardType=UIKeyboardTypeNumberPad;
studenNo.tag=CellTag+indexPath.row;
studenNo.delegate=self;
[cell addSubview:studenNo];
}
if (indexPath.row==4) {
//设备账号
UILabel *userIdLabel=[ZLControl createzlLabelWithtextColor:[UIColor colorWithHexString:@"#b4b4b4"] frame:CGRectMake(120, 0, kWidth-100, 44) font:nil backGroundColor:[UIColor whiteColor] TextAlignment:NSTextAlignmentLeft text:self.userCenterItem.sbaccount];
userIdLabel.tag=CellTag+indexPath.row;
[cell addSubview:userIdLabel];
}
}
}
if (indexPath.section==1) {
CellTag=2000;
UIView * view=[cell viewWithTag:CellTag+indexPath.row];
[view removeFromSuperview];
if (indexPath.row==0) {
UIButton * schoolNameBtn=[ZLControl createzlButtonWithtitleColor:[UIColor colorWithHexString:@"#b4b4b4"] target:self sel:@selector(choiceSchoollFuc) event:UIControlEventTouchUpInside backGroundImage:nil image:nil backGrondColor:nil font:nil frame:CGRectMake(120, 0, kWidth-132, 44) title:self.userCenterItem.schoolName];
schoolNameBtn.tag=CellTag+indexPath.row;
// schoolNameBtn.enabled=NO;
schoolNameBtn.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft ;
[cell addSubview:schoolNameBtn];
}
if (indexPath.row==1) {
UIButton * CampusNameBtn=[ZLControl createzlButtonWithtitleColor:[UIColor colorWithHexString:@"#b4b4b4"] target:self sel:@selector(choiceCampusFuc) event:UIControlEventTouchUpInside backGroundImage:nil image:nil backGrondColor:nil font:nil frame:CGRectMake(120, 0, kWidth-132, 44) title:self.userCenterItem.campusName];
CampusNameBtn.tag=CellTag+indexPath.row;
CampusNameBtn.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft ;
[cell addSubview:CampusNameBtn];
}
if (indexPath.row==2) {
UIButton * enterSchoolBtn=[ZLControl createzlButtonWithtitleColor:[UIColor colorWithHexString:@"#1e1e1e"] target:self sel:@selector(choiceEntranceFuc) event:UIControlEventTouchUpInside backGroundImage:nil image:nil backGrondColor:nil font:nil frame:CGRectMake(120, 0, kWidth-100, 44) title:self.userCenterItem.enterSchoolDate];
enterSchoolBtn.tag=CellTag+indexPath.row;
enterSchoolBtn.contentHorizontalAlignment=UIControlContentHorizontalAlignmentLeft ;
[cell addSubview:enterSchoolBtn];
}
}
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return sectionTitle[section];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView * BgView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, kWidth, 50)];
UILabel * sectionTitleLable = [[UILabel alloc]initWithFrame:CGRectMake(15, 0, kWidth, 50)];
NSString *learnedText=sectionTitle[section];
NSAttributedString * attributeStr= [BYHelp AttributeStringWithString:learnedText value:[UIColor colorWithHexString:@"#b4b4b4"] range:NSMakeRange(0,learnedText.length)];
sectionTitleLable.attributedText= attributeStr;
sectionTitleLable.font = [UIFont systemFontOfSize:14.0];
[BgView addSubview:sectionTitleLable];
return BgView;
}
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
if (section==0) {
return nil;
}
UIView * footerView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, kWidth, 80)];
footerView.backgroundColor=[UIColor whiteColor];
[footerView addSubview:[self saveBtnView]];
return footerView;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.section==0) {
if (indexPath.row==0) {
}
else if (indexPath.row==1){
}
}
else if(indexPath.section==1)
{
}
}
#pragma mark clickAction
/*
选择性别
*/
-(void)choiceSexFuc
{
choiceSex=nil;
choiceSex = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
choiceSex.backgroundColor =[UIColor clearColor];
[self.view addSubview:choiceSex];
UIView * sexBlackView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
sexBlackView.backgroundColor = [UIColor blackColor];
sexBlackView.alpha = 0.4;
[choiceSex addSubview:sexBlackView];
UIView * sexWhiteView = [[UIView alloc]initWithFrame:CGRectMake((kWidth-100)/2, (kHeight-89)/2, 100, 89)];
sexWhiteView.backgroundColor = [UIColor whiteColor];
sexWhiteView.layer.cornerRadius = 4.0;
[choiceSex addSubview:sexWhiteView];
UIButton * manBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 100, 44)];
[manBtn setTitle:@"男" forState:UIControlStateNormal];
[manBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[manBtn addTarget:self action:@selector(manBtnFuc) forControlEvents:UIControlEventTouchUpInside];
[sexWhiteView addSubview:manBtn];
UIView * sexLineView = [[UIView alloc]initWithFrame:CGRectMake(0, 44, 100, 1)];
sexLineView.backgroundColor = [UIColor grayColor];
[sexWhiteView addSubview:sexLineView];
UIButton * womenBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 45, 100, 44)];
[womenBtn setTitle:@"女" forState:UIControlStateNormal];
[womenBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[womenBtn addTarget:self action:@selector(womenBtnFuc) forControlEvents:UIControlEventTouchUpInside];
[sexWhiteView addSubview:womenBtn];
}
/*
选择性别男
*/
-(void)manBtnFuc
{
[choiceSex removeFromSuperview];
self.userCenterItem.gender=@"Male";
[self.tableView reloadData];
}
/*
选择性别男
*/
-(void)womenBtnFuc
{
[choiceSex removeFromSuperview];
self.userCenterItem.gender=@"Female";
[self.tableView reloadData];
}
/*
选择生日
*/
-(void)choiceBirthdayFuc
{
[_birthdayPicker show];
}
/*
选择校区
*/
-(void)choiceCampusFuc
{
// if (_campusArray.count==0) {
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
// message:@"请先选择学校"
// delegate:nil
// cancelButtonTitle:@"我知道了"
// otherButtonTitles:nil];
// [alert show];
// }
// [_campusPicker show];
}
/*
选择学校
*/
-(void)choiceSchoollFuc
{
// NSLog(@"选择学校");
// [_schoolPicker show];
}
/*
选择入学时间
*/
-(void)choiceEntranceFuc
{
[_inschooltimePicker show];
}
- (IBAction)backBtnClick:(id)sender {
if (_birthdayPicker) {
[_birthdayPicker remove];
}
if (_schoolPicker) {
[_schoolPicker remove];
}
if (_campusPicker) {
[_campusPicker remove];
}
if (_inschooltimePicker) {
[_inschooltimePicker remove];
}
[self.navigationController popViewControllerAnimated:YES];
}
-(void)toobarDonBtnHaveClick:(ZHPickView *)pickView resultString:(NSString *)resultString
{
if (_birthdayPicker==pickView){
self.userCenterItem.birthday=[resultString substringToIndex:10];
}
if (_inschooltimePicker==pickView){
self.userCenterItem.enterSchoolDate=[resultString substringToIndex:10];
}
//选择学校
if (_schoolPicker==pickView){
[self.allSchoolModel.AllSchooListArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
SchoolItem * item =obj;
if ([resultString isEqualToString:item.schoolName]) {
self.userCenterItem.schoolName=resultString;
[item.campusList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
campusItem * campusitem=obj;
_campusArray= [[NSMutableArray alloc]init];
[_campusArray addObject:campusitem.campusName];
NSLog(@"+++%@",campusitem.campusName);
}];
dispatch_async(dispatch_get_main_queue(), ^{
_campusPicker=nil;
_campusPicker = [[ZHPickView alloc] initPickviewWithArray:_campusArray isHaveNavControler:YES];
_campusPicker.frame = CGRectMake(0, kHeight-_campusPicker.frame.size.height, kWidth, _campusPicker.frame.size.height);
_campusPicker.delegate = self;
});
return ;
}
}];
}
//选择校区
if (_campusPicker==pickView) {
self.userCenterItem.campusName=resultString;
}
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
if (textField.tag==1000) {
self.userCenterItem.username=textField.text;
if (textField.text.length>20) {
textField.text=[textField.text substringToIndex:19];
self.userCenterItem.username=[textField.text substringToIndex:19];
}
}
else if(textField.tag==1003){
self.userCenterItem.studentNo=textField.text;
}
}
-(void)confirmBtnFuc{
if ([BYHelp stringContainsEmoji:self.userCenterItem.username]) {
NSLog(@"含有表情");
if (_emotionAlert!=nil) {
[_emotionAlert removeFromSuperview];
_emotionAlert=nil;
}
_emotionAlert=[[EmotionAlertView alloc]initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
_emotionAlert.message=@"不能输入表情";
__weak typeof(self) weakSelf=self;
[_emotionAlert setOkClick:^{
[weakSelf.emotionAlert removeFromSuperview];
}];
AppDelegate * app = (AppDelegate*)[UIApplication sharedApplication].delegate;
[app.window addSubview:_emotionAlert];
[_emotionAlert mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.mas_equalTo(@(0));
}];
return;
}else {
NSLog(@"不含有表情");
}
if (self.userCenterItem.username.length==0 ) {
[ZXTool showText:@"用户名未填写"];
return;
}
if (self.userCenterItem.gender.length==0) {
[ZXTool showText:@"性别未填写"];
return;
}
if (self.userCenterItem.birthday.length==0 ) {
[ZXTool showText:@"出生日期未填写"];
return;
}
if (![self.userCenterItem.userType isEqualToString:@"teacher"]) {
if (self.userCenterItem.studentNo.length==0 ) {
[ZXTool showText:@"学号未填写"];
return;
}
}
if (self.userCenterItem.schoolName.length==0 ) {
[ZXTool showText:@"学校未填写"];
return;
}
if (self.userCenterItem.campusName.length==0 ) {
[ZXTool showText:@"校区未填写"];
return;
}
if (![self.userCenterItem.userType isEqualToString:@"teacher"]) {
if (self.userCenterItem.enterSchoolDate.length==0 ) {
[ZXTool showText:@"入学时间未填写"];
return;
}
}
//教师账号默认给个时间数据
if ([self.userCenterItem.userType isEqualToString:@"teacher"]) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat =@"yyyyMMddHHmmss";
NSString *timeStr = [formatter stringFromDate:[NSDate date]];
self.userCenterItem.enterSchoolDate=timeStr;
}
[self.saveModel saveUserInfoWith:self.userCenterItem];
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if (textField.tag==1000||textField.tag==1003) {
if(range.location>=20||textField.text.length+string.length>20)
{
if (string.length==0) {
return YES ;
}
return NO;
}
else{
return YES;
}
}
return YES;
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UserInfoViewController">
<connections>
<outlet property="countLabel" destination="gjl-Oz-xQK" id="42K-Mw-0bS"/>
<outlet property="nameLabel" destination="383-j9-XU4" id="p4D-Rh-rTR"/>
<outlet property="tableView" destination="ysd-OO-Byz" id="aEf-EX-cco"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wRZ-Pu-923">
<rect key="frame" x="0.0" y="0.0" width="375" height="213"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="mineNavBg" translatesAutoresizingMaskIntoConstraints="NO" id="Dmk-Ex-6bG">
<rect key="frame" x="0.0" y="0.0" width="375" height="213"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Head-portrait" translatesAutoresizingMaskIntoConstraints="NO" id="8Ve-aK-QjO">
<rect key="frame" x="21" y="96" width="80" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="WpZ-6c-kAN"/>
<constraint firstAttribute="width" constant="80" id="uYv-9V-SWh"/>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="X6K-MS-ziL">
<rect key="frame" x="116" y="96" width="249" height="80"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="383-j9-XU4">
<rect key="frame" x="0.0" y="20" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gjl-Oz-xQK">
<rect key="frame" x="0.0" y="44" width="33" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="gjl-Oz-xQK" firstAttribute="leading" secondItem="X6K-MS-ziL" secondAttribute="leading" id="aWp-ET-ScL"/>
<constraint firstItem="383-j9-XU4" firstAttribute="top" secondItem="X6K-MS-ziL" secondAttribute="top" constant="20" id="mDG-t3-zuE"/>
<constraint firstItem="383-j9-XU4" firstAttribute="leading" secondItem="X6K-MS-ziL" secondAttribute="leading" id="vlT-6D-yuU"/>
<constraint firstAttribute="bottom" secondItem="gjl-Oz-xQK" secondAttribute="bottom" constant="20" id="zWn-oh-QAO"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="个人信息" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zjT-VM-5xn">
<rect key="frame" x="151" y="25" width="73.5" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="B2U-xJ-iwx">
<rect key="frame" x="20" y="29" width="26" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="26" id="SOD-b0-TYe"/>
<constraint firstAttribute="height" constant="26" id="o9M-D4-ghg"/>
</constraints>
<state key="normal" title="Button" image="个人返回-1"/>
<connections>
<action selector="backBtnClick:" destination="-1" eventType="touchUpInside" id="He8-xJ-rse"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="8Ve-aK-QjO" firstAttribute="leading" secondItem="wRZ-Pu-923" secondAttribute="leading" constant="21" id="0Ps-3e-BIL"/>
<constraint firstAttribute="bottom" secondItem="Dmk-Ex-6bG" secondAttribute="bottom" id="Dc2-LQ-B8S"/>
<constraint firstItem="Dmk-Ex-6bG" firstAttribute="leading" secondItem="wRZ-Pu-923" secondAttribute="leading" id="Ps6-Pm-5j9"/>
<constraint firstAttribute="bottom" secondItem="8Ve-aK-QjO" secondAttribute="bottom" constant="37" id="S40-nA-cbW"/>
<constraint firstItem="Dmk-Ex-6bG" firstAttribute="top" secondItem="wRZ-Pu-923" secondAttribute="top" id="VDD-7C-cHU"/>
<constraint firstAttribute="trailing" secondItem="Dmk-Ex-6bG" secondAttribute="trailing" id="akT-j3-3Qp"/>
<constraint firstItem="X6K-MS-ziL" firstAttribute="centerY" secondItem="8Ve-aK-QjO" secondAttribute="centerY" id="d8U-u0-bn3"/>
<constraint firstAttribute="height" constant="213" id="e5h-GJ-Fzf"/>
<constraint firstItem="X6K-MS-ziL" firstAttribute="height" secondItem="8Ve-aK-QjO" secondAttribute="height" id="f2R-oc-CPv"/>
<constraint firstAttribute="trailing" secondItem="X6K-MS-ziL" secondAttribute="trailing" constant="10" id="hUa-75-Gss"/>
<constraint firstItem="X6K-MS-ziL" firstAttribute="leading" secondItem="8Ve-aK-QjO" secondAttribute="trailing" constant="15" id="hz3-j6-F3k"/>
<constraint firstItem="zjT-VM-5xn" firstAttribute="top" secondItem="wRZ-Pu-923" secondAttribute="top" constant="25" id="iPv-Gl-qaJ"/>
<constraint firstItem="zjT-VM-5xn" firstAttribute="centerX" secondItem="wRZ-Pu-923" secondAttribute="centerX" id="j7p-Sx-NBD"/>
<constraint firstItem="B2U-xJ-iwx" firstAttribute="top" secondItem="wRZ-Pu-923" secondAttribute="top" constant="29" id="k0p-Rl-2J6"/>
<constraint firstItem="B2U-xJ-iwx" firstAttribute="leading" secondItem="wRZ-Pu-923" secondAttribute="leading" constant="20" id="mnk-Em-2a4"/>
</constraints>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="ysd-OO-Byz">
<rect key="frame" x="0.0" y="213" width="375" height="454"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
</tableView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="ysd-OO-Byz" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="3pV-km-hCR"/>
<constraint firstAttribute="trailing" secondItem="ysd-OO-Byz" secondAttribute="trailing" id="5SX-Qo-wrn"/>
<constraint firstAttribute="bottom" secondItem="ysd-OO-Byz" secondAttribute="bottom" id="Duv-I7-nmK"/>
<constraint firstItem="wRZ-Pu-923" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="IFa-9T-bbs"/>
<constraint firstItem="ysd-OO-Byz" firstAttribute="top" secondItem="wRZ-Pu-923" secondAttribute="bottom" id="WJk-8P-Qxk"/>
<constraint firstItem="wRZ-Pu-923" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="o8g-Sj-rej"/>
<constraint firstAttribute="trailing" secondItem="wRZ-Pu-923" secondAttribute="trailing" id="x9x-Yn-cXh"/>
</constraints>
<point key="canvasLocation" x="34.5" y="53.5"/>
</view>
</objects>
<resources>
<image name="Head-portrait" width="161" height="161"/>
<image name="mineNavBg" width="380" height="214"/>
<image name="个人返回-1" width="58" height="58"/>
</resources>
</document>
../../../JJException/JJException/Source/Main/JJException.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJExceptionMacros.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJExceptionProxy.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSArray+ArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSAttributedString+AttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSDictionary+DictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableArray+MutableArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableAttributedString+MutableAttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableDictionary+MutableDictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableSet+MutableSetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableString+MutableStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSNotificationCenter+ClearNotification.h
\ No newline at end of file
../../../JJException/JJException/Source/DeallocBlock/NSObject+DeallocBlock.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+KVOCrash.h
\ No newline at end of file
../../../JJException/JJException/Source/Swizzle/NSObject+SwizzleHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+UnrecognizedSelectorHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+ZombieHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSSet+SetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSString+StringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSTimer+CleanTimer.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJException.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSArray+ArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSAttributedString+AttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSDictionary+DictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableArray+MutableArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableAttributedString+MutableAttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableDictionary+MutableDictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableSet+MutableSetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableString+MutableStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSNotificationCenter+ClearNotification.h
\ No newline at end of file
../../../JJException/JJException/Source/DeallocBlock/NSObject+DeallocBlock.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+KVOCrash.h
\ No newline at end of file
../../../JJException/JJException/Source/Swizzle/NSObject+SwizzleHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+UnrecognizedSelectorHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+ZombieHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSSet+SetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSString+StringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSTimer+CleanTimer.h
\ No newline at end of file
//
// NSAttributedString+AttributedStringHook.h
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSAttributedString (AttributedStringHook)
+ (void)jj_swizzleNSAttributedString;
@end
//
// NSAttributedString+AttributedStringHook.m
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSAttributedString+AttributedStringHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSAttributedString_AttributedStringHook)
@implementation NSAttributedString (AttributedStringHook)
+ (void)jj_swizzleNSAttributedString{
NSAttributedString* instanceObject = [NSAttributedString new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls, @selector(initWithString:), @selector(hookInitWithString:));
swizzleInstanceMethod(cls, @selector(attributedSubstringFromRange:), @selector(hookAttributedSubstringFromRange:));
swizzleInstanceMethod(cls, @selector(attribute:atIndex:effectiveRange:), @selector(hookAttribute:atIndex:effectiveRange:));
swizzleInstanceMethod(cls, @selector(enumerateAttribute:inRange:options:usingBlock:), @selector(hookEnumerateAttribute:inRange:options:usingBlock:));
swizzleInstanceMethod(cls, @selector(enumerateAttributesInRange:options:usingBlock:), @selector(hookEnumerateAttributesInRange:options:usingBlock:));
}
- (id)hookInitWithString:(NSString*)str{
if (str){
return [self hookInitWithString:str];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSAttributedString initWithString parameter nil");
return nil;
}
- (id)hookAttribute:(NSAttributedStringKey)attrName atIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range{
if (location < self.length){
return [self hookAttribute:attrName atIndex:location effectiveRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString attribute:atIndex:effectiveRange: attrName:%@ location:%tu",attrName,location]);
return nil;
}
- (NSAttributedString *)hookAttributedSubstringFromRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookAttributedSubstringFromRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString attributedSubstringFromRange range:%@",NSStringFromRange(range)]);
return nil;
}
- (void)hookEnumerateAttribute:(NSString *)attrName inRange:(NSRange)range options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)(id _Nullable, NSRange, BOOL * _Nonnull))block{
if (range.location + range.length <= self.length) {
[self hookEnumerateAttribute:attrName inRange:range options:opts usingBlock:block];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString enumerateAttribute attrName:%@ range:%@",attrName,NSStringFromRange(range)]);
}
}
- (void)hookEnumerateAttributesInRange:(NSRange)range options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)(NSDictionary<NSString*,id> * _Nonnull, NSRange, BOOL * _Nonnull))block{
if (range.location + range.length <= self.length) {
[self hookEnumerateAttributesInRange:range options:opts usingBlock:block];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString enumerateAttributesInRange range:%@",NSStringFromRange(range)]);
}
}
@end
//
// NSMutableAttributedString+MutableAttributedStringHook.h
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableAttributedString (MutableAttributedStringHook)
+ (void)jj_swizzleNSMutableAttributedString;
@end
//
// NSMutableAttributedString+MutableAttributedStringHook.m
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableAttributedString+MutableAttributedStringHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableAttributedString_MutableAttributedStringHook)
@implementation NSMutableAttributedString (MutableAttributedStringHook)
+ (void)jj_swizzleNSMutableAttributedString{
NSMutableAttributedString* instanceObject = [NSMutableAttributedString new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls,@selector(initWithString:), @selector(hookInitWithString:));
swizzleInstanceMethod(cls,@selector(initWithString:attributes:), @selector(hookInitWithString:attributes:));
swizzleInstanceMethod(cls,@selector(addAttribute:value:range:), @selector(hookAddAttribute:value:range:));
swizzleInstanceMethod(cls,@selector(addAttributes:range:), @selector(hookAddAttributes:range:));
swizzleInstanceMethod(cls,@selector(setAttributes:range:), @selector(hookSetAttributes:range:));
swizzleInstanceMethod(cls,@selector(removeAttribute:range:), @selector(hookRemoveAttribute:range:));
swizzleInstanceMethod(cls,@selector(deleteCharactersInRange:), @selector(hookDeleteCharactersInRange:));
swizzleInstanceMethod(cls,@selector(replaceCharactersInRange:withString:), @selector(hookReplaceCharactersInRange:withString:));
swizzleInstanceMethod(cls,@selector(replaceCharactersInRange:withAttributedString:), @selector(hookReplaceCharactersInRange:withAttributedString:));
}
- (id)hookInitWithString:(NSString*)str{
if (str){
return [self hookInitWithString:str];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString initWithString parameter nil");
return nil;
}
- (id)hookInitWithString:(NSString*)str attributes:(nullable NSDictionary*)attributes{
if (str){
return [self hookInitWithString:str attributes:attributes];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString initWithString:attributes: str:%@ attributes:%@",str,attributes]);
return nil;
}
- (void)hookAddAttribute:(id)name value:(id)value range:(NSRange)range{
if (!range.length) {
[self hookAddAttribute:name value:value range:range];
}else if (value){
if (range.location + range.length <= self.length) {
[self hookAddAttribute:name value:value range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString addAttribute:value:range: name:%@ value:%@ range:%@",name,value,NSStringFromRange(range)]);
}
}else {
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString addAttribute:value:range: value nil");
}
}
- (void)hookAddAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range{
if (!range.length) {
[self hookAddAttributes:attrs range:range];
}else if (attrs){
if (range.location + range.length <= self.length) {
[self hookAddAttributes:attrs range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString addAttributes:range: attrs:%@ range:%@",attrs,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString addAttributes:range: value nil");
}
}
- (void)hookSetAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range{
if (!range.length) {
[self hookSetAttributes:attrs range:range];
}else if (attrs){
if (range.location + range.length <= self.length) {
[self hookSetAttributes:attrs range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString setAttributes:range: attrs:%@ range:%@",attrs,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString setAttributes:range: attrs nil");
}
}
- (void)hookRemoveAttribute:(id)name range:(NSRange)range {
if (!range.length) {
[self hookRemoveAttribute:name range:range];
}else if (name){
if (range.location + range.length <= self.length) {
[self hookRemoveAttribute:name range:range];
}else {
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString removeAttribute:range: name:%@ range:%@",name,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString removeAttribute:range: attrs nil");
}
}
- (void)hookDeleteCharactersInRange:(NSRange)range {
if (range.location + range.length <= self.length) {
[self hookDeleteCharactersInRange:range];
}else {
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString deleteCharactersInRange: range:%@",NSStringFromRange(range)]);
}
}
- (void)hookReplaceCharactersInRange:(NSRange)range withString:(NSString *)str {
if (str){
if (range.location + range.length <= self.length) {
[self hookReplaceCharactersInRange:range withString:str];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString replaceCharactersInRange:withString string:%@ range:%@",str,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString replaceCharactersInRange:withString: string nil");
}
}
- (void)hookReplaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)str {
if (str){
if (range.location + range.length <= self.length) {
[self hookReplaceCharactersInRange:range withAttributedString:str];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString replaceCharactersInRange:withString string:%@ range:%@",str,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString replaceCharactersInRange:withString: attributedString nil");
}
}
@end
//
// NSMutableSet+MutableSetHook.h
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSMutableSet (MutableSetHook)
+ (void)jj_swizzleNSMutableSet;
@end
NS_ASSUME_NONNULL_END
//
// NSMutableSet+MutableSetHook.m
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableSet+MutableSetHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableSet_MutableSetHook)
@implementation NSMutableSet (MutableSetHook)
+ (void)jj_swizzleNSMutableSet{
NSMutableSet* instanceObject = [NSMutableSet new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls,@selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(cls,@selector(removeObject:), @selector(hookRemoveObject:));
}
- (void) hookAddObject:(id)object {
if (object) {
[self hookAddObject:object];
} else {
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet addObject nil object");
}
}
- (void) hookRemoveObject:(id)object {
if (object) {
[self hookRemoveObject:object];
} else {
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet removeObject nil object");
}
}
@end
//
// NSMutableString+MutableStringHook.h
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableString (MutableStringHook)
+ (void)jj_swizzleNSMutableString;
@end
//
// NSMutableString+MutableStringHook.m
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableString+MutableStringHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableString_MutableStringHook)
@implementation NSMutableString (MutableStringHook)
+ (void)jj_swizzleNSMutableString{
//__NSCFString
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(appendString:), @selector(hookAppendString:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(insertString:atIndex:), @selector(hookInsertString:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(deleteCharactersInRange:), @selector(hookDeleteCharactersInRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
}
- (void) hookAppendString:(NSString *)aString{
if (aString){
[self hookAppendString:aString];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString appendString value:%@ parameter nil",self]);
}
}
- (void) hookInsertString:(NSString *)aString atIndex:(NSUInteger)loc{
if (aString && loc <= self.length) {
[self hookInsertString:aString atIndex:loc];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString insertString:atIndex: value:%@ paremeter string:%@ atIndex:%tu",self,aString,loc]);
}
}
- (void) hookDeleteCharactersInRange:(NSRange)range{
if (range.location + range.length <= self.length){
[self hookDeleteCharactersInRange:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString deleteCharactersInRange value:%@ range:%@",self,NSStringFromRange(range)]);
}
}
- (NSString *)hookSubstringFromIndex:(NSUInteger)from{
if (from <= self.length) {
return [self hookSubstringFromIndex:from];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringFromIndex value:%@ from:%tu",self,from]);
return nil;
}
- (NSString *)hookSubstringToIndex:(NSUInteger)to{
if (to <= self.length) {
return [self hookSubstringToIndex:to];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringToIndex value:%@ to:%tu",self,to]);
return self;
}
- (NSString *)hookSubstringWithRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookSubstringWithRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringWithRange value:%@ range:%@",self,NSStringFromRange(range)]);
return nil;
}
@end
//
// NSNotificationCenter+ClearNotification.h
// JJException
//
// Created by Jezz on 2018/9/6.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSNotificationCenter (ClearNotification)
+ (void)jj_swizzleNSNotificationCenter;
@end
//
// NSNotificationCenter+ClearNotification.m
// JJException
//
// Created by Jezz on 2018/9/6.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSNotificationCenter+ClearNotification.h"
#import "NSObject+SwizzleHook.h"
#import "NSObject+DeallocBlock.h"
#import "JJExceptionMacros.h"
#import <objc/runtime.h>
JJSYNTH_DUMMY_CLASS(NSNotificationCenter_ClearNotification)
@implementation NSNotificationCenter (ClearNotification)
+ (void)jj_swizzleNSNotificationCenter{
[self jj_swizzleInstanceMethod:@selector(addObserver:selector:name:object:) withSwizzledBlock:^id(JJSwizzleObject *swizzleInfo) {
return ^(__unsafe_unretained id self,id observer,SEL aSelector,NSString* aName,id anObject){
[self processAddObserver:observer selector:aSelector name:aName object:anObject swizzleInfo:swizzleInfo];
};
}];
}
- (void)processAddObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject swizzleInfo:(JJSwizzleObject*)swizzleInfo{
if (!observer) {
return;
}
if ([observer isKindOfClass:NSObject.class]) {
__unsafe_unretained typeof(observer) unsafeObject = observer;
[observer jj_deallocBlock:^{
[[NSNotificationCenter defaultCenter] removeObserver:unsafeObject];
}];
}
void(*originIMP)(__unsafe_unretained id,SEL,id,SEL,NSString*,id);
originIMP = (__typeof(originIMP))[swizzleInfo getOriginalImplementation];
if (originIMP != NULL) {
originIMP(self,swizzleInfo.selector,observer,aSelector,aName,anObject);
}
}
@end
//
// NSSet+SetHook.h
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSSet (SetHook)
+ (void)jj_swizzleNSSet;
@end
NS_ASSUME_NONNULL_END
//
// NSSet+SetHook.m
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSSet+SetHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSSet_SetHook)
@implementation NSSet (SetHook)
+ (void)jj_swizzleNSSet{
[NSSet jj_swizzleClassMethod:@selector(setWithObject:) withSwizzleMethod:@selector(hookSetWithObject:)];
}
+ (instancetype)hookSetWithObject:(id)object{
if (object){
return [self hookSetWithObject:object];
}
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet setWithObject nil object");
return nil;
}
@end
//
// NSString+StringHook.h
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (StringHook)
+ (void)jj_swizzleNSString;
@end
//
// NSString+StringHook.m
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSString+StringHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSString_StringHook)
@implementation NSString (StringHook)
+ (void)jj_swizzleNSString{
[NSString jj_swizzleClassMethod:@selector(stringWithUTF8String:) withSwizzleMethod:@selector(hookStringWithUTF8String:)];
[NSString jj_swizzleClassMethod:@selector(stringWithCString:encoding:) withSwizzleMethod:@selector(hookStringWithCString:encoding:)];
//NSPlaceholderString
swizzleInstanceMethod(NSClassFromString(@"NSPlaceholderString"), @selector(initWithCString:encoding:), @selector(hookInitWithCString:encoding:));
swizzleInstanceMethod(NSClassFromString(@"NSPlaceholderString"), @selector(initWithString:), @selector(hookInitWithString:));
//_NSCFConstantString
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(rangeOfString:options:range:locale:), @selector(hookRangeOfString:options:range:locale:));
//NSTaggedPointerString
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(rangeOfString:options:range:locale:), @selector(hookRangeOfString:options:range:locale:));
}
+ (NSString*) hookStringWithUTF8String:(const char *)nullTerminatedCString{
if (NULL != nullTerminatedCString) {
return [self hookStringWithUTF8String:nullTerminatedCString];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString stringWithUTF8String NULL char pointer");
return nil;
}
+ (nullable instancetype) hookStringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
{
if (NULL != cString){
return [self hookStringWithCString:cString encoding:enc];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString stringWithCString:encoding: NULL char pointer");
return nil;
}
- (nullable instancetype) hookInitWithString:(id)cString{
if (nil != cString){
return [self hookInitWithString:cString];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString initWithString nil parameter");
return nil;
}
- (nullable instancetype) hookInitWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding{
if (NULL != nullTerminatedCString){
return [self hookInitWithCString:nullTerminatedCString encoding:encoding];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString initWithCString:encoding NULL char pointer");
return nil;
}
- (NSString *)hookSubstringFromIndex:(NSUInteger)from{
if (from <= self.length) {
return [self hookSubstringFromIndex:from];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringFromIndex value:%@ from:%tu",self,from]);
return nil;
}
- (NSString *)hookSubstringToIndex:(NSUInteger)to{
if (to <= self.length) {
return [self hookSubstringToIndex:to];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringToIndex value:%@ from:%tu",self,to]);
return self;
}
- (NSString *)hookSubstringWithRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookSubstringWithRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringWithRange value:%@ range:%@",self,NSStringFromRange(range)]);
return nil;
}
- (NSRange)hookRangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)range locale:(nullable NSLocale *)locale{
if (searchString){
if (range.location + range.length <= self.length) {
return [self hookRangeOfString:searchString options:mask range:range locale:locale];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString rangeOfString:options:range:locale: value:%@ range:%@",self,NSStringFromRange(range)]);
return NSMakeRange(NSNotFound, 0);
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString rangeOfString:options:range:locale: searchString nil value:%@ range:%@",self,NSStringFromRange(range)]);
return NSMakeRange(NSNotFound, 0);
}
}
@end
//
// NSTimer+CleanResource.h
// JJException
//
// Created by Jezz on 2018/9/4.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSTimer (CleanTimer)
+ (void)jj_swizzleNSTimer;
@end
//
// NSTimer+CleanResource.m
// JJException
//
// Created by Jezz on 2018/9/4.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSTimer+CleanTimer.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
/**
Copy the NSTimer Info
*/
@interface TimerObject : NSObject
@property(nonatomic,readwrite,assign)NSTimeInterval ti;
/**
weak reference target
*/
@property(nonatomic,readwrite,weak)id target;
@property(nonatomic,readwrite,assign)SEL selector;
@property(nonatomic,readwrite,assign)id userInfo;
/**
TimerObject Associated NSTimer
*/
@property(nonatomic,readwrite,weak)NSTimer* timer;
/**
Record the target class name
*/
@property(nonatomic,readwrite,copy)NSString* targetClassName;
/**
Record the target method name
*/
@property(nonatomic,readwrite,copy)NSString* targetMethodName;
@end
@implementation TimerObject
- (void)fireTimer{
if (!self.target) {
[self.timer invalidate];
self.timer = nil;
handleCrashException(JJExceptionGuardNSTimer,[NSString stringWithFormat:@"Need invalidate timer from target:%@ method:%@",self.targetClassName,self.targetMethodName]);
return;
}
if ([self.target respondsToSelector:self.selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.target performSelector:self.selector withObject:self.timer];
#pragma clang diagnostic pop
}
}
@end
@implementation NSTimer (CleanTimer)
+ (void)jj_swizzleNSTimer{
swizzleClassMethod([NSTimer class], @selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:), @selector(hookScheduledTimerWithTimeInterval:target:selector:userInfo:repeats:));
}
+ (NSTimer*)hookScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
if (!yesOrNo) {
return [self hookScheduledTimerWithTimeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo];
}
TimerObject* timerObject = [TimerObject new];
timerObject.ti = ti;
timerObject.target = aTarget;
timerObject.selector = aSelector;
timerObject.userInfo = userInfo;
if (aTarget) {
timerObject.targetClassName = [NSString stringWithCString:object_getClassName(aTarget) encoding:NSASCIIStringEncoding];
}
timerObject.targetMethodName = NSStringFromSelector(aSelector);
NSTimer* timer = [NSTimer hookScheduledTimerWithTimeInterval:ti target:timerObject selector:@selector(fireTimer) userInfo:userInfo repeats:yesOrNo];
timerObject.timer = timer;
return timer;
}
@end
//
// NSObject+DeallocBlock.h
// JJException
//
// Created by Jezz on 2018/9/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (DeallocBlock)
/**
Observer current instance class dealloc action
@param block dealloc callback
*/
- (void)jj_deallocBlock:(void(^)(void))block;
@end
//
// NSObject+DeallocBlock.m
// JJException
//
// Created by Jezz on 2018/9/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+DeallocBlock.h"
#import <objc/runtime.h>
static const char DeallocNSObjectKey;
/**
Observer the target middle object
*/
@interface DeallocStub : NSObject
@property (nonatomic,readwrite,copy) void(^deallocBlock)(void);
@end
@implementation DeallocStub
- (void)dealloc {
if (self.deallocBlock) {
self.deallocBlock();
}
self.deallocBlock = nil;
}
@end
@implementation NSObject (DeallocBlock)
- (void)jj_deallocBlock:(void(^)(void))block{
@synchronized(self){
NSMutableArray* blockArray = objc_getAssociatedObject(self, &DeallocNSObjectKey);
if (!blockArray) {
blockArray = [NSMutableArray array];
objc_setAssociatedObject(self, &DeallocNSObjectKey, blockArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
DeallocStub *stub = [DeallocStub new];
stub.deallocBlock = block;
[blockArray addObject:stub];
}
}
@end
//
// NSArray+ArrayHook.h
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSArray (ArrayHook)
+ (void)jj_swizzleNSArray;
@end
//
// NSArray+ArrayHook.m
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSArray+ArrayHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSArray_ArrayHook)
@implementation NSArray (ArrayHook)
+ (void)jj_swizzleNSArray{
[NSArray jj_swizzleClassMethod:@selector(arrayWithObject:) withSwizzleMethod:@selector(hookArrayWithObject:)];
[NSArray jj_swizzleClassMethod:@selector(arrayWithObjects:count:) withSwizzleMethod:@selector(hookArrayWithObjects:count:)];
/* __NSArray0 */
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayI */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayI_Transfer */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* above iOS10 __NSSingleObjectArrayI */
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSFrozenArrayM */
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayReversed */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
}
+ (instancetype) hookArrayWithObject:(id)anObject
{
if (anObject) {
return [self hookArrayWithObject:anObject];
}
handleCrashException(JJExceptionGuardArrayContainer,@"NSArray arrayWithObject object is nil");
return nil;
}
- (id) hookObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
return [self hookObjectAtIndex:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray objectAtIndex invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (id) hookObjectAtIndexedSubscript:(NSInteger)index {
if (index < self.count) {
return [self hookObjectAtIndexedSubscript:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray objectAtIndexedSubscript invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (NSArray *)hookSubarrayWithRange:(NSRange)range
{
if (range.location + range.length <= self.count){
return [self hookSubarrayWithRange:range];
}else if (range.location < self.count){
return [self hookSubarrayWithRange:NSMakeRange(range.location, self.count-range.location)];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray subarrayWithRange invalid range location:%tu length:%tu",range.location,range.length]);
return nil;
}
+ (instancetype)hookArrayWithObjects:(const id [])objects count:(NSUInteger)cnt
{
NSInteger index = 0;
id objs[cnt];
for (NSInteger i = 0; i < cnt ; ++i) {
if (objects[i]) {
objs[index++] = objects[i];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray arrayWithObjects invalid index object:%tu total:%tu",i,cnt]);
}
}
return [self hookArrayWithObjects:objs count:index];
}
@end
//
// NSDictionary+DictionaryHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSDictionary (DictionaryHook)
+ (void)jj_swizzleNSDictionary;
@end
//
// NSDictionary+DictionaryHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSDictionary+DictionaryHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSDictionary_DictionaryHook)
@implementation NSDictionary (DictionaryHook)
+ (void)jj_swizzleNSDictionary{
[NSDictionary jj_swizzleClassMethod:@selector(dictionaryWithObject:forKey:) withSwizzleMethod:@selector(hookDictionaryWithObject:forKey:)];
[NSDictionary jj_swizzleClassMethod:@selector(dictionaryWithObjects:forKeys:count:) withSwizzleMethod:@selector(hookDictionaryWithObjects:forKeys:count:)];
}
+ (instancetype) hookDictionaryWithObject:(id)object forKey:(id)key
{
if (object && key) {
return [self hookDictionaryWithObject:object forKey:key];
}
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSDictionary dictionaryWithObject invalid object:%@ and key:%@",object,key]);
return nil;
}
+ (instancetype) hookDictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt
{
NSInteger index = 0;
id ks[cnt];
id objs[cnt];
for (NSInteger i = 0; i < cnt ; ++i) {
if (keys[i] && objects[i]) {
ks[index] = keys[i];
objs[index] = objects[i];
++index;
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSDictionary dictionaryWithObjects invalid keys:%@ and object:%@",keys[i],objects[i]]);
}
}
return [self hookDictionaryWithObjects:objs forKeys:ks count:index];
}
@end
//
// NSMutableArray+MutableArrayHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableArray (MutableArrayHook)
+ (void)jj_swizzleNSMutableArray;
@end
//
// NSMutableArray+MutableArrayHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableArray+MutableArrayHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableArray_MutableArrayHook)
@implementation NSMutableArray (MutableArrayHook)
+ (void)jj_swizzleNSMutableArray{
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(insertObject:atIndex:), @selector(hookInsertObject:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(removeObjectAtIndex:), @selector(hookRemoveObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(removeObjectsInRange:), @selector(hookRemoveObjectsInRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(insertObject:atIndex:), @selector(hookInsertObject:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(removeObjectAtIndex:), @selector(hookRemoveObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(removeObjectsInRange:), @selector(hookRemoveObjectsInRange:));
}
- (void) hookAddObject:(id)anObject {
if (anObject) {
[self hookAddObject:anObject];
}else{
handleCrashException(JJExceptionGuardArrayContainer,@"NSMutableArray addObject nil object");
}
}
- (id) hookObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
return [self hookObjectAtIndex:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray objectAtIndex invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (id) hookObjectAtIndexedSubscript:(NSInteger)index {
if (index < self.count) {
return [self hookObjectAtIndexedSubscript:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray objectAtIndexedSubscript invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (void) hookInsertObject:(id)anObject atIndex:(NSUInteger)index {
if (anObject && index <= self.count) {
[self hookInsertObject:anObject atIndex:index];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray insertObject invalid index:%tu total:%tu insert object:%@",index,self.count,anObject]);
}
}
- (void) hookRemoveObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
[self hookRemoveObjectAtIndex:index];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray removeObjectAtIndex invalid index:%tu total:%tu",index,self.count]);
}
}
- (void) hookReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
if (index < self.count && anObject) {
[self hookReplaceObjectAtIndex:index withObject:anObject];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray replaceObjectAtIndex invalid index:%tu total:%tu replace object:%@",index,self.count,anObject]);
}
}
- (void) hookRemoveObjectsInRange:(NSRange)range {
if (range.location + range.length <= self.count) {
[self hookRemoveObjectsInRange:range];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray removeObjectsInRange invalid range location:%tu length:%tu",range.location,range.length]);
}
}
- (NSArray *)hookSubarrayWithRange:(NSRange)range
{
if (range.location + range.length <= self.count){
return [self hookSubarrayWithRange:range];
}else if (range.location < self.count){
return [self hookSubarrayWithRange:NSMakeRange(range.location, self.count-range.location)];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray subarrayWithRange invalid range location:%tu length:%tu",range.location,range.length]);
return nil;
}
@end
//
// NSMutableDictionary+MutableDictionaryHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableDictionary (MutableDictionaryHook)
+ (void)jj_swizzleNSMutableDictionary;
@end
//
// NSMutableDictionary+MutableDictionaryHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableDictionary+MutableDictionaryHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableDictionary_MutableDictionaryHook)
@implementation NSMutableDictionary (MutableDictionaryHook)
+ (void)jj_swizzleNSMutableDictionary{
swizzleInstanceMethod(NSClassFromString(@"__NSDictionaryM"), @selector(setObject:forKey:), @selector(hookSetObject:forKey:));
swizzleInstanceMethod(NSClassFromString(@"__NSDictionaryM"), @selector(removeObjectForKey:), @selector(hookRemoveObjectForKey:));
}
- (void) hookSetObject:(id)object forKey:(id)key {
if (object && key) {
[self hookSetObject:object forKey:key];
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSMutableDictionary setObject invalid object:%@ and key:%@",object,key],self);
}
}
- (void) hookRemoveObjectForKey:(id)key {
if (key) {
[self hookRemoveObjectForKey:key];
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,@"NSMutableDictionary removeObjectForKey nil key",self);
}
}
@end
//
// NSObject+KVOCrash.h
// JJException
//
// Created by Jezz on 2018/8/29.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (KVOCrash)
+ (void)jj_swizzleKVOCrash;
@end
//
// NSObject+KVOCrash.m
// JJException
//
// Created by Jezz on 2018/8/29.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+KVOCrash.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "JJExceptionProxy.h"
static const char DeallocKVOKey;
static const char ObserverDeallocKVOKey;
/**
Record the kvo object
Override the isEqual and hash method
*/
@interface KVOObjectItem : NSObject
@property(nonatomic,readwrite,assign)NSObject* observer;
@property(nonatomic,readwrite,copy)NSString* keyPath;
@property(nonatomic,readwrite,assign)NSKeyValueObservingOptions options;
@property(nonatomic,readwrite,assign)void* context;
@end
@implementation KVOObjectItem
- (BOOL)isEqual:(KVOObjectItem*)object{
if ([self.observer isEqual:object.observer] && [self.keyPath isEqualToString:object.keyPath]) {
return YES;
}
return NO;
}
- (NSUInteger)hash{
return [self.observer hash] ^ [self.keyPath hash];
}
- (void)dealloc{
self.observer = nil;
self.context = nil;
if (self.keyPath) {
[self.keyPath release];
}
[super dealloc];
}
@end
@interface KVOObjectContainer : NSObject
/**
KVO object array set
*/
@property(nonatomic,readwrite,retain)NSMutableSet* kvoObjectSet;
/**
Associated owner object
*/
@property(nonatomic,readwrite,unsafe_unretained)NSObject* whichObject;
/**
NSMutableSet safe-thread
*/
#if OS_OBJECT_HAVE_OBJC_SUPPORT
@property(nonatomic,readwrite,retain)dispatch_semaphore_t kvoLock;
#else
@property(nonatomic,readwrite,assign)dispatch_semaphore_t kvoLock;
#endif
- (void)addKVOObjectItem:(KVOObjectItem*)item;
- (void)removeKVOObjectItem:(KVOObjectItem*)item;
- (BOOL)checkKVOItemExist:(KVOObjectItem*)item;
@end
@implementation KVOObjectContainer
- (void)addKVOObjectItem:(KVOObjectItem*)item{
if (item) {
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
[self.kvoObjectSet addObject:item];
dispatch_semaphore_signal(self.kvoLock);
}
}
- (void)removeKVOObjectItem:(KVOObjectItem*)item{
if (item) {
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
[self.kvoObjectSet removeObject:item];
dispatch_semaphore_signal(self.kvoLock);
}
}
- (BOOL)checkKVOItemExist:(KVOObjectItem*)item{
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
BOOL exist = NO;
if (!item) {
dispatch_semaphore_signal(self.kvoLock);
return exist;
}
exist = [self.kvoObjectSet containsObject:item];
dispatch_semaphore_signal(self.kvoLock);
return exist;
}
- (dispatch_semaphore_t)kvoLock{
if (!_kvoLock) {
_kvoLock = dispatch_semaphore_create(1);
return _kvoLock;
}
return _kvoLock;
}
/**
Clean the kvo object array and temp var
release the dispatch_semaphore
*/
- (void)dealloc{
[self.kvoObjectSet release];
self.whichObject = nil;
dispatch_release(self.kvoLock);
[super dealloc];
}
- (void)cleanKVOData{
for (KVOObjectItem* item in self.kvoObjectSet) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
@try {
((void(*)(id,SEL,id,NSString*))objc_msgSend)(self.whichObject,@selector(hookRemoveObserver:forKeyPath:),item.observer,item.keyPath);
}@catch (NSException *exception) {
}
#pragma clang diagnostic pop
}
}
- (NSMutableSet*)kvoObjectSet{
if(_kvoObjectSet){
return _kvoObjectSet;
}
_kvoObjectSet = [[NSMutableSet alloc] init];
return _kvoObjectSet;
}
@end
@interface JJObserverContainer : NSObject
@property (nonatomic,readwrite,retain) NSHashTable* observers;
/**
Associated owner object
*/
@property(nonatomic,readwrite,assign) NSObject* whichObject;
- (void)addObserver:(KVOObjectItem *)observer;
- (void)removeObserver:(KVOObjectItem *)observer;
@end
@implementation JJObserverContainer
- (instancetype)init
{
self = [super init];
if (self) {
self.observers = [NSHashTable hashTableWithOptions:NSMapTableWeakMemory];
}
return self;
}
- (void)addObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers addObject:observer];
}
}
- (void)removeObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers removeObject:observer];
}
}
- (void)cleanObservers{
for (KVOObjectItem* item in self.observers) {
[self.whichObject removeObserver:item.observer forKeyPath:item.keyPath];
}
@synchronized (self) {
[self.observers removeAllObjects];
}
}
- (void)dealloc{
self.whichObject = nil;
[self.observers release];
[super dealloc];
}
@end
@implementation NSObject (KVOCrash)
+ (void)jj_swizzleKVOCrash{
swizzleInstanceMethod([self class], @selector(addObserver:forKeyPath:options:context:), @selector(hookAddObserver:forKeyPath:options:context:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:), @selector(hookRemoveObserver:forKeyPath:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:context:), @selector(hookRemoveObserver:forKeyPath:context:));
}
- (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
return;
}
if (!observer || keyPath.length == 0) {
return;
}
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self,&DeallocKVOKey);
KVOObjectItem* item = [[KVOObjectItem alloc] init];
item.observer = observer;
item.keyPath = keyPath;
item.options = options;
item.context = context;
if (!objectContainer) {
objectContainer = [KVOObjectContainer new];
[objectContainer setWhichObject:self];
objc_setAssociatedObject(self, &DeallocKVOKey, objectContainer, OBJC_ASSOCIATION_RETAIN);
[objectContainer release];
}
if (![objectContainer checkKVOItemExist:item]) {
[objectContainer addKVOObjectItem:item];
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
}
JJObserverContainer* observerContainer = objc_getAssociatedObject(observer,&ObserverDeallocKVOKey);
if (!observerContainer) {
observerContainer = [JJObserverContainer new];
[observerContainer setWhichObject:self];
[observerContainer addObserver:item];
objc_setAssociatedObject(observer, &ObserverDeallocKVOKey, observerContainer, OBJC_ASSOCIATION_RETAIN);
[observerContainer release];
}else{
[observerContainer addObserver:item];
}
[item release];
jj_swizzleDeallocIfNeeded(self.class);
jj_swizzleDeallocIfNeeded(observer.class);
}
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void*)context{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookRemoveObserver:observer forKeyPath:keyPath context:context];
return;
}
[self removeObserver:observer forKeyPath:keyPath];
}
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookRemoveObserver:observer forKeyPath:keyPath];
return;
}
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
if (!observer) {
return;
}
if (!objectContainer) {
return;
}
KVOObjectItem* item = [[KVOObjectItem alloc] init];
item.observer = observer;
item.keyPath = keyPath;
if ([objectContainer checkKVOItemExist:item]) {
@try {
[self hookRemoveObserver:observer forKeyPath:keyPath];
}@catch (NSException *exception) {
}
[objectContainer removeKVOObjectItem:item];
}
[item release];
}
/**
Ignore Special Library
@param object Instance Class
@return YES or NO
*/
- (BOOL)ignoreKVOInstanceClass:(id)object{
if (!object) {
return NO;
}
//Ignore ReactiveCocoa
if (object_getClass(object) == objc_getClass("RACKVOProxy")) {
return YES;
}
//Ignore AMAP
NSString* className = NSStringFromClass(object_getClass(object));
if ([className hasPrefix:@"AMap"]) {
return YES;
}
return NO;
}
/**
* Hook the kvo object dealloc and to clean the kvo array
*/
- (void)jj_cleanKVO{
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
JJObserverContainer* observerContainer = objc_getAssociatedObject(self, &ObserverDeallocKVOKey);
if (objectContainer) {
[objectContainer cleanKVOData];
}else if(observerContainer){
[observerContainer cleanObservers];
}
}
@end
//
// NSObject+UnrecognizedSelectorHook.h
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (UnrecognizedSelectorHook)
+ (void)jj_swizzleUnrecognizedSelector;
@end
//
// NSObject+UnrecognizedSelectorHook.m
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+UnrecognizedSelectorHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSObject_UnrecognizedSelectorHook)
@implementation NSObject (UnrecognizedSelectorHook)
+ (void)jj_swizzleUnrecognizedSelector{
//Class Method
swizzleClassMethod([self class], @selector(methodSignatureForSelector:), @selector(classMethodSignatureForSelectorSwizzled:));
swizzleClassMethod([self class], @selector(forwardInvocation:), @selector(forwardClassInvocationSwizzled:));
//Instance Method
swizzleInstanceMethod([self class], @selector(methodSignatureForSelector:), @selector(methodSignatureForSelectorSwizzled:));
swizzleInstanceMethod([self class], @selector(forwardInvocation:), @selector(forwardInvocationSwizzled:));
}
+ (NSMethodSignature*)classMethodSignatureForSelectorSwizzled:(SEL)aSelector {
NSMethodSignature* methodSignature = [self classMethodSignatureForSelectorSwizzled:aSelector];
if (methodSignature) {
return methodSignature;
}
return [self.class checkObjectSignatureAndCurrentClass:self.class];
}
- (NSMethodSignature*)methodSignatureForSelectorSwizzled:(SEL)aSelector {
NSMethodSignature* methodSignature = [self methodSignatureForSelectorSwizzled:aSelector];
if (methodSignature) {
return methodSignature;
}
return [self.class checkObjectSignatureAndCurrentClass:self.class];
}
/**
* Check the class method signature to the [NSObject class]
* If not equals,return nil
* If equals,return the v@:@ method
@param currentClass Class
@return NSMethodSignature
*/
+ (NSMethodSignature *)checkObjectSignatureAndCurrentClass:(Class)currentClass{
IMP originIMP = class_getMethodImplementation([NSObject class], @selector(methodSignatureForSelector:));
IMP currentClassIMP = class_getMethodImplementation(currentClass, @selector(methodSignatureForSelector:));
// If current class override methodSignatureForSelector return nil
if (originIMP != currentClassIMP){
return nil;
}
// Customer method signature
// void xxx(id,sel,id)
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
/**
Forward instance object
@param invocation NSInvocation
*/
- (void)forwardInvocationSwizzled:(NSInvocation*)invocation{
NSString* message = [NSString stringWithFormat:@"Unrecognized instance class:%@ and selector:%@",NSStringFromClass(self.class),NSStringFromSelector(invocation.selector)];
handleCrashException(JJExceptionGuardUnrecognizedSelector,message);
}
/**
Forward class object
@param invocation NSInvocation
*/
+ (void)forwardClassInvocationSwizzled:(NSInvocation*)invocation{
NSString* message = [NSString stringWithFormat:@"Unrecognized static class:%@ and selector:%@",NSStringFromClass(self.class),NSStringFromSelector(invocation.selector)];
handleCrashException(JJExceptionGuardUnrecognizedSelector,message);
}
@end
//
// NSObject+Zombie.h
// JJException
//
// Created by Jezz on 2018/7/26.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (ZombieHook)
+ (void)jj_swizzleZombie;
@end
//
// NSObject+Zombie.m
// JJException
//
// Created by Jezz on 2018/7/26.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+ZombieHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
const NSInteger MAX_ARRAY_SIZE = 1024 * 1024 * 5;// MAX Memeory Size 5M
@interface ZombieSelectorHandle : NSObject
@property(nonatomic,readwrite,assign)id fromObject;
@end
@implementation ZombieSelectorHandle
void unrecognizedSelectorZombie(ZombieSelectorHandle* self, SEL _cmd){
}
@end
@interface JJZombieSub : NSObject
@end
@implementation JJZombieSub
- (id)forwardingTargetForSelector:(SEL)selector{
NSMethodSignature* sign = [self methodSignatureForSelector:selector];
if (!sign) {
id stub = [[ZombieSelectorHandle new] autorelease];
[stub setFromObject:self];
class_addMethod([stub class], selector, (IMP)unrecognizedSelectorZombie, "v@:");
return stub;
}
return [super forwardingTargetForSelector:selector];
}
@end
@implementation NSObject (ZombieHook)
+ (void)jj_swizzleZombie{
[self jj_swizzleInstanceMethod:@selector(dealloc) withSwizzleMethod:@selector(hookDealloc)];
}
- (void)hookDealloc{
Class currentClass = self.class;
//Check black list
if (![[[JJExceptionProxy shareExceptionProxy] blackClassesSet] containsObject:currentClass]) {
[self hookDealloc];
return;
}
//Check the array max size
//TODO:Real remove less than MAX_ARRAY_SIZE
if ([JJExceptionProxy shareExceptionProxy].currentClassSize > MAX_ARRAY_SIZE) {
id object = [[JJExceptionProxy shareExceptionProxy] objectFromCurrentClassesSet];
[[JJExceptionProxy shareExceptionProxy] removeCurrentZombieClass:object_getClass(object)];
object?free(object):nil;
}
objc_destructInstance(self);
object_setClass(self, [JJZombieSub class]);
[[JJExceptionProxy shareExceptionProxy] addCurrentZombieClass:currentClass];
}
@end
//
// JJException.h
// JJException
//
// Created by Jezz on 2018/7/21.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Before start JJException,must config the JJExceptionGuardCategory
- JJExceptionGuardNone: Do not guard normal crash exception
- JJExceptionGuardUnrecognizedSelector: Unrecognized Selector Exception
- JJExceptionGuardDictionaryContainer: NSDictionary,NSMutableDictionary
- JJExceptionGuardArrayContainer: NSArray,NSMutableArray
- JJExceptionGuardZombie: Zombie
- JJExceptionGuardKVOCrash: KVO exception
- JJExceptionGuardNSTimer: NSTimer
- JJExceptionGuardNSNotificationCenter: NSNotificationCenter
- JJExceptionGuardNSStringContainer:NSString,NSMutableString,NSAttributedString,NSMutableAttributedString
- JJExceptionGuardAllExceptZombie:Above All except Zombie
- JJExceptionGuardAll: Above All
*/
typedef NS_OPTIONS(NSInteger,JJExceptionGuardCategory){
JJExceptionGuardNone = 0,
JJExceptionGuardUnrecognizedSelector = 1 << 1,
JJExceptionGuardDictionaryContainer = 1 << 2,
JJExceptionGuardArrayContainer = 1 << 3,
JJExceptionGuardZombie = 1 << 4,
JJExceptionGuardKVOCrash = 1 << 5,
JJExceptionGuardNSTimer = 1 << 6,
JJExceptionGuardNSNotificationCenter = 1 << 7,
JJExceptionGuardNSStringContainer = 1 << 8,
JJExceptionGuardAllExceptZombie = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
};
/**
Exception interface
*/
@protocol JJExceptionHandle<NSObject>
/**
Crash message and extra info from current thread
@param exceptionMessage crash message
@param info extraInfo,key and value
*/
- (void)handleCrashException:(NSString*)exceptionMessage extraInfo:(nullable NSDictionary*)info;
@optional
/**
Crash message,exceptionCategory, extra info from current thread
@param exceptionMessage crash message
@param exceptionCategory JJExceptionGuardCategory
@param info extra info
*/
- (void)handleCrashException:(NSString*)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(nullable NSDictionary*)info;
@end
/**
Exception main
*/
@interface JJException : NSObject
/**
If exceptionWhenTerminate YES,the exception will stop application
If exceptionWhenTerminate NO,the exception only show log on the console, will not stop the application
Default value:NO
*/
@property(class,nonatomic,readwrite,assign)BOOL exceptionWhenTerminate;
/**
JJException guard exception status,default is NO
*/
@property(class,nonatomic,readonly,assign)BOOL isGuardException;
/**
Config the guard exception category,default:JJExceptionGuardNone
@param exceptionGuardCategory JJExceptionGuardCategory
*/
+ (void)configExceptionCategory:(JJExceptionGuardCategory)exceptionGuardCategory;
/**
Start the exception protect
*/
+ (void)startGuardException;
/**
Stop the exception protect
* Why deprecated this method:
* https://github.com/jezzmemo/JJException/issues/54
*/
+ (void)stopGuardException __attribute__((deprecated("Stop invoke this method,If invoke this,Maybe occur the infinite loop and then CRASH")));
/**
Register exception interface
@param exceptionHandle JJExceptionHandle
*/
+ (void)registerExceptionHandle:(id<JJExceptionHandle>)exceptionHandle;
/**
Only handle the black list zombie object
Sample Code:
[JJException addZombieObjectArray:@[TestZombie.class]];
@param objects Class Array
*/
+ (void)addZombieObjectArray:(NSArray*)objects;
@end
NS_ASSUME_NONNULL_END
//
// JJException.m
// JJException
//
// Created by Jezz on 2018/7/21.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "JJException.h"
#import "JJExceptionProxy.h"
@implementation JJException
+ (BOOL)isGuardException {
return [JJExceptionProxy shareExceptionProxy].isProtectException;
}
+ (BOOL)exceptionWhenTerminate{
return [JJExceptionProxy shareExceptionProxy].exceptionWhenTerminate;
}
+ (void)setExceptionWhenTerminate:(BOOL)exceptionWhenTerminate{
[JJExceptionProxy shareExceptionProxy].exceptionWhenTerminate = exceptionWhenTerminate;
}
+ (void)startGuardException{
[JJExceptionProxy shareExceptionProxy].isProtectException = YES;
}
+ (void)stopGuardException{
[JJExceptionProxy shareExceptionProxy].isProtectException = NO;
}
+ (void)configExceptionCategory:(JJExceptionGuardCategory)exceptionGuardCategory{
[JJExceptionProxy shareExceptionProxy].exceptionGuardCategory = exceptionGuardCategory;
}
+ (void)registerExceptionHandle:(id<JJExceptionHandle>)exceptionHandle{
[JJExceptionProxy shareExceptionProxy].delegate = exceptionHandle;
}
+ (void)addZombieObjectArray:(NSArray*)objects{
[[JJExceptionProxy shareExceptionProxy] addZombieObjectArray:objects];
}
@end
//
// JJExceptionMacros.h
// JJException
//
// Created by Kealdish on 2019/2/26.
// Copyright © 2019 Jezz. All rights reserved.
//
#ifndef JJExceptionMacros_h
#define JJExceptionMacros_h
/**
Add this macro before each category implementation, so we don't have to use
-all_load or -force_load to load object files from static libraries that only
contain categories and no classes.
*******************************************************************************
Example:
JJSYNTH_DUMMY_CLASS(NSObject_DeallocBlock)
*/
#ifndef JJSYNTH_DUMMY_CLASS
#define JJSYNTH_DUMMY_CLASS(_name_) \
@interface JJSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation JJSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif
#endif /* JJExceptionMacros_h */
//
// JJExceptionProxy.h
// JJException
//
// Created by Jezz on 2018/7/22.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "JJException.h"
NS_ASSUME_NONNULL_BEGIN
/**
C style invoke handle crash message
@param exceptionMessage crash message
*/
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage);
/**
C style invoke handle crash message,and extra crash info
@param exceptionMessage crash message
@param extraInfo extra crash message
*/
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage,NSDictionary* extraInfo);
/**
C style invoke handle crash message,and extra crash info
@param exceptionCategory crash type
@param exceptionMessage crash message
@param extraInfo extra info
*/
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage,NSDictionary* extraInfo);
/**
C style invoke handle crash type,and exception message
@param exceptionCategory JJExceptionGuardCategory
@param exceptionMessage crash message
*/
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage);
/**
Exception Proxy
*/
@interface JJExceptionProxy : NSObject<JJExceptionHandle>
+ (instancetype)shareExceptionProxy;
#pragma mark - Handle crash interface
/**
Hold the JJExceptionHandle interface object
*/
@property(nonatomic,readwrite,weak)id<JJExceptionHandle> delegate;
/**
Setting hook excpetion status,default value is NO
*/
@property(nonatomic,readwrite,assign)BOOL isProtectException;
/**
If exceptionWhenTerminate YES,the exception will stop application
If exceptionWhenTerminate NO,the exception only show log on the console, will not stop the application
Default value:NO
*/
@property(nonatomic,readwrite,assign)BOOL exceptionWhenTerminate;
/**
Setting exceptionGuardCategory
@see JJExceptionGuardCategory
*/
@property(nonatomic,readwrite,assign)JJExceptionGuardCategory exceptionGuardCategory;
#pragma mark - Zombie collection
/**
Real addZombieObjectArray invoke
@param objects class array
*/
- (void)addZombieObjectArray:(NSArray*)objects;
/**
Zombie only process the Set class
*/
@property(nonatomic,readonly,strong)NSSet* blackClassesSet;
/**
Record the all Set class size
*/
@property(nonatomic,readonly,assign)NSInteger currentClassSize;
/**
Add object to the currentClassesSet
@param object NSObject
*/
- (void)addCurrentZombieClass:(Class)object;
/**
Remove object from the currentClassesSet
@param object NSObject
*/
- (void)removeCurrentZombieClass:(Class)object;
/**
Record the objc_destructInstance instance object
*/
@property(nonatomic,readonly,strong)NSSet* currentClassesSet;
/**
Random get the object from blackClassesSet
@return NSObject
*/
- (nullable id)objectFromCurrentClassesSet;
@end
NS_ASSUME_NONNULL_END
//
// JJExceptionProxy.m
// JJException
//
// Created by Jezz on 2018/7/22.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "JJExceptionProxy.h"
#import <mach-o/dyld.h>
#import <objc/runtime.h>
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage extraInfo:@{}];
}
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage,NSDictionary* extraInfo){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage extraInfo:extraInfo];
}
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage,NSDictionary* extraInfo){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage exceptionCategory:exceptionCategory extraInfo:extraInfo];
}
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage exceptionCategory:exceptionCategory extraInfo:nil];
}
/**
Get application base address,the application different base address after started
@return base address
*/
uintptr_t get_load_address(void) {
const struct mach_header *exe_header = NULL;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
exe_header = header;
break;
}
}
return (uintptr_t)exe_header;
}
/**
Address Offset
@return slide address
*/
uintptr_t get_slide_address(void) {
uintptr_t vmaddr_slide = 0;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
break;
}
}
return (uintptr_t)vmaddr_slide;
}
@interface JJExceptionProxy(){
NSMutableSet* _currentClassesSet;
NSMutableSet* _blackClassesSet;
NSInteger _currentClassSize;
dispatch_semaphore_t _classArrayLock;//Protect _blackClassesSet and _currentClassesSet atomic
dispatch_semaphore_t _swizzleLock;//Protect swizzle atomic
}
@end
@implementation JJExceptionProxy
+(instancetype)shareExceptionProxy{
static dispatch_once_t onceToken;
static id exceptionProxy;
dispatch_once(&onceToken, ^{
exceptionProxy = [[self alloc] init];
});
return exceptionProxy;
}
- (instancetype)init{
self = [super init];
if (self) {
_blackClassesSet = [NSMutableSet new];
_currentClassesSet = [NSMutableSet new];
_currentClassSize = 0;
_classArrayLock = dispatch_semaphore_create(1);
_swizzleLock = dispatch_semaphore_create(1);
}
return self;
}
- (void)handleCrashException:(NSString *)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(NSDictionary *)info{
if (!exceptionMessage) {
return;
}
NSArray* callStack = [NSThread callStackSymbols];
NSString* callStackString = [NSString stringWithFormat:@"%@",callStack];
uintptr_t loadAddress = get_load_address();
uintptr_t slideAddress = get_slide_address();
NSString* exceptionResult = [NSString stringWithFormat:@"%ld\n%ld\n%@\n%@",loadAddress,slideAddress,exceptionMessage,callStackString];
if ([self.delegate respondsToSelector:@selector(handleCrashException:extraInfo:)]){
[self.delegate handleCrashException:exceptionResult extraInfo:info];
}
if ([self.delegate respondsToSelector:@selector(handleCrashException:exceptionCategory:extraInfo:)]) {
[self.delegate handleCrashException:exceptionResult exceptionCategory:exceptionCategory extraInfo:info];
}
#ifdef DEBUG
NSLog(@"================================JJException Start==================================");
NSLog(@"JJException Type:%ld",(long)exceptionCategory);
NSLog(@"JJException Description:%@",exceptionMessage);
NSLog(@"JJException Extra info:%@",info);
NSLog(@"JJException CallStack:%@",callStack);
NSLog(@"================================JJException End====================================");
if (self.exceptionWhenTerminate) {
NSAssert(NO, @"");
}
#endif
}
- (void)handleCrashException:(NSString *)exceptionMessage extraInfo:(nullable NSDictionary *)info{
[self handleCrashException:exceptionMessage exceptionCategory:JJExceptionGuardNone extraInfo:info];
}
- (void)setIsProtectException:(BOOL)isProtectException{
dispatch_semaphore_wait(_swizzleLock, DISPATCH_TIME_FOREVER);
if (_isProtectException != isProtectException) {
_isProtectException = isProtectException;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if(self.exceptionGuardCategory & JJExceptionGuardArrayContainer){
[NSArray performSelector:@selector(jj_swizzleNSArray)];
[NSMutableArray performSelector:@selector(jj_swizzleNSMutableArray)];
[NSSet performSelector:@selector(jj_swizzleNSSet)];
[NSMutableSet performSelector:@selector(jj_swizzleNSMutableSet)];
}
if(self.exceptionGuardCategory & JJExceptionGuardDictionaryContainer){
[NSDictionary performSelector:@selector(jj_swizzleNSDictionary)];
[NSMutableDictionary performSelector:@selector(jj_swizzleNSMutableDictionary)];
}
if(self.exceptionGuardCategory & JJExceptionGuardUnrecognizedSelector){
[NSObject performSelector:@selector(jj_swizzleUnrecognizedSelector)];
}
if (self.exceptionGuardCategory & JJExceptionGuardZombie) {
[NSObject performSelector:@selector(jj_swizzleZombie)];
}
if (self.exceptionGuardCategory & JJExceptionGuardKVOCrash) {
[NSObject performSelector:@selector(jj_swizzleKVOCrash)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSTimer) {
[NSTimer performSelector:@selector(jj_swizzleNSTimer)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSNotificationCenter) {
[NSNotificationCenter performSelector:@selector(jj_swizzleNSNotificationCenter)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSStringContainer) {
[NSString performSelector:@selector(jj_swizzleNSString)];
[NSMutableString performSelector:@selector(jj_swizzleNSMutableString)];
[NSAttributedString performSelector:@selector(jj_swizzleNSAttributedString)];
[NSMutableAttributedString performSelector:@selector(jj_swizzleNSMutableAttributedString)];
}
#pragma clang diagnostic pop
}
dispatch_semaphore_signal(_swizzleLock);
}
- (void)setExceptionGuardCategory:(JJExceptionGuardCategory)exceptionGuardCategory{
if (_exceptionGuardCategory != exceptionGuardCategory) {
_exceptionGuardCategory = exceptionGuardCategory;
}
}
- (void)addZombieObjectArray:(NSArray*)objects{
if (!objects) {
return;
}
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
[_blackClassesSet addObjectsFromArray:objects];
dispatch_semaphore_signal(_classArrayLock);
}
- (NSSet*)blackClassesSet{
return _blackClassesSet;
}
- (void)addCurrentZombieClass:(Class)object{
if (object) {
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
_currentClassSize = _currentClassSize + class_getInstanceSize(object);
[_currentClassesSet addObject:object];
dispatch_semaphore_signal(_classArrayLock);
}
}
- (void)removeCurrentZombieClass:(Class)object{
if (object) {
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
_currentClassSize = _currentClassSize - class_getInstanceSize(object);
[_currentClassesSet removeObject:object];
dispatch_semaphore_signal(_classArrayLock);
}
}
- (NSSet*)currentClassesSet{
return _currentClassesSet;
}
- (NSInteger)currentClassSize{
return _currentClassSize;
}
- (nullable id)objectFromCurrentClassesSet{
NSEnumerator* objectEnum = [_currentClassesSet objectEnumerator];
for (id object in objectEnum) {
return object;
}
return nil;
}
@end
//
// NSObject+SwizzleHook.h
// JJException
//
// Created by Jezz on 2018/7/10.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
/*
* JJSwizzledIMPBlock assist variable
*/
typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
@interface JJSwizzleObject : NSObject
- (JJSwizzleOriginalIMP)getOriginalImplementation;
@property (nonatomic,readonly,assign) SEL selector;
@end
typedef id (^JJSwizzledIMPBlock)(JJSwizzleObject* swizzleInfo);
/*
* JJSwizzledIMPBlock assist variable
*/
/**
* Swizzle Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Swizzle Instance Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Only swizzle the current class,not swizzle all class
* perform jj_cleanKVO selector before the origin dealloc
@param class Class
*/
void jj_swizzleDeallocIfNeeded(Class class);
/**
Swizzle the NSObject Extension
*/
@interface NSObject (SwizzleHook)
/**
Swizzle Class Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle Instance Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle instance method to the block target
@param originSelector originSelector
@param swizzledBlock block
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock;
@end
//
// NSObject+SwizzleHook.m
// JJException
//
// Created by Jezz on 2018/7/10.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <libkern/OSAtomic.h>
typedef IMP (^JJSWizzleImpProvider)(void);
static const char jjSwizzledDeallocKey;
@interface JJSwizzleObject()
@property (nonatomic,readwrite,copy) JJSWizzleImpProvider impProviderBlock;
@property (nonatomic,readwrite,assign) SEL selector;
@end
@implementation JJSwizzleObject
- (JJSwizzleOriginalIMP)getOriginalImplementation{
NSAssert(_impProviderBlock,nil);
return (JJSwizzleOriginalIMP)_impProviderBlock();
}
@end
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector){
if (!cls) {
return;
}
Method originalMethod = class_getClassMethod(cls, originSelector);
Method swizzledMethod = class_getClassMethod(cls, swizzleSelector);
Class metacls = objc_getMetaClass(NSStringFromClass(cls).UTF8String);
if (class_addMethod(metacls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)) ) {
/* swizzing super class method, added if not exist */
class_replaceMethod(metacls,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
/* swizzleMethod maybe belong to super */
class_replaceMethod(metacls,
swizzleSelector,
class_replaceMethod(metacls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)),
method_getTypeEncoding(originalMethod));
}
}
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
if (!cls) {
return;
}
/* if current class not exist selector, then get super*/
Method originalMethod = class_getInstanceMethod(cls, originSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzleSelector);
/* add selector if not exist, implement append with method */
if (class_addMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)) ) {
/* replace class instance method, added if selector not exist */
/* for class cluster , it always add new selector here */
class_replaceMethod(cls,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
/* swizzleMethod maybe belong to super */
class_replaceMethod(cls,
swizzleSelector,
class_replaceMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)),
method_getTypeEncoding(originalMethod));
}
}
// a class doesn't need dealloc swizzled if it or a superclass has been swizzled already
BOOL jj_requiresDeallocSwizzle(Class class)
{
BOOL swizzled = NO;
for ( Class currentClass = class; !swizzled && currentClass != nil; currentClass = class_getSuperclass(currentClass) ) {
swizzled = [objc_getAssociatedObject(currentClass, &jjSwizzledDeallocKey) boolValue];
}
return !swizzled;
}
void jj_swizzleDeallocIfNeeded(Class class)
{
static SEL deallocSEL = NULL;
static SEL cleanupSEL = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
deallocSEL = sel_getUid("dealloc");
cleanupSEL = sel_getUid("jj_cleanKVO");
});
@synchronized (class) {
if ( !jj_requiresDeallocSwizzle(class) ) {
return;
}
objc_setAssociatedObject(class, &jjSwizzledDeallocKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Method dealloc = class_getInstanceMethod(class, deallocSEL);
if ( dealloc == NULL ) {
Class superclass = class_getSuperclass(class);
class_addMethod(class, deallocSEL, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
struct objc_super superStruct = (struct objc_super){ self, superclass };
((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&superStruct, deallocSEL);
}), method_getTypeEncoding(dealloc));
}else{
__block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
((void(*)(id, SEL))deallocIMP)(self, deallocSEL);
}));
}
}
@implementation NSObject (SwizzleHook)
void __JJ_SWIZZLE_BLOCK(Class classToSwizzle,SEL selector,JJSwizzledIMPBlock impBlock){
Method method = class_getInstanceMethod(classToSwizzle, selector);
__block IMP originalIMP = NULL;
JJSWizzleImpProvider originalImpProvider = ^IMP{
IMP imp = originalIMP;
if (NULL == imp){
Class superclass = class_getSuperclass(classToSwizzle);
imp = method_getImplementation(class_getInstanceMethod(superclass,selector));
}
return imp;
};
JJSwizzleObject* swizzleInfo = [JJSwizzleObject new];
swizzleInfo.selector = selector;
swizzleInfo.impProviderBlock = originalImpProvider;
id newIMPBlock = impBlock(swizzleInfo);
const char* methodType = method_getTypeEncoding(method);
IMP newIMP = imp_implementationWithBlock(newIMPBlock);
originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType);
}
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector{
swizzleClassMethod(self.class, originSelector, swizzleSelector);
}
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector{
swizzleInstanceMethod(self.class, originSelector, swizzleSelector);
}
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock{
__JJ_SWIZZLE_BLOCK(self.class, originSelector, swizzledBlock);
}
@end
MIT License
Copyright (c) 2018 jezz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/JJException.svg)](https://img.shields.io/cocoapods/v/JJException.svg)
[![Build Status](https://travis-ci.org/jezzmemo/JJException.svg?branch=master)](https://travis-ci.org/jezzmemo/JJException.svg?branch=master)
[![codecov](https://codecov.io/gh/jezzmemo/JJException/branch/master/graph/badge.svg)](https://codecov.io/gh/jezzmemo/JJException)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/JJException.svg?style=flat)](http://cocoadocs.org/docsets/JJException)
![License MIT](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)
# JJException
Common problems will not crash by the JJException,Hook the Unrecognized Selector,Out of bound,Paramter is nil,etc.Throw the exception to the interface,And then save the exception record to the log,Upgrad the app or Hot-Fix to resolve the exception.
保护App,一般常见的问题不会导致闪退,增强App的健壮性,同时会将错误抛出来,根据每个App自身的日志渠道记录,下次迭代或者热修复以下问题.
- [x] Unrecognized Selector Sent to Instance(方法不存在异常)
- [x] NSNull(方法不存在异常)
- [x] NSArray,NSMutableArray,NSDictonary,NSMutableDictionary(数组越界,key-value参数异常)
- [x] KVO(忘记移除keypath导致闪退)
- [x] Zombie Pointer(野指针)
- [x] NSTimer(忘记移除导致内存泄漏)
- [x] NSNotification(忘记移除导致异常)
- [x] NSString,NSMutableString,NSAttributedString,NSMutableAttributedString(下标越界以及参数nil异常)
## 如何安装
__Requirements__
* iOS 8.0+
* OSX 10.7+
* Xcode 8.0+
__Podfile__
```
pod 'JJException'
```
__Cartfile__
```
github "jezzmemo/JJException"
```
__手动导入代码__
导入`Source`文件夹里所有文件,需要将`MRC`目录下所有.m文件,编译选项更改成-fno-objc-arc
## 如何使用
* 所有异常的分类,根据自身需要,自由组合,__如果没用到Zombie功能,建议使用JJExceptionGuardAllExceptZombie__
```objc
typedef NS_OPTIONS(NSInteger,JJExceptionGuardCategory){
JJExceptionGuardNone = 0,
JJExceptionGuardUnrecognizedSelector = 1 << 1,
JJExceptionGuardDictionaryContainer = 1 << 2,
JJExceptionGuardArrayContainer = 1 << 3,
JJExceptionGuardZombie = 1 << 4,
JJExceptionGuardKVOCrash = 1 << 5,
JJExceptionGuardNSTimer = 1 << 6,
JJExceptionGuardNSNotificationCenter = 1 << 7,
JJExceptionGuardNSStringContainer = 1 << 8,
JJExceptionGuardAllExceptZombie = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
};
```
* 设置异常类型并开启,__建议放在`didFinishLaunchingWithOptions`第一行,以免在多线程出现异常的情况__
```objc
[JJException configExceptionCategory:JJExceptionGuardAll];
[JJException startGuardException];
```
* 实时关闭保护
```objc
[JJException stopGuardException];
```
* 当异常时,默认程序不会中断,如果需要遇到异常时退出,需要如下设置:
```objc
//Default value:NO
JJException.exceptionWhenTerminate = YES;
```
* Zombie使用黑名单机制,只有加入这个名单的才有作用,示例如下:
```objc
[JJException addZombieObjectArray:@[TestZombie.class]];
```
* 如果需要记录日志,只需要实现`JJExceptionHandle`协议,并注册:
```objc
@interface ViewController ()<JJExceptionHandle>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[JJException registerExceptionHandle:self];
}
- (void)handleCrashException:(NSString*)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(nullable NSDictionary*)info{
}
```
## FAQ
> 是否影响上线App Store
不会的,JJException的功能都是使用的官方API,没有任何私有API
> 保护App的实现技术原理是什么?
[JJException技术原理](https://github.com/jezzmemo/JJException/blob/master/JJExceptionPrinciple.md)
> JJException是否和Bugly和友盟等第三方库是否有冲突?
Bugly和友盟是记录Crash Bug的log还有一些统计功能,JJException主要是通过Hook技术来实现,所以不会和JJException冲突
> 如何上传异常信息到Bugly?
Bugly可以帮我们解决重复信息和CallStack信息,以及状态维护。
实现JJExceptionHandle协议后,将异常信息组织成Error,然后用[Bugly reportError:error]上传异常信息,上传后异常信息Bugly的后台`错误分析`菜单里
> Swift是否有作用
是有作用的,Swift有些API实现是独立实现的,比如String,Array,用结构体的方式,但是有些还是沿用了Objective-c,凡是沿用Objective-c的特性的,JJException还是生效的,下面我来列下还依然生效的功能点:
* Unrecognized Selector Sent to Instance
* NSNull
* KVO
* NSNotification
* NSString,NSMutableString,NSAttributedString,NSMutableAttributedString(__注意不是String__)
* NSArray,NSMutableArray,NSDictonary,NSMutableDictionary(__注意不是Array__)
* Zombie Pointer
这里贴下Swift的初始化代码示例:
```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.registerJJException()
return true
}
func registerJJException(){
JJException.configExceptionCategory(.allExceptZombie)
JJException.startGuard()
JJException.register(self);
}
func handleCrashException(_ exceptionMessage: String, extraInfo info: [AnyHashable : Any]?) {
}
```
> JJException Hook那些API?
[HookAPI](https://github.com/jezzmemo/JJException/blob/master/JJExceptionHookAPI.md)
## TODO(大家记得给我星哦)
* 国际化JJException
## License
JJException is released under the MIT license. See LICENSE for details.
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9C67FC40C7E15E74BE99DE8F3435F9D9"
BuildableName = "libJJException.a"
BlueprintName = "JJException"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
#import <Foundation/Foundation.h>
@interface PodsDummy_JJException : NSObject
@end
@implementation PodsDummy_JJException
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JJException
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/JJException" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/JJException"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/JJException
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment