50

iOS开发笔记— UITableView、ARC、xcconfig、Push

 5 years ago
source link: http://www.cocoachina.com/ios/20190212/26299.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

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

正文

UITableView

UITableView在reloadData 的时候,如果height的高度发生较大变化,contentOffset无法保持原来的大小时,会发生滚动的效果。如果直接reloadData再setContentOffset:设置位置,仍会出现滚动的效果。

如果需要去除该滚动效果,可以在reloadData之后,调用scrollToRowAtIndexPath并设置animated:NO,最后再用setContentOffset:微调位置。

同理,如果需要在reloadData后,手动scroll到header时,可用同上的解决方案。

UITableView还有类似的问题,如果列表项过多时, scrollToRowAtIndexPath有时并不准确 ,比如有1000行时滚动到第500行,此时可能会出现滚到501或者499行的情况。

究其原因,是因为UITableView不会调用1~499行所有的heightFor和cellFor方法,所以无法准确计算出来位置。

从这里去分析,如果需要滚动到准确的位置,可以用estimatedRowHeight的属性,设置和行高一样的高度;在行高各不相同的场景,可以设置estimatedRowHeight为大致的数字,在scrollToRowAtIndexPath之后通过setContentOffset:微调位置。

ARC

Automatic Reference Counting(ARC)是编译器特性,由编译器插入对象内存管理代码,实现内存管理。

如果仅仅是retain/release的管理,非常容易理解,但是插入的代码如何实现weak、strong这些运行时特性?

最近同事遇到一个问题,以下代码会crash:

他实现了一个editingButton的getter,同时在dealloc的时候将其移除;

如果editingButton在整个生命周期都没有初始化时,则在dealloc使用getter会触发初始化,然后在下面的weakify(self);这一行crash。

- (void)dealloc
{
    [self.editingView removeFromSuperview];
    [self.editingButton removeFromSuperview];  // crash
}

- (UIButton *)editingButton
{
    if (!_editingButton)
    {
        _editingButton = [UIButton buttonWithType:UIButtonTypeCustom];
        ......
        weakify(self); // CRASH
        [_editingButton ss_addEventHandler:^(id  _Nonnull sender) {
                  ......
        } forControlEvents:UIControlEventTouchDown];
    }
    return _editingButton;
}

闪退的堆栈如下

FfEjqar.png!web

ARC的文档 中找到闪退的方法,其中有一段描述如下:

ZjUfIjv.png!web

当dealloc开始的时候,weakSelf的指针应该都已经被重置为nil;如果在dealloc的函数中再次初始化weakSelf指针会出现异常。

另外,在dealloc方法执行属性的getter方法也是不合理,因为属性的getter方法大都包括如果未创建就创建并初始化的逻辑。

ARC的文档 这份文档也是非常好的ARC学习资料。

xcconfig

xcconfig是用来保存build setting键值对的文件,里面是一些纯文本;

//:configuration = Debug
PRODUCT_BUNDLE_IDENTIFIER = com.loyinglin.dev
DISPLAY_NAME = 测试标题
PRODUCT_NAME = Learning
GCC_TREAT_WARNINGS_AS_ERRORS = YES

//:configuration = Debug
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 SSDEBUG=1

比如这里配置是一份debug的xcconfig,其中PRODUCT_BUNDLE_IDENTIFIER = com.loyinglin.dev的键值会在编译的时候生效。

xcconfig有什么用?

一个Xcode工程,一定会有Debug的开发环境和Release的发布环境,可能会有Testflight的灰度环境、DailyBuild的持续集成环境、XXLanguage的多语言环境、TestCoverage的覆盖率测试环境、IAP的内购测试环境等;每个环境所用的证书不同,APP安装后显示的名字不同,provision file也不同等等。

一种方案是使用Target来解决,公用的部分设置在project,每个环境根据各自特点自定义某些设置;这样带来的后果是target数量增多明显,而target增多带来的后果是当需要新增extension的时候会工作量巨大,并且多环境的管理难度加剧。

另外一种方案是使用Configuration来区分环境,而xcconfig就是用来管理Configuration的文件。

如何创建和使用xcconfig?

1、在Xcode中新建文件,输入config,选择configuration settings file;这一步是创建xcconfig的文件。

UjUfieq.png!web

2、在Xcode中选中工程,在configurations中选择需要配置的选项,这里以debug为例,点击后选择刚刚已经创建的xcconfig,则可以把xcconfig和debug的编译选项绑定在一起。

zABnyie.png!web

如果你用了cocoaPod,你会发现这一项已经有了CocoaPod创建xcconfig,如果选择了自己新建的xcconfig,则会编译失败;

此时可以在自己新建的xcconfig头文件中加入以下代码:

#include "Pods/Target Support Files/Pods-YourName/Pods-YourName.debug.xcconfig"

注意需要修改成自己的工程名。

3、在build setting选中某个配置项,cmd+c复制然后到xcconfig的文件中,cmd+v就可以复制配置项到xcconfig中。

注意如果这个配置项在build setting已经有自定义值,需要将其删除,原因下面解释。

2qERNrQ.png!webxcconifg的配置和工程默认配置、手动在build setting配置有什么区别?

配置的结果优先级不同,我的理解是:

a、project默认配置是最低优先级,因为是最基础的配置;

b、target配置基于project,但target默认会添加一些配置,优先级比上面高;

c、xcconfig的配置是target某个config的配置,优先级比上面高;

d、target的build setting中直接添加的配置项,优先级比上面高; MbmQjmi.png!web

知道上面的关系后,我们可以解决使用xcconifg时, CI 打包xcconifg配置项不生效 的问题:

检查是否对应配置项是否在target的build setting中直接添加;

如果需要新增某个configuration,可以直接duplicate已有的configuration,但是如果使用Pods需要重新pod install,以生成对应的pod工程的配置项,否则会出现下图的报错:

zUBNFnb.png!web

找不到对应库,因为新的configuration没有设置对应的file

Push 证书

.p12是连接苹果APNs服务器的证书(公钥+私钥);

.cer 是苹果的证书文件(公钥);

.pem是OpenSSL的证书文件(公钥+私钥);

当我们生成push证书时,其实就是将我们本地的p12通过脚本,导出对应的pem文件;

下面是一段常用的脚本:

P12_CERT=AppStorePush.p12 # p12证书文件
PASSWD=loying # p12密码

EXPORT_CERT=AppStorePush.pem # 导出pem证书
EXPORT_KEY=AppStorePushWithKey.pem  # 导出的pem私钥,有密码
EXPORT_KEY_UNENCRY=AppStorePushWithoutKey.pem # 导出的pem私钥,无密码
EXPORT_KEY_AND_CERT=AppStore_ck.pem # 含有证书和私钥的pem

openssl pkcs12 -clcerts -nokeys -out ${EXPORT_CERT} -in ${P12_CERT} -passin pass:${PASSWD} # 导出证书

openssl pkcs12 -nocerts -passout pass:${PASSWD} -out ${EXPORT_KEY} -in ${P12_CERT} -passin pass:${PASSWD} #导出私钥,有密码

openssl rsa -in ${EXPORT_KEY} -passin pass:${PASSWD} -out ${EXPORT_KEY_UNENCRY} # 导出私钥,无密码

cat ${EXPORT_CERT} ${EXPORT_KEY_UNENCRY} > ${EXPORT_KEY_AND_CERT}   # 证书和私钥合起来

openssl s_client -connect gateway.push.apple.com:2195 -cert ${EXPORT_CERT} -key ${EXPORT_KEY_UNENCRY}   # 测试 push证书

# gateway.push.apple.com
# gateway.sandbox.push.apple.com

在调试Push的时候,以下这个软件(App Store可以下载)非常便捷:

BVze2uJ.png!web

使用时配置好证书(可以点击connect验证是否连接APNs成功),再从iPhone获取到deviceToken添加到设备列表,便可以使用推送。

总结

这些都是在项目中遇到的一些问题,UITableView这个是老生常谈,ARC那篇文档是很好的学习资料,xcconfig需要多研究,未来随着版本和渠道增多会越来越复杂,Push在Easy APNs Provider这个软件出来后就很好测试,再也不用登录信鸽去手动配置Push。

新的一年,继续搬砖和学习。

作者:落影loyinglin

链接:https://www.jianshu.com/p/0093cb8c5a35


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK