文章首发于个人blog
欢迎指正补充,可联系[email protected]
原文地址:iOS项目总结-CPR
拓展:iOS开发经验总结
目录
日期:2018.10
- 零、页面卡顿处理
- 一、Masonry 布局 1/5处
- 二、masonry 不立刻生成frame
- 三、UIButton中image和title的 UIEdgeInsets
- 四、YYModel使用
- 五、ipad横竖屏,固定竖屏
- 六、启动页黑屏
- 七、数组谓词排序、倒序
- 八、tableview的cell点击 与 手势点击冲突
- 九、cell 上的 定时器
- 十、float 四舍五入
- 十一、tableview 删除某一行cell使用 deleteRowsAtIndexPaths
- 十二、Objective-C 同步锁
- 十三、线程同步
- 十四、NSURL地址有中文
- 十五、全局隐藏状态栏
- 十六、设备适配,区分iphoneX XR XS等
- 十七、GCD定时器,暂停状态,直接关闭定时器,崩溃
- 十八、Validate APP 报错
- 十九、iOS中延迟执行和取消的几种方式
- 二十、NSArray 的 copy 和 multecopy
- 二十一、在Controller中监听APP进入后台与APP被杀死状态
- 二十二、设备适配,区分iphoneX XR XS等
- 二十三、dealloc中只释放引用和解除监听,不作其他操作
- 二十四、@aotureleasepool降低内存峰值
- 二十五、数组遍历。
更新日期:2018.11
- 二十六、MBProgressHUD显示图层探究
结合 Reveal 谈谈 MBProgressHUD 的用法 - 二十七、获取当前显示的ViewController探究
iOS获取当前正在显示的UIViewController
iOSgetCurrentVC - 二十八、int64_t 与 int32_t 与 NSInteger
- 二十九、时间戳 NSInteger 32位机型范围越界
- 三十、dealloc崩溃:-[ViewController .cxx_destruct]
ARC下dealloc过程及.cxx_destruct的探究 - 三十一、取消延时操作失效(看:十九)
更新日期:2018.12
- 三十二、发布证书问题:
doesn't include signing certificate
报错Provisioning profile "XX" doesn't include signing certificate "xx developer"
更新日期:2019.01
- 三十三、内存泄露检测和解决
- 三十四、崩溃
-[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:usingPresentationValues:]: row (16) beyond bounds (16) for section (0).
更新日期:2019.02
- 三十五、iOS12上获取不到wifi名字的问题
零、页面卡顿处理
iOS 保持界面流畅的技巧
IOS开发CPU/GPU消耗原因分析和解决办法
iOS性能优化
Instruments性能优化-Core Animation
一、Masonry 布局 1/5处
参考文档
长度关系和位置关系,不能做比例运算。
举个例子
我现在希望子视图的横向中心线(centerY)在高度的1/5处,一般的想法是
make.centerY.mas_equalTo(self.height).multipliedBy(0.2);
运行出错。
正确的做法是
make.centerY.mas_equalTo(self.bottom).multipliedBy(0.2);
同理,我想子视图的垂直中心线(centerX)在宽的1/5处,应为
make.centerX.mas_equalTo(self.right).dividedBy(5);
最后,高度和宽度的比例那就更容易理解了.
make.height.mas_equalTo(self.height).dividedBy(5);(子视图高度是父视图高度的1/5)
二、masonry 不立刻生成frame
iOS开发笔记 | 由使用Masonry布局不能立即获取到frame想到的一些问题
三、UIButton中image和title的 UIEdgeInsets
iOS UIButton之UIEdgeInsets详解
四、YYModel使用
4.1、json -> NSDictionary
// NSObject+YYModel.m
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
if (!json || json == (id)kCFNull) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return dic;
}
五、ipad横竖屏,固定竖屏
参考
1、要勾选requeires full screen
,同时不选择 upside down 、landscape left 、landscape right
2、然后在AppDelegate.m
中修改
// APPdelegate.m
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)nowWindow {
return UIInterfaceOrientationMaskPortrait;
}
六、启动页黑屏
启动页加载的图片命名不能包含 launch、Launch Images Source与Launch Screen File 等(改了图片名还真解决了黑屏)
iOS8.0 以后,系统默认优先选用Launch Screen.storyboard作为启动图,Launch screen interface file base name 需要填写对应的storyboard.
如果实在没办法解决,可以修改图片名称,再试试,虽然不知道为什么,我就是这么干成功的。
七、数组谓词排序、倒序
https://blog.csdn.net/hbblzjy/article/details/73232275
7.1、数组谓词排序
// Key:关键字 ASC:正序
NSSortDescriptor *desc = [NSSortDescriptor sortDescriptorWithKey:@"correctrate" ascending:YES];
// 直接排序完成
[self.dataSource sortUsingDescriptors:@[desc]];
7.2、数组倒序
// 倒序
NSArray *copyArr = (NSArray *)[[ARR reverseObjectEnumerator] allObjects];
八、tableview的cell点击 与 手势点击冲突
iOS基础补完计划–透过堆栈看事件响应机制
iOS点击事件和手势冲突
iOS触摸事件那点儿事
iOS 解决tableView和点击手势冲突问题
判断如果点击的是tableView的cell,关闭手势 不是点击cell手势开启
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) { // uitableview
return NO;
}
return YES;
}
九、cell 上的 定时器
YSTimeCountDown
十、float 四舍五入
NSString *new = [NSString stringWithFormat:@"%.1f",correctRate]; // 不保留小数,它会自动四舍五入
// 举例
NSString *new = [NSString stringWithFormat:@"%.1f",2.33345633444];
NSString *new1 = [NSString stringWithFormat:@"%.1f",2.34345633444];
NSString *new2 = [NSString stringWithFormat:@"%.1f",2.35345633444];
NSString *new3 = [NSString stringWithFormat:@"%.1f",2.36345633444];
// 输出
2.3
2.3
2.4
2.4
十一、tableview 删除某一行cell使用 deleteRowsAtIndexPaths
了解了上面几个函数,我们来看什么是操作刷新块:
当我们调用的上面的函数时,tableView会立刻调用代理方法进行刷新,如果其中我们所做的操作是删除某行,而然数据源数组我们可能并没有刷新,程序就会崩溃掉,原因是代理返回的信息和我们删除后不符。
iOS为我们提供了下面两个函数解决这个问题:
开始块标志 、结束快标志
- (void)beginUpdates;
- (void)endUpdates;
我们可以将我们要做的操作全部写在这个块中,那么,只有当程序执行到结束快标志后,才会调用代理刷新方法。代码示例如下:
[tab beginUpdates];
[tab deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
[dataArray removeObjectAtIndex:1];
[tab endUpdates];
//注意:不要在这个块中调用reloadData这个方法,它会使动画失效。
十二、Objective-C 同步锁
Effective Objective-C Notes:GCD 实现同步锁
iOS中保证线程安全的几种方式与性能对比
十三、线程同步
iOS多线程:『GCD』详尽总结
/* 创建并发队列 */
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
/* 添加两个并发操作A和B,即A和B会并发执行 */
dispatch_async(concurrentQueue, ^(){
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"OperationA");
});
dispatch_async(concurrentQueue, ^(){
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"OperationB");
});
/* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
dispatch_barrier_async(concurrentQueue, ^(){
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"OperationBarrier!");
});
/* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
dispatch_async(concurrentQueue, ^(){
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"OperationC");
});
dispatch_async(concurrentQueue, ^(){
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"OperationD");
});
十四、NSURL地址有中文
// 地址有中文,URL=nil
URL = [NSURL URLWithString:str];
iOS中NSURL一路踩坑
十五、全局隐藏状态栏
第一步:target -> Hide status bar 勾选
第二步:Info.plist
设置 View controller-based status bar appearance
,把键值设置为NO。
十六、设备适配,区分iphoneX XR XS等
史上最全的iOS各种设备信息获取总结(iPhone XS Max/XR 详细信息已更新)
// 判断是否为iPhoneX - 5.8 inch
#define iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXR - 5.8 inch
#define iPhoneXR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXS - 6.1 inch
#define iPhoneXS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXS MAX - 6.5 inch
#define iPhoneXSMax ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) : NO)
// 主要是用于区分是否是 刘海屏
#define LiuHaiPhone \
({BOOL isLiuHaiPhone = NO;\
if (@available(iOS 11.0, *)) {\
isLiuHaiPhone = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\
}\
(isLiuHaiPhone);})
十七、GCD定时器,暂停状态,直接关闭定时器,崩溃
2018-07-26 GCD定时器的释放与程序崩溃的问题
如果timer暂停状态,需要先恢复,在进行Cancel
if(_timer){
// 此刻被暂停了,先恢复,再销毁
dispatch_resume(_timer);
dispatch_source_cancel(_timer);
_timer = nil;
}
十八、Validate APP 报错
程序中包含 1024 * 1024 icon的问题;icon应该是直角,且alpha不设置
十九、iOS中延迟执行和取消的几种方式
iOS中延迟执行和取消的几种方式
- 方法一、performSelector 方法
[self performSelector:@selector(delayMethod) withObject:nil afterDelay:2.0];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayMethod) object:nil];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
- 方法二、NSTimer 定时器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
[timer invalidate];
- 方法三、NSThread线程的sleep
注:此方法是一种阻塞执行方式,建议放在子线程中执行,否则会卡住界面。但有时还是需要阻塞执行,如进入欢迎界面需要沉睡3秒才进入主界面时。
取消延迟执行?
sleep放入子线程,可以通过处理取消。
[NSThread sleepForTimeInterval:2.0];
[self delayMethod];
- 方法四、GCD
__weak typeof(self)weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
二十、NSArray 的 copy 和 multecopy
copy与mutablecopy使用对比
二十一、在Controller中监听APP进入后台与APP被杀死状态
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(comeHome:) name:@"UIApplicationDidEnterBackgroundNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
- (void)comeHome:(UIApplication *)application {
NSLog(@"进入后台");
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序被杀死");
}
二十二、设备适配,区分iphoneX XR XS等
史上最全的iOS各种设备信息获取总结(iPhone XS Max/XR 详细信息已更新)
// 判断是否为iPhoneX - 5.8 inch
#define iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXR - 5.8 inch
#define iPhoneXR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXS - 6.1 inch
#define iPhoneXS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
// 判断是否为iPhoneXS MAX - 6.5 inch
#define iPhoneXSMax ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) : NO)
// 主要是用于区分是否是 刘海屏
#define LiuHaiPhone \
({BOOL isLiuHaiPhone = NO;\
if (@available(iOS 11.0, *)) {\
isLiuHaiPhone = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\
}\
(isLiuHaiPhone);})
二十三、dealloc中只释放引用和解除监听,不作其他操作
二十四、@aotureleasepool降低内存峰值
NSArray * dataRecords = ....;
NSMutableArray * peopleArr = [NSMutableArray new];
for(NSDictionary *record in dataRecords) {
@aotureleasepool {
EOCPerson * person = [[EOCPerson alloc] initWithRecord:record];
[peopleArr addobject:person];
}
}
二十五、数组遍历。
iOS开发中数组常用的五种遍历方式
- for循环
- for in 快速枚举
- NSEnumerator
- 快速遍历 - enumerateObjectsUsingBlock
- 快速迭代 - dispatch_apply
- RAC遍历
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) { NSLog("%@", x); }];
/**************/
更新日期:2018.11
/**************/
二十六、MBProgressHUD显示图层探究
- 结合 Reveal 谈谈 MBProgressHUD 的用法
视图层次结构
第一层:UIWindow ,即 self.view.window;
第二层:UINavigationController 的 view ,即我们前面提到的 self.navigationController.view;
第三层:self.view;
第四层:导航栏 UINavigationBar ,即 self.navigationController.navigationBar。
总结:
当你需要让导航栏上的按钮不可点击的时候,可以选择使用 self.view.window
或 self.navigationController.view
显示 MBProgressHUD 。
反之,可以选择 self.view
。
二十七、获取当前显示的ViewController探究
- iOS获取当前正在显示的UIViewController
- iOSgetCurrentVC
+(UIViewController *)getCurrentVC {
UIViewController * result = nil;
// 获取默认的window,如果有多个window情况下,获取最上层的window
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal) {
NSArray * windows = [[UIApplication sharedApplication] windows];
for (UIWindow * tmpWindow in windows) {
if (tmpWindow.windowLevel == UIWindowLevelNormal) {
window = tmpWindow;
break;
}
}
}
// 获取window的rootViewController
result = window.rootViewController;
while (result.presentedViewController) {
result = result.presentedViewController;
}
if ([result isKindOfClass:[UITabBarController class]]) {
result = [(UITabBarController *)result selectedViewController];
}
if ([result isKindOfClass:[UINavigationController class]]) {
result = [(UINavigationController *)result visibleViewController];
}
return result;
}
二十八、int64_t 与 int32_t 与 NSInteger
在32位系统中
int 占4个字节
long 占4个字节
NSInteger 是int的别名,占4个字节
long long 占8个字节
int32_t 是int的别名,占4个字节
int64_t 是long long的别名,占8个字节
在64位系统中
int 占4个字节
long 占8个字节
NSInteger 是long的别名,占8个字节
long long 占8个字节
int32_t 是int的别名,占4个字节
int64_t 是long long的别名,占8个字节
二十九、时间戳 NSInteger 32位机型范围越界
29.1、如何判断32位、64位?
iOS-基本数据类型、大小端、电脑位
- 方案一:利用long 在不同操作系统中所占字节不同。
long在32位操作系统中占4Byte
long在64位操作系统中占8Bytesize_t longSize = sizeof(long); NSLog(@"longSize = %zu", longSize);
- 方案二:读取寄存器长度
register read
29.2、NSInteger取值范围?
#if __LP64__ || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
32位下,NSInteger是int
取值范围:-2(32-1) ~~~ 2(32-1)-1
-2147483648 ~~~ 2147483647
64位下,NSInteger是long
取值范围:-2(64-1) ~~~ 2(64-1)-1
-9223372036854775808 ~~~ 9223372036854775807
三十、dealloc崩溃:-[ViewController .cxx_destruct]
- ARC下dealloc过程及.cxx_destruct的探究
原因:GCD定时器 dispatch_source_t
处于 暂停状态 释放异常,导致 .cxx_destruct
析构失败。
解决方案:对 dispatch_source_t
状态修改,再进行释放。
三十一、取消延时操作失效(看:十九)
cancelPreviousPerformRequestsWithTarget: isn't working for me
原因一:参数异常,一个object为 'self' ,一个为 'nil'
// 设置延时操作
[self performSelector:@selector(hideNavigationBar) withObject:self afterDelay:3.0];
// 取消延时操作
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(navigationBarHide) object:nil];
原因二:我犯的错误,取消操作处于其他线程中,这里就牵扯到了runloop处理performSelector操作,总之在主线程中进行 创建延时操作和取消延时操作
就好了。
三十二、发布证书问题:doesn't include signing certificate
报错Provisioning profile "XX" doesn't include signing certificate "xx developer"
三十三、内存泄露检测和解决
内存检测:使用Xcode工具Instruments
和 腾讯扩展库MLeaksFinder
项目中的内存泄露主要有:
第一个是Storyboard中无用的视图且放置了init箭头;
第二个是delegate用了strong修饰;
三十四、崩溃 -[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:usingPresentationValues:]: row (16) beyond bounds (16) for section (0).
原因:
这个错误是传入的IndexPath已经越界了。需要在调用之前加入判断语句。
拓展:
[self.tableView scrollToRowAtIndexPath:]方法滑动崩溃
类似美团、饿了么点餐界面的分类与菜品联动
当分类中某一个分类下面菜品数量为零时,点击此分类,导致程序Crash
// 将row设置为NSNotFound就可以了
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:NSNotFound inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
三十五、iOS12上获取不到wifi名字的问题
需要添加新的权限即可。
iOS 12 获取wifi名称失败的解决方法