专栏首页落影的专栏iOS开发笔记(十四)

iOS开发笔记(十四)

前言

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

正文

CocoaPod

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

pod_binary 'XXKitDebug','0.0.2', :configurations => ['Debug', 'DailyBuild']

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

- (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方法。只要填入一个非负的值,即可解决。

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,如果能够获取到则启动异步请求任务进行处理。

- (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增加如下部分

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参数

总结

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iOS开发笔记(四)

    前言 最近遇到一个苦恼的问题,寻找了漫长的时间才解决。 起因是项目需要fork一个新的分支到新的git,于是把代码复制到新的git,创建git库,然后推送,一...

    落影
  • iOS开发笔记(十三)

    GPUImage的framework/Source目录下,有iOS和Mac两个文件夹,在iOS的工程中include了Mac文件夹的代码,会产生编译错误;将Ma...

    落影
  • iOS 开发笔记

    相较于Legacy Build System,New Build System能够大幅度提升编译速度,同时更严格地检测代码质量(循环引用等)和更友好地给出提示

    无忧366
  • iOS开发笔记(十一)— UITableView、ARC、xcconfig、Push

    分享iOS开发中遇到的问题,和相关的一些思考,本次内容包括:UITableView滚动问题、ARC、xcconfig、Push证书。

    落影
  • Android开发笔记(四十四)动态UI事件

    动画事件主要用于Animation控件,可监控动画开始、结束、重播等行为。相关类名与方法说明如下: 监听器类名 : AnimationListener ...

    用户4464237
  • iOS开发笔记(三)

    前言 日常开发遇到的问题记录。 JSON Invalid type in JSON write (NSConcreteMutableData) 合法的j...

    落影
  • iOS开发笔记(五)

    前言 社会的模式很多是重复的,当你做一样事情很擅长时,与之类似的事情也能触类旁通。 正文 Code开发 1、delegate的trick 很多人习惯在调用de...

    落影
  • iOS开发笔记(六)

    前言 专注、坚持,是优良的品格。 正文 1、cell和cell.contentView 的区别 在给UITableViewCell添加视图的时候,我们有以下两种...

    落影
  • iOS开发笔记(七)

    正文 这次分享三个有意思的问题:二维码生成、Xcode8单元测试的问题、添加新字体。 二维码生成 iOS平台上的二维码生成有很多第三方库,也可以使用原生的方法,...

    落影

扫码关注云+社区

领取腾讯云代金券