IOS安装CocoaPods详情过程https://www.tuicool.com/articles/3yAJR3i
iOS Git的使用: https://www.jianshu.com/p/1134abfb40c1
pod 'JXCategoryView'
iOS开发iOS14适配https://www.tuicool.com/articles/BRJBvuR
如何申请接入微信APP支付:https://www.jianshu.com/p/20d33602877c
iOS接入微信支付:https://www.jianshu.com/p/49c7515e96e4
myGitHub:2016llf
Aspect Ratio
Aspect Ratio:设置视图的宽高比
视图宽度随着屏幕宽度变化拉伸时,让其高度自动进行等比例拉伸.保持该视图宽高比不变.
一般先确定宽度(6的尺寸),再设置宽高比利,
Device RGB
iOS色彩空间——xib和代码设置颜色的偏差问题在使用可视化编程时,
在XIB或SB中直接设置颜色,选择RGB Slider设置颜色时,Xcode 中默认勾选的是Generic RGB。
解决方式:选择Device RGB,
//强与弱
__weak typeof(self) weakSelf = self;
[self doSomeBlockJob:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
...
}
}];
按钮细节问题
[_cancelBtn setTitleColor:UIColorHex(00ACEB) forState:(UIControlStateNormal)];
[_cancelBtn setTitleColor:UIColorHexAndAlpha(00ACEB, 0.5) forState:(UIControlStateHighlighted)];
[_cancelBtn setBackgroundImage:[BYDImageManager imageWithColor:kColorHighlight] forState:UIControlStateHighlighted];
UIViewController
//UIViewController细节:
self.navigationItem.title = @"XXXX"
等比例缩放图片
//img:为传入的图片; size:为放置图片区域的大小; scaledImage:为返回压缩后的图片
+ (UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size{
// 创建一个bitmap的context
// 并把它设置成为当前正在使用的context
UIGraphicsBeginImageContext(size);
// 绘制改变大小的图片
[img drawInRect:CGRectMake(0,0, size.width, size.height)];
// 从当前context中创建一个改变大小后的图片
UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
// 使当前的context出堆栈
UIGraphicsEndImageContext();
//返回新的改变大小后的图片
return scaledImage;
}
内存优化
1,图片的读取采用正确的方式
imageNamed: 会缓存图片,用它读取频繁用到的小图片,最恶心的是不知道它到底什么时候释放。
imageWithContentsOfFile: 不缓存图片,适合一次性读取的大图片。
NSString *path = [[NSBuddle mainBuddle] pathForResource:@"resourceName" oftype@"resourceType"];
UIImage *image = [[UIImage imageWithContentsOfFile:path];
2,imageWithContentsOfFile 、 Assets.xcassets
对于大的图片且偶尔需要显示的应放到工程目录下,不要放到 Assets.xcassets 中;
并使用 imageWithContentsOfFile 加载不让系统缓存
对于经常需要展示的小图片放到 Assets.xcassets 中让系统缓存,使用 imageNamed 加载
一张图片 7kb,在5个地方会用到;resource会占用35kb内存,imageAssets占用7kb
一张图片300kb,在一个地方会用到;resource会在使用后会清除300kb内存,imageAssets会一直占用300kb内存
///
这里顺便提一下 imageNamed: 和 imageWithContentsOfFile: 的区别,这两个 API 都需要解码,并且工作流程都是一致的。
不过imageNamed:会做缓存处理,在下一次用到相同的资源时,就会从缓存里面读取。而 imageWithContentsOfFile: 则不会。
其实,大多数情况下,都是直接使用[UIImage imageNamed:]这种加载本地图片,既然是使用这种方法,
那么图片放在Images.xcassets里面和放在单独文件夹里面区别不是很大,
最大的区别就是Images.xcassets在打包的时候会减小包的体积。
数据库问题
1 数据库频繁操作打开关闭,手动打开关闭锁, 使用一个单例类操作数据库
2 避免同时操作一个资源使用队列串行方式
Appearance方法
我们通过UIAppearance设置一些UI的全局效果,这样就可以很方便的实现UI的自定义效果
又能最简单的实现统一界面风格,但是需要注意使用细则:
**
1、控件遵守了UIAppearance协议,才能对控件进行appearance设置
2、只有被UI_APPEARANCE_SELECTOR这个宏修饰的属性才能使用appearance进行设置,其他属性则不具备该功能
3、appearance设置需要在该控制显示之前设置完成,否则可能无效
**
if (@available(iOS 11.0, *)){
[[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
}
if (@available(iOS 15.0, *)) {
[UITableView appearance].sectionHeaderTopPadding = 0;
}
// [[UITableView appearance] setEstimatedRowHeight:0];
[[UITableView appearance] setEstimatedSectionFooterHeight:0];
[[UITableView appearance] setEstimatedSectionHeaderHeight:0];
NSUserDefaults standardUserDefaults中在最后使用synchronize的作用
系统会保存到该应用下的/Library/Preferences/gongcheng.plist文件中。需要注意的是如果程序意外退出,
NSUserDefaultsstandardUserDefaults数据不会被系统写入到该文件,所以,
要使用[[NSUserDefaultsstandardUserDefaults] synchronize]命令直接同步到文件里,来避免数据的丢失。
///小插曲
- (void)test {
NSDictionary *dict = @{@1: @"甲",
@2: @"乙",
@3: @"丙",
@4: @"丁"};
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:@"testKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
///报错
2018-04-16 10:55:04.573393+0800 text[1344:58370] [User Defaults] Attempt to set a non-property-list object {
3 = "\U4e19";
2 = "\U4e59";
1 = "\U7532";
4 = "\U4e01";
} as an NSUserDefaults/CFPreferences value for key testKey
///why
And although NSDictionary and CFDictionary objects allow their keys to be objects of any type,
if the keys are not string objects, the collections are not property-list objects.
虽然 NSDictionary 和 CFDictionary 对象的 Key 可以为任何类型(只要遵循 NSCopying 协议即可),
但是如果当 Key 不为字符串 string 对象时,此时这个字典对象就不能算是 property list objects 了,
捕捉异常
@try
{
///关键代码
NSString *str = @"abc";
[str substringFromIndex:111]; // 程序到这里会崩
}
@catch (NSException *exception)
{
///捕捉异常(弹框提示报错了)
NSLog(@"%s\n%@", __FUNCTION__, exception);
NSLog(@"%@", exception.name);
NSLog(@"%@", exception.reason);
}
@finally
{
///资源回收完毕!
NSLog(@"我一定会执行");
}
多用字面量语法,少用等价方法
// 字面量字符串
NSString *str = @"QiShare";
// 字面量数值
NSNumber *num = @(1);
NSNumber *floatNum = @(1.0);
int x = 5;
float y = 3.14;
NSNumber *num = @(x * y);
// 字面量数组
NSArray *animals = @[@"cat", @"dog", @"tiger", @"monkey"];
NSString *cat = animals[0];
// 字面量字典
NSDictionary *qiShareDic = @{@"englishName": @"QiShare",
@"chineseName": @"奇分享"}];
NSString *englishName = qiShareDic[@"englishName"];
NSString *chineseName = qiShareDic[@"chineseName"];
//@property()定义一个成员变量时:1:生成一个带有下滑线的成员变量,2:生成成员变量的set和get方法
trong常使用在可变类型:NSMutableArray、NSMutableDictionary、NSMutableString
copy常使用在不可以变类型:NSArray、NSDictionary、NSString
assign常使用在基本类型:NSInteger、NSUInteger,CGFloat,NSTimeInterval
weak常使用在delegate等防止循环引用的场景
NSString应该使用copy与strong关键字
NSMutableString 不要用 copy https://www.jianshu.com/p/5d138efee024
这里当我们用copy修饰可变类型(NSMutableString、NSMutableArray、NSMutableSet)时,竟然崩溃了。因为当用copy修饰后,NSMutaleArray由可变类型变成了不可变类型,而NSArray并没有增、删元素的方法,所以崩溃。因此,可变类型(NSMutableString、NSMutableArray、NSMutableSet),要用strong修饰。
@property (nonatomic, strong) NSString *str1;
@property (nonatomic, copy) NSString *str2;
NSMutableString *mtStr = [NSMutableString stringWithFormat:@"hello"];
self.str1 = mtStr;
self.str2 = mtStr;
[mtStr appendString:@"world"];
NSLog(@"mtStr:%p,, str1:%p,, str2:%p",mtStr, self.str1, self.str2);
// mtStr:0x281641710,, str1:0x281641710,, str2:0xb5284f85e15a82cb
// mtStr,str1指向同一个对象
// 而mtStr,str2对象地址不同,是两个不同的对象,所以copy是深复制,创建了一个新的对象。
NSLog(@"str1-------%@",self.str1);
// str1-------helloworld
// 经过strong修饰过的str1却随mtStr的改变而改变。
NSLog(@"str2-------%@",self.str2);
// str2-------hello
// 经过copy关键字修饰过的str2并没有因为mtStr的变化而变化,
// 如果不希望字串的值跟着赋值的字串的值变化,所以我们一般用copy.
// 如果希望字串的值跟着赋值的字串的值变化,可以使用strong。
//上面的情况是针对于当把NSMutableString赋值给NSString的时候,才会有不同,如果是赋值是NSString对象,那么使用copy还是strong,结果都是一样的,因为NSString对象根本就不能改变自身的值,他是不可变的。
//把一个对象赋值给一个属性变量,当这个对象变化了,如果希望属性变量变化就使用strong属性,如果希望属性变量不跟着变化,就是用copy属性。
static:声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。
//两个概念:生命周期、作用域
//static修饰全局变量,拥有局部变量修饰时候的属性,不同的是该变量只能被本文件访问使用。
static int age = 20;
//
- (void)staticTest{
// static修饰局部变量,它的生命周期、作用域都是在这个代码块内。
//1,使得局部变量只初始化一次
//2,局部变量在程序中只有一份内存,使这个变量拥有记忆功能(可以被赋值).
//3,局部变量的作用域不变,但是生命周期变了(直到程序结束才销毁)
static int age = 0;
age++;
//所以此处输入的是局部变量的值,在其他部分调用全局age还是20。
NSLog(@"局部变量的age:%d",age);
}
const:修饰变量,const修饰的变量值是不可变的,(主要强调变量是不可修改的)
const修饰的是其右边的值,也就是const右边的这个整体的值不能改变。
//staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量。
static NSString * const key = @"name";
iOS - Xcode Slicing图片拉伸的图文详解
这里主要讲下Slicing里三条线条的意义和作用
也就是当图片拉伸时,用区域A的图片元素去不断复制填充区域B的空间,区域B是被白遮罩层盖的,这区域的图片部分是不可见的,是用于区域A填充的空间。
线1和线2的区间为区域A
线2和线3之间的白遮罩区间为区域B
1,先设置 slicing 图片拉伸
2,设置UIImageView的contentMode属性为Redraw或者scale To Fill
字符串问题
//减少硬编码
static NSString * const newPushGotoMessageTips = @"前往消息中心";
#define heightItem kAutoSize(64)
//字符串转数组
NSString *str1 = @"张三, 李四, 王五, 祥子";
NSArray *array1 = [str1 componentsSeparatedByString:@","];
NSLog(@"%@", array1);
(张三, 李四, 王五,祥子,)
//数组转字符串
NSArray *array2= @[@"哈哈", @"呵呵", @"你好", @"简书"];
NSString *str2 = [array2 componentsJoinedByString:@","];
NSLog(@"%@", str2);
哈哈,呵呵,你好,简书
//检查字符串是否为空 或 NSNumber 并替换
+ (NSString *)checkEmpty:(id)value defaultString:(NSString *)defaultString {
if ([value isKindOfClass:[NSString class]] && ((NSString *)value).length) {
return value;
}
if ([value isKindOfClass:[NSNumber class]]) {
return [value stringValue];
}
if ([defaultString isKindOfClass:[NSString class]]) {
return defaultString;
}
return @"";
}
//检查字符串是否为空
+ (BOOL)checkEmptyString:(NSString *)string {
if ([string isKindOfClass:[NSString class]] && string.length) {
return NO;
}
return YES;
}
//检查数组是否为空
+ (BOOL)checkEmptyArray:(NSArray *)array {
if ([array isKindOfClass:[NSArray class]] && array.count) {
return NO;
}
return YES;
}
//检查字典是否为空
+ (BOOL)checkEmptyDic:(NSDictionary *)dic {
if ([dic isKindOfClass:[NSDictionary class]] && dic.allValues.count) {
return NO;
}
return YES;
}
字符串根据 , 转数组
if ([model.attr_name length] > 2) {
NSArray *arrayStr = [model.attr_name componentsSeparatedByString:@","];
NSMutableString *attriStr = [NSMutableString string];
for (NSString * str in arrayStr) {
[attriStr appendString:NSStringFromFormat(@"%@\n", str)];
}
[attriStr deleteCharactersInRange:NSMakeRange([attriStr length] - 1, 1)];
self.subTitleLabel.text = attriStr;
}
UITextField
//限制长度
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textfieldEditChange) name:UITextFieldTextDidChangeNotification object:nil];
- (void)textfieldEditChange{
if (self.phoneTextFiled.text.length > 11) {
self.phoneTextFiled.text = [self.phoneTextFiled.text substringToIndex:11];
}
}
///去掉前后空格
NSString *searchText = textField.text;
searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
UIKeyboard
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
#pragma mark - keyboardWillShow
- (void)keyboardWillShow:(NSNotification *)notification {
CGFloat kbHeight = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
CGFloat offset = kbHeight;
double duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
//将视图上移计算好的偏移
if(offset > 0) {
[UIView animateWithDuration:duration animations:^{
_contenView.top = kScreenHeight - kAutoSize(323) - offset/2;
}];
}
}
#pragma mark - keyboardWillHide
- (void)keyboardWillHide:(NSNotification *)notify {
double duration = [[notify.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^{
_contenView.top = kScreenHeight - kAutoSize(323);
}];
}
Masonry
//优先级约束
[self.numberLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.priceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
//大于等于10
make.bottom.lessThanOrEqualTo(self).mas_offset(@-10);
//宽高比利1:1
make.width.mas_equalTo(self.leftImageView.mas_height).multipliedBy(1);
//底部安全距离
if (@available(ios 11.0,*)) {
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
}else{
make.bottom.equalTo(@0);
}
///
mas_makeConstraints:
一个视图,刚创建出来,没有任何约束。后期也不会重置相关的约束用这个方法。
已有的约束,再添加约束,不修改原有约束
添加新的约束, 则视图会添加这个约束
不会删除已有约束
mas_updateConstraints:
一个视图,刚创建出来,没有任何约束或者已经有约束需要去更新相关约束或者加入约束,但后面不回去重制约束
已有的约束,再添加约束,则更新原有约束
添加新的约束, 则视图会添加这个约束
不会删除已有约束
mas_remakeConstraints
一个视图虽然已经有了约束,但是之前的约束都不要了,需要重新去更新约束。
已有的约束,再添加约束,则更新原有约束
添加新的约束, 则视图会添加这个约束
会删除已有的所有约束
Block
- (void)pushViewControllerSuccess:(UIViewController *)viewController
animated:(BOOL)animated
successHandle:(void (^)(void))success
faliedHandle:(void (^)(void))falied;
数组
//项目开发过程中可能会有这种需求,某个可变数组不断地增加元素,同时
//我们需要判断新的元素是否已经在数组里,如果不在才添加该元素,否则丢弃
//containsObject:是在比较内存地址,即使两个对象内容完全一样,地址不同,那也是不同的。
//我个人认为这个方法应该叫是否存在同一个对象
NSMutableArray *defaultArray = [NSMutableArray array];
if (![defaultArray containsObject:searchText]) {
[defaultArray insertObject:searchText atIndex:0];
}
//取前面10个
if (defaultArray.count > 10) {
[defaultArray subarrayWithRange:NSMakeRange(0, 10)]
}
网络数据处理
//pageSize == 10
if (isFirst) {
//show转圈圈
}
//成功
if (listArray.count < pageSize) {
dispatch_async(dispatch_get_main_queue(), ^(){
[weakSelf.collectionView.mj_footer endRefreshingWithNoMoreData];
});
}
if (weakSelf.dataPage == 1)
{
[weakSelf.qualityGoodsModeArray removeAllObjects];
(listArray.count == 0) ? (notDataView.hidden = NO) : (notDataView.hidden = YES);
}
else if (weakSelf.dataPage > 1 && listArray.count == 0)
{
weakSelf.dataPage--;
}
[weakSelf.dataArray addObjectsFromArray:listArray];
[weakSelf.collectionView.mj_footer endRefreshing];
weakSelf.collectionView.mj_footer.hidden = NO;
[weakSelf.collectionView reloadData];
//失败
weakSelf.dataPage--;
[weakSelf.collectionView.mj_footer endRefreshing];
if (weakSelf.dataArray.count > 0) {
weakSelf.collectionView.mj_footer.hidden = NO;
//show吐司(提示无网络,报错)
return;
}else{
weakSelf.collectionView.mj_footer.hidden = YES;
}
if (isFirst) {
if (error.isNetwork) {
[weakSelf.collectionView statusViewLoadFailed:^{
[weakSelf requestDatafirst:YES];
}];
} else {
[weakSelf.collectionView statusViewLoadError:^{
[weakSelf requestDatafirst:YES];
}];
}
}
多个请求网络无序
dispatch_group_enter :通知 group,下个任务要放入 group 中执行了
dispatch_group_leave: 通知 group,任务成功完成,要移除,与 enter成对出现
dispatch_group_wait: 在任务组完成时调用,或者任务组超时是调用
dispatch_group_notify: 只要任务全部完成了,就会在最后调用
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//成功
dispatch_group_leave(group);
//失败
dispatch_group_leave(group);
});
//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//成功
dispatch_group_leave(group);
//失败
dispatch_group_leave(group);
});
//
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//成功
dispatch_group_leave(group);
//失败
dispatch_group_leave(group);
});
//
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//TODO:
});
多个网络请求有序
用信号量机制使异步线程完成同步操作
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, queue, ^{
//1
//成功1(发送一个信号)
dispatch_semaphore_signal(semaphore);
//失败1(发送一个信号)
dispatch_semaphore_signal(semaphore);
//等待信号
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
//2
//成功2(发送一个信号)
dispatch_semaphore_signal(semaphore);
//失败2(发送一个信号)
dispatch_semaphore_signal(semaphore);
//等待信号
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
//3
//成功3(发送一个信号)
dispatch_semaphore_signal(semaphore);
//失败3(发送一个信号)
dispatch_semaphore_signal(semaphore);
//等待信号
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
//
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//TODO:
});
VC返回指定的VC
for (UIViewController *subVC in self.navigationController.viewControllers) {
if ([subVC isKindOfClass:[BYDAfterSalesGoodsVC class]]) {
[self.navigationController popToViewController:subVC animated:YES];
break;
}
}
URL跳支付宝支付
///白名单
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipay</string>
<string>alipayshare</string>
</array>
<key>LSApplicationQueriesSchemes</key>
///
BOOL isSucc = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:resultModel.payinfo]];
if (!isSucc) {
[UIFactory showToast:@"用户未安装支付宝客户端"];
}else{
[weakSelf paySuccessEvent];
}
WKWebView加载html字符串
WKWebView * webView = [[WKWebView alloc]init];
webView.navigationDelegate = self;
webView.frame = self.view.bounds;
[webView loadHTMLString:self.htmlStr baseURL:nil];
[self.view addSubview:webView];
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"网页导航加载完毕");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//修改字体大小(方法一)
NSString *fontSize = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%f%%'",kAutoSize(250)];
[webView evaluateJavaScript:fontSize completionHandler:nil];
}
导航栏按钮问题:大小24*24,距离20
//
UIButton * searchBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, kAutoSize(24), kAutoSize(24))];
[searchBtn setImage:UIImageWithName(@"Top_search_icon") forState:(UIControlStateNormal)];
[searchBtn addTarget:self action:@selector(touchSearchClick) forControlEvents:(UIControlEventTouchUpInside)];
UIBarButtonItem * searchBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:searchBtn];
//
UIBarButtonItem *fixedSpaceBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedSpaceBarButtonItem.width = kAutoSize(20);
//
UIButton * scanBarBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, kAutoSize(24), kAutoSize(24))];
[scanBarBtn setImage:UIImageWithName(@"shop_scan_icon") forState:(UIControlStateNormal)];
[scanBarBtn addTarget:self action:@selector(touchScanVinClick) forControlEvents:(UIControlEventTouchUpInside)];
UIBarButtonItem * scanBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:scanBarBtn];
//
self.navigationItem.rightBarButtonItems = @[scanBarButtonItem,fixedSpaceBarButtonItem,searchBarButtonItem];
getset
@property (nonatomic,strong) BYDSentryVideoInfo *model;
-(void)setModel:(BYDSentryVideoInfo *)model
{
_model = model;
}
进入后台做耗时操作
- (void)applicationDidEnterBackground:(UIApplication *)application
{
//阻塞主线程
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(int i=0;i<10000;i++)
{
NSLog(@"i->> %d",i);
}
//在这里你处理一些事情
});
}
不允许在 dealloc 的时候取 weak self.
- (void)dealloc
{
[_segmentView removeFromSuperview];
}
项目规范问题:
1,屏幕适配:kAutoSize()
2,字体适配:UIFontSize()
3,按钮高亮状态
[secondBtn setBackgroundImage:[ImageManager imageWithColor:kColorHighlight] forState:UIControlStateHighlighted];
4,tableview:先隐藏tableView,拿到数据,再显示tableView
5,多个网络请求:顺序执行有依赖关系(用信号量一个一个执行有序),并发执行( 加到group里执行玩再处理)
6,无网络状态,加载失败状态,无数据状态。(占位图+文字+按钮)(正在加载...)(加载失败)(上传中...)(网络异常,请检查网络设置)
7,吐司规范:宏定义
8,接口返回错误码规范
9,订单列表:列表没数据转圈圈,有数据不转圈圈,上拉下拉也不转圈圈,
底部(endRefreshingWithNoMoreData)
10,判断是否越狱手机,越狱手机直接提示不支持使用,点击确定退出应用。
11,数据模式跟UI分开,模型里面不能写UI。
12,Model的网络请求用减方法(实例方法,必须通过实例化的对象调用)。
13,第三方的方法不能直接调用,都要二次封装使用。
项目风险安全问题:
1,梆梆安全
2,微信支付
3,支付宝支付
4,上架审核
5,软件著作权
6,埋点
第三方
SwipeTableView 一款好用的,既能上下滚动又能左右滑动的控件SwipeTableView。
keyChain是ios中唯一可以存储安全可靠敏感数据的地方。而且应用被卸载,数据也不会被删除,所以非常可靠。苹果官方的操作keychain,比较繁琐和隐晦,这里使用开源的第三方库SAMKeychain。
Bugly
优化
1,图片压缩:删除肿的元数据。通过压缩图像节省磁盘空间和带宽,而不会降低质量:https://imageoptim.com/mac
2,后台给业务部门⼀个图⽚上传的标准,业务部门上传的图⽚处理成标准范围内再上传(20-100kb)
3,列表页显示缩略图,详情页使用原始图
4,项目管理从svn(管理方便,适合开发人数不多的项目开发;但服务器压力太大,数据库容量暴增。 如果不能连接到服务器上,基本上不可以工作。)转成git(适合分布式开发,强调个体)
5,iOS路由(MGJRouter) 跳转
CMMI认证
CMMI全称是Capability Maturity Model Integration, 即软件能力成熟度模型集成,是由美国国防部与卡内基-梅隆大学和美国国防工业协会共同开发和研制的,其目的是帮助软件企业对软件工程过程进行管理和改进,增强开发与改进能力,从而能按时地、不超预算地开发出高质量的软件。其所依据的想法是:只要集中精力持续努力去建立有效的软件工程过程的基础结构,不断进行管理的实践和过程的改进,就可以克服软件开发中的困难。CMMI为改进一个组织的各种过程提供了一个单一的集成化框架,新的集成模型框架消除了各个模型的不一致性,减少了模型间的重复,增加透明度和理解,建立了一个自动的、可扩展的框架。因而能够从总体上改进组织的质量和效率。CMMI主要关注点就是成本效益、明确重点、过程集中和灵活性四个方面。
自测的问题
1,iOS,Android的实现,两端同事多沟通,保证逻辑,交互,界面统一。
2,中文文字带上中文的标点符号。
3,每周二代码评审
4,手机号判断:只弹出数字键盘,判断(长度11位)&&(首位是1)
5,下单按钮防重点
6,手机app性能指标记录
7,收集手机app有关隐私权限,以及提示语
项目中遇到的崩溃
//奔溃提示:
**[__NSArrayM info]: unrecognized selector sent to instance 0x280e2bb10**
//原因:解析后台返回的数组,数组里面包含有两个模型。第一个为空,调用第一个模型发生崩溃
XXXModel *model = listArray[0];
model.info
//解决:判断对象是否是某个类型或某个类型子类的对象
XXXModel *model = listArray[0];
if ([XXXModel isKindOfClass:[XXXModel class]]) {
model.info
}
//总结:造成unrecognized selector sent to instance XXX,
//大部分情况下是因为对象被提前release了,在你心里不希望他release的情况下,指针还在,对象已经不在了。
后期项目管理
1,每次发版本前都会走查bugly,解决崩溃问题。
2,每次新功能开发完都会代码走查。
3,每次发版本前都会进行发布评审:
- bug修复与验证
- 强关功能
- SDK更新
- 机型适配
- 注意事项,加固,代码混淆,关闭Log日志,关闭新特性,版本备份,项目配置