前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS开发笔记(十四)

iOS开发笔记(十四)

作者头像
落影
发布2021-01-21 06:26:05
1.3K0
发布2021-01-21 06:26:05
举报
文章被收录于专栏:落影的专栏落影的专栏

前言

分享iOS开发中遇到的问题,和相关的一些思考。

正文

CocoaPod

最近某位同学在项目中添加了一个调试工具XXKitDebug,但是不想在线上开启,于是通过configurations进行区分,仅在'Debug' 和 'DailyBuild' 引入。(线上版本configurations是distribution)

代码语言:javascript
复制
pod_binary 'XXKitDebug','0.0.2', :configurations => ['Debug', 'DailyBuild']

然后在代码中通过宏定义来描述:

代码语言:javascript
复制
- (void)onShowDebugPage {
#if __has_include(<XXKitDebug/XXKitDebug.h>)
    [XXKitDebug showDebugPage];
#endif
}

这段__has_include是否能够在Distribution情况下屏蔽下面的代码?

答案是:不可以,会出现链接失败。

官方文档有关于__has_include的说明,是通过检查指定的文件,是否能够正常引入来进行。 但是Podfile的解析和执行是在pod install的时候,此时并不知道将来的build的configuration,CocoaPod的解决办法是针对不同的configuration生成不同的xcconfig。比如说上面的XXKitDebug会在debug.xcconfig和distribution.xcconfig。

我们对比下debug.xcconfig和distribution.xcconfig的区别,发现两者在HEADER_SEARCH_PATHS选项中都添加了XXGeckoKitDebug,但是distribution.xcconfig在OTHER_LDFLAGS的时候并没有添加XXKitDebug。 这样解释了为什么,__has_include可以找得到头文件,但是最终报符号缺失,因为链接时没有带上这个库的符号。

UIKit
UITableView全量调用cellForRowAtIndexPath

在开发过程中,遇到一个奇怪的问题:某个界面的UITableView有20个元素(numberOfSectionsInTableView返回20),当前屏幕只有10个cell,但是UITableView仍然调用20次cellForRowAtIndexPath

排查问题时,首先是看UITableView的几个核心属性numberOfSections、rowHeight和cellFor函数的官方解释。 在rowHeight的属性说明中, 找到以下这句:

When you implement the delegate method, the table view must call that method for every row of the table, including those that are offscreen. For tables with large numbers of rows (1000 or more), calling that method many times can negatively impact performance.

按照官方文档的说明,确实是cell的调用次数就是model的数量。 这时候回想起来UITableView有一个estimatedRowHeight属性,查了一下这个属性的说明,发现默认值是UITableViewAutomaticDimension(-1)表示不会预估高度,会实时调用所有cell的heightFor和cellFor方法。只要填入一个非负的值,即可解决。

代码语言:javascript
复制
self.tableView.estimatedRowHeight = 0;

思考?:为什么会有rowHeight和estimatedRowHeight两个属性来控制?直接根据cellFor来计算不可以吗? 假如是我们来设计一个列表项,我们肯定希望在初始化的时候要知道所有cell的高度,这样才能方便的控制整个列表的高度。(UIKit一开始确实是这样设计的) 但是这样会面临全量调用heightFor和cellFor方法的问题,在cell数量较多的时候,非常容易卡主。并且这两个方法都是主线程同步调用,也不好通过dispatch_async来进行子线程操作。所以iOS7的时候UIKit引入了estimatedRowHeight,并且为了兼容旧代码设置默认值是UITableViewAutomaticDimension。

GCD

有一个业务场景是批量加载数据,实现用串行队列承载所有任务(下文self.queue),同时为了提高处理效率增加了并发,下文self.semaphore初始化为3,串行队列每次执行会获取self.semaphore,如果能够获取到则启动异步请求任务进行处理。

代码语言:javascript
复制
- (void)requestWithId:(NSString *)xxId {
    // self.semaphore 初始化为3,self.queue是串行队列
    dispatch_async(self.queue, ^{
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        // async request do some thing
        {
            ....
            // done
            dispatch_semaphore_signal(self.semaphore);
        }
}

后面版本迭代优化,发现self.queue处理速度比较慢,是否能直接把queue从串行队列改为并发队列?

如果直接改为并发队列,极端场景可能会出现以下的现象:

当queue变成并发队列的时候,就出现经典的gcd并发队列阻塞操作问题,会导致线程爆炸。

思考?:如何避免类似这种问题的出现? 个人观点是不要有阻塞操作,所有任务存起来(比如说用数组存),并发若干个任务去执行,有任务完成就去数组里面取新任务。这样实现可以方便增加优先级,仅需要在取任务的逻辑增加优先级判断;还可以对超时任务进行处理,比如说每次添加任务都检查下是否有任务执行时间很长,判断是否跳过该任务。 阻塞操作容易导致线程卡死,又不好做后续的维护和扩展处理,因为在等待过程中整个线程无法进行逻辑处理。

Xcode
1.调试启动方式

在Xcode断点调试时,最常用的是按下command+R,然后等编译、链接、安装、运行。 但是这种方式无法调试通过Push启动、从其他App呼起等场景,此时可以修改下面的配置,在按下command+R的时候只会进行上述的前三步,待用户手动触发App启动。

2.去除i386库的支持

i386是一个很老的架构,目前是32位的模拟器在使用。某一个依赖库的新版本不支持i386,build时在提示符号缺失。

Podfile增加如下部分

代码语言:javascript
复制
post_install do |installer_representation|
    installer_representation.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['EXCLUDED_ARCHS'] = 'i386'
        end
    end
end

Xcode工程设置的build settings,可以增加excluded architectures的i386(可选)

target的buid settings

PS:Xcode12默认架构里不包括模拟器,可以按照上图Architectures的Debug配置,增加模拟器的选项。 如果是Xcode12,excluded architectures不添加也没关系,因为默认就没有i386。

思考?:为什么CocoaPod的默认架构里面包括i386,但是主工程里面的默认架构没有包括i386?(CocoaPod版本1.7.4) 是因为两者的baseSDK不同。主工程的BaseSDK是iOS(可以看上图第三项配置),工程中target的BaseSDK一般默认继承project的设置(注意上图BaseSDK的参数是灰色),project的BaseSDk设置了工程的BaseSDK是iOS。

project的buid settings

我们再看看Pod的工程设置,注意下图的architectures和Base SDK设置和上图的不同

pod工程的project的buid settings

当工程选择No SDK的时候,默认选项就是macOS,此时architecture就包括armv7/arm64和intel x86_64/i386。 没有找到资料解释Pod工程设置为什么没有继承主工程,而是采用全架构编译的方式。只能猜测是为了避免某些工程比较复杂,导致缺少部分架构;又或者自己没有找到正确的使用方式。

3.xcode12 ipa包名修改

升级xcode 12之后发现archive产物的名字发生改变,可以检查下图的配置

以及plist的Bundle name参数

总结

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • CocoaPod
      • UIKit
        • UITableView全量调用cellForRowAtIndexPath
      • GCD
        • Xcode
          • 1.调试启动方式
          • 2.去除i386库的支持
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档