iOS开发项目小结

文章首发于个人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

iOS开发项目小结_第1张图片
样例

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 报错

iOS开发项目小结_第2张图片
验证包 报错

程序中包含 1024 * 1024 icon的问题;icon应该是直角,且alpha不设置

iOS开发项目小结_第3张图片

十九、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 详细信息已更新)

iOS开发项目小结_第4张图片
iphone尺寸
// 判断是否为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.windowself.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位操作系统中占8Byte
    size_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名称失败的解决方法

你可能感兴趣的:(iOS开发项目小结)