关于996ICU的一些看法
这个项目在有7k多star的时候我就看到有人推了,当时也就是看看而已,了解了这个标识的意思,并没有点进去,更没有star。再之后随着越来越多的star和关注量,公众号、社区、论坛,但凡有程序员的角落,大家都在争先传播着“996ICU”这个概念,每一次转发、star都是对996这种工作制的无声抵抗。刚才看了一眼star已经12w+,马上就要超过react,这足以说明忍受着996的程序员是多么大的一个群体。我这里不想跟大部分人一样,痛斥996,歌颂这种行为,我想说些不一样的东西。 从自己经历出发吧,2015年刚出来工作的时候是996,当时我并没有不情愿或者觉得累,因为我很清楚自己就是个菜鸟,996可以让自己有更多的时间用来学习东西,提升技术。这种工作节奏持续了大半年,这段时间的感受也是即充实又满足的。后来公司业绩好转,改成大小周,再几个月后又改成双休。当然最开始享受双休的那几天还是很爽的,后来就慢慢习惯了,996前后我的工作状态并没有什么变化。 再后来进了现在的公司,人事告诉我大小周,那时我已经保持了将近一年的双休节奏,稍微犹豫了那么一会,但考虑到公司不错的发展前景,我还是答应进来了。从双休切换到大小周之后,我才意识到双休是多么爽的一件事。。。真的深刻体会到得到了就习以为常,失去了才懂的珍惜的感受。 在我进来不到半年的时间,公司运行的大小周调整为975,因为晚上7点公司提供晚餐,其实开发这边都是吃完饭休息一会再继续回去工作,大约也是9点走,可以说是995。 得知双休之后,我的学习状态也高涨起来了,规划着两天的自由时间可以做很多事情了,睡懒觉、打游戏、学一些自己感兴趣的东西、跑步。然而实际情况却是开始双休之后的很长一段时间里,我完成度最高的是睡懒觉、打游戏,对于学习,健身基本没怎么执行。有那么几个周末我深深地体会到舒适区只要进入,是很难挣脱出来。周末的早上醒来就感觉自己是被封印在床上了一样,除了刷手机什么都不想做,哪都不想去,早上喝水,中午叫外卖,晚上再来一份外卖,一天结束了。 同时我又是一个反省意识很强的人,在虚度了一个周末之后心里就会非常自责。一个被荒废的周末还不如忙碌一天的工作给我带来的满足感,我甚至产生了想要回到996的状态,以此来约束自律性很差的自己。 再后来也就是最近一段时间,为了达成近两个月的OKR,我“如愿以偿”地又回到了996的模式。因为早有准备,多上一天班并没有让我感觉失去多少自由。公司也考虑大家劳逸结合的情况,搭建了台球,乒乓球,Switch游戏机这种娱乐措施。周六加班,也就是做一些修修补补的任务,中午吃完饭会打一会台球,乒乓球,或者玩一会农药,回来继续工作。有时候会感觉周六加班更像是换了一个地方过周末。 但其实我也不是完全接受996的,它解决了我的时间配比问题,但也会给我引起其他不便。比如我想和女朋友周末出去就近玩一下,北京以内还好,如果想出北京就会时间安排不过来。当然还有其他的不便,要知道休息两天和休息一天可是相差一倍呢。 那回到这个话题,996这个制度是否OK呢? 如果你自律性很强,有很多想法想要实现,也有较好的时间规划,那多一些自己的私人时间是再好不过的了,996对你的确不能让你发挥更大的作用。如果你自律性很差,也没考虑过多出来的时间用来干嘛(玩和睡除外),那我建议你可以先好好想想双休之后你会做什么,双休对你来说生活品质是提高了还是降低了。当然如果你认为自己就是不想加班,只想过平淡恬静的生活,上班对你来说就是为了赚钱,这种想法也没错。因为不是每个程序员都热爱着这份工作,能从中获取到乐趣的,那就换份轻松点的工作呗。我下面讨论的内容对这类人群也是不适用的,你们可以看到这就结束了。 说些不那么中听的话,如果你自律性差,公司的996相当于帮你加了一道屏障,这个时间段它帮你隔开舒适区,迫使你投入到工作中。而我公司也有不少,没有加班任务,时不时也会主动来公司加班的人。那最理想的情况其实就是,公司相信员工,不强制加班,大家想休息了,该休息了就休息,保证把工作效率提上去。员工呢,同时为自己和公司负责,没完成工作,学习新东西,就可以来公司主动加班。 之所以说他理想,是因为很多公司不信任员工,很多员工也没有那种对自己和公司强烈的责任感。所以互联网行业出现这么多的996不是单纯公司的问题,一些程序员也负有一定的责任。 再说一些特殊情况,如果是无良公司,不考虑员工效率问题,各种强制加班,搞996,那我也是坚决反对的。 996ICU项目里有提《劳动法》,标准工时一周最高为48小时,而996是72小时,超标不少;以及超时薪资应为平日工资的150%。这让我想到了大二寒假在电子厂打工的经历,标标准准的按照劳动法来的,基本都是按时薪算的。为什么同样是受劳务合同保护,不同的行业却有着不同的处理方式呢? 我认为一个重要的原因是,电子厂流水线上的那是标准的工人,计时或者计件,可以清清楚楚的搞清楚。而程序员呢,智力劳动,没法准确的衡量一个人的工作价值。你加班两小时,是因为水平问题还是别的原因,如果是自己原因的话,公司需要付钱吗,这个说不清的。所以是否要按照劳动法付给雇员加班费,存在很多不确定因素,这个是不现实也是不合理的。 我们很多人其实心理也明白,去给996ICU点一个star并不会让自己摆脱996现状。但我们还是那样做了,这其中有一点抗争精神,但抗争之后我希望大家能够更清楚的看待996这个问题。 我更希望996这个含义可以适当拓宽一点,包含我们工作及工作以外提升自我的时间。 如果公司要求996,分出一些时间做自己的事情,保证工作学习的82配比。如果不要求996,那就尽可能合理分配时间,保证每周有72小时是投入到这个行业里来的。在这段时间里你可以用来提升自我,或者是听我给你们吹水🙃
iOS开发月报#8|201902
- 28 Feb, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。新闻一览 2019年开发人员技能报告 这是HackerRank平台通过对7万多名开发人员做的一项调查,问题从他们认为最有前途的技术到他们在工作中和面试过程中想要的是什么,甚至还有coding过程中听什么音乐最有帮助!一起看下吧。 Tips presentedviewcontroller和presentingViewController 假设从A控制器通过present的方式跳转到了B控制器,那么 A.presentedViewController 就是B控制器; B.presentingViewController 就是A控制器。如果没有对应关系会返回nil 获取类名 //传入实例对象object let typeName = String(describing: type(of: object))隐藏NavigationBar导致手势返回失效 当我们从一个A(NavigationController)界面通过push进入B界面,如果要隐藏B的NavigationBar,那么从B到A的返回手势也会被屏蔽掉。 如果需要加回来我们可以这么做: //1.给需要添加返回手势的ViewController增加UIGestureRecognizerDelegate//2.由当前ViewController接管pop手势 self.navigationController?.interactivePopGestureRecognizer?.delegate = self关于 Decimal 官方对Decimal介绍就一句话A structure representing a base-10 number.表示以10为基数的数的结构体,就是十进制的表示方法。 它可以通过 mantissa * 10 ^ exponent的形式表示任意数值。mantissa(尾数)是最长38位的整数,exponent(指数)是 -128到127的整数。 你也可以把Decimal理解成跟Int, Float, Double一样的数据类型,那有了整形,浮点型数值还会出现Decimal这个类型? 可以看一个例子: let stride: Float = 0.01 var sum: Float = 0 for _ in 1...100 { sum += stride } print(sum)//0.99999934sum的值不是1,这是因为精度的问题,那如果把Float换成Decimal就会完全等于1。 SKProduct的price属性就是Decimal类型 违反5.5条款被打回的经历 在一次有关内购项审核被打回的经历中,遇到了一个新问题,错误描述中提示违反了条款5.5 Developer Code of Conduct。 简单看下这一条Please treat everyone with respect, whether in your responses to App Store reviews, customer support requests, or when communicating with Apple, including your responses in Resolution Center. Do not engage in harassment of any kind, discriminatory practices, intimidation, bullying, and don’t encourage others to engage in any of the above. Customer trust is the cornerstone of the App Store’s success. Apps should never prey on users or attempt to rip-off customers, trick them into making unwanted purchases, force them to share unnecessary data, raise prices in a tricky manner, charge for features or content that are not delivered, or engage in any other manipulative practices within or outside of the app.总结一下就是要尊重用户。 在联系过苹果审核客服之后,被告知是因为在引导过程中弹了两次订阅弹窗(一次订阅,一次freetrial)被拒的。但其实这个流程已经跑了大半年,一直没问题,这次却突然被揪出来了,有点莫名其妙。 可能审核人员认为,这种行为有诱导或者强迫用户付费的嫌疑。由此也可以看出审核人员对项目流程的管理越来越严格了。去掉一个步骤之后再次提交就过审了。 推荐阅读 Swift ABI 稳定对我们到底意味着什么 Swift 社区最近最重大的新闻应该就是 ABI 稳定了。这个话题虽然已经讨论了有一阵子了,但随着 Xcode 10.2 beta 的迭代和 Swift 5 的 release 被提上日程,最终 Swift ABI 稳定能做到什么程度,我们开发者能做些什么,需要做些什么,就变成了一个重要的话题。 一起来看下使用 Swift 5 会对我们的项目有什么影响以及怎么处理。 Void SwiftGG翻译组翻译的Mattt一篇介绍Void的文章。 Void是什么,它和nil有什么区别,这个我们经常用但不一定真的理解的东西,一起通过这篇文章看下吧。 love2.io 程序员的自我修养,作为一个合格的程序员需要具备哪些知识呢? 数据结构、算法、代码架构、项目管理工具、网络知识等?可能还不够,还需要懂点设计、学着当一个合格的PM、学会团队合作、学会学习、提升效率等等;够了吗?你还需要了解一下创业流程、关注自己的身体健康。。。 太多了,不列举了,因为这里都有。 为什么数字下标要从 0 开始而不是 1? 大家都知道数字下标是从0开始的,那为什么是0而不是1呢?说实话在看这篇文章之前我也不能很清楚的说明白,如果你也有这种疑惑就看看这个文章吧。 什么叫有知识 选自罗胖60秒: 1、话说什么叫做有知识?最近我看万维钢老师有一个有趣的定义。他说,考试得了高分,不叫有知识。茶余饭后能高谈阔论,这也不叫有知识。 你发现没有,这些场合下,知识虽然有用,但是这些知识都不太牵扯到具体的得失,所以只是智力游戏。 2、那什么才叫有知识呢?万维钢老师说,只有当局势不明朗,没有人告诉你该怎么办,而错误的判断又会导致一些不良后果,这个时候,如果你因为有知识而敢于拿一个主意,你才算是真有知识。 3、请注意,这不是在说,实用的知识才是知识,而是在说,只有当知识能够帮助你做实际决策的时候,它才是你的知识。 4、这种决策当然可大可小,大到你选择什么样的职业,选择听医生的哪个建议;小到帮你选择看哪一部电影,或者是为孩子挑选买哪一本书。这都是具体的决策。 音视频 苏俄科幻简史:直指星海的冒险时代 最近在机核(gamecores)上听到的一段讲述苏俄科幻的音频,可以当故事听,也可以当一段历史去听,非常有意思。 对于很多人而言,苏俄的科幻文学、电影乃至游戏都是一个比较陌生的概念。事实上从沙俄末期到苏联再到今天的俄罗斯,来自极北之地的斯拉夫人民也曾在科幻这个领域上走了很远很远,取得了惊人的成绩。 Github DoraemonKit 由滴滴团队开源,中文名“哆啦A梦”。 DoraemonKit 是一个功能集合面板,能够让每一个 App 快速接入一些常用的或者你没有实现的一些辅助开发工具、测试效率工具、视觉辅助工具,而且能够完美在 Doraemon 面板中接入你已经实现的与业务紧密耦合的一些非通有的辅助工具,功能强大,接入方便,便于扩展。 git-history 这个项目可以让你在浏览器动态地查看Github库上代码的变动历史。 使用方法非常简单: 1、在对应的文件url中把github.com替换成github.githistory.xyz 2、没有第二步 resume.github.com 这个库可以通过你仓库的活跃事件来生成一份在线简历。使用很简单,直接访问以下网址:https://resume.github.io/?yourusername 需要将yourusername替换成你的用户名,别忘了那个? iOS-Developer-Roadmap 一名iOS开发人员需要具备哪些知识呢?这个库做了比较详尽的总结,以技能树的形式对iOS开发所需知识点,知识面做了整理,我们可以作为参照,查看自己有哪些缺失。文本版本 图片版本(基础版本, 完整版本)
关于消息摘要及对应的Swift实现
- 27 Feb, 2019
开发过程中我们经常会遇到对数据进行一些类似MD5,SHA-256等的处理,那处理的作用是什么以及为什么要这么做,就是今天要讨论的内容。如果你对这些算法的概念已经很熟悉,那就不用继续往下看了,如果你感觉生疏,或者并不能说清楚他们的作用,那就跟着我一起回顾一下吧。从MD5说起 我门从最常见的MD5说起。下面是维基百科对MD5的定义:MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5的概念中出现了两个名词:信息摘要算法 + 密码散列函数。 我们再分别来看这两个概念的含义。 信息摘要算法 消息摘要算法消息摘要是把任意长度的输入揉和而产生长度固定的伪随机输入的算法。 它具有以下特点:无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,一般认为,摘要的最终输出越长,该摘要算法就越安全。变长输入,定长输出。一般地,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。消息摘要函数是单向函数,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息。好的摘要算法,没有人能从中找到“碰撞”,即无法找到两条不同的消息,使它们的摘要相同。从理论上来说,不管使用什么样的摘要算法,必然存在2个不同的消息,对应同样的摘要。因为输入是一个无穷集合,而输出是一个有限集合。但是实际上,很难或者说根本不可能人为的造出具有同样摘要的2个不同消息。 密码散列函数: 密码散列函数(Cryptographic hash function),又译为加密散列函数,是散列函数的一种。它被认为是一种单向函数,也就是说极其难以由散列函数输出的结果,回推输入的数据是什么。 概念梳理 这里比较重要的就是理解信息摘要,它概念就是把一段数据压缩成固定长度的数据,但是我们不能通过压缩后的数据反推原始数据。 有一种应用场景比较能说明问题: 我们在登录的时候,对用户密码进行MD5计算之后再传到服务器,之后数据库显示的用户信息就是MD5之后的16字节字符串。管理员即使能访问用户数据库但他并不能由MD5处理之后的数据得知原始密码。 而客户端这边却可以在输入密码之后,进行MD5计算然后与服务器密码进行比较。 SHA家族 安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。 SHA的出现就是解决MD5不安全的问题,它是一种比MD5更安全的密码散列函数。 SHA家族的算法,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布,是美国的政府标准,其分别是:名称 简介 应用范围 发布时间SHA-0 SHA-1前身 - 1993SHA-1 曾被视为MD5的后继者,但SHA-1的安全性在2000年以后已经不被大多数的加密场景所接受。 广泛 1995SHA-2 包括SHA-224/SHA-256/SHA-384/SHA-512,目前并未被破解 广泛 2001SHA-3 比SHA-2更安全的散列函数,并无替代SHA-2的计划 较小 2015散列函数的安全性 上面说到了MD5是不安全的,不安全意味着可以找到MD5的“碰撞”,就是两个文件可以产生相同的“指纹”。这意味着,当你在网络上使用电子签名签署一份合同后,还可能找到另外一份具有相同签名但内容迥异的合同,这样两份合同的真伪性便无从辨别。 2004年8月17日,在CRYPTO 2004的Rump会议上,王小云,冯登国、来学嘉,和于红波宣布了攻击MD5、SHA-0 和其他杂凑函数的初步结果。他们攻击SHA-0的计算复杂度是2的40次方,这意味着他们的攻击成果比Joux还有其他人所做的更好。 2005年二月,王小云和殷益群、于红波再度发表了对SHA-0破密的算法,可在2的39次方的计算复杂度内就找到碰撞。 2009年,中国科学院的谢涛和冯登国仅用了2的20.96次方的碰撞算法复杂度,破解了MD5的碰撞抵抗,该攻击在普通计算机上运行只需要数秒钟。 2017年荷兰密码学研究小组CWI和Google正式宣布攻破了SHA-1。 摘要的Swift实现 Swift有一个比较成熟的专门处理摘要,加密等安全相关的框架CryptoSwift。 以下是Swift对摘要算法的原生实现,需要引入CommonCrypto框架,这个已经内置到Xcode里面了,列出了对String和Data的摘要扩展。 import Foundation import CommonCryptoenum CryptoAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 var digestLength: Int { var result: Int32 = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } }extension String { var md5: String { return digest(string: self, algorithm: .MD5) } var sha1: String { return digest(string: self, algorithm: .SHA1) } var sha224: String { return digest(string: self, algorithm: .SHA224) } var sha256: String { return digest(string: self, algorithm: .SHA256) } var sha384: String { return digest(string: self, algorithm: .SHA384) } var sha512: String { return digest(string: self, algorithm: .SHA512) } func digest(string: String, algorithm: CryptoAlgorithm) -> String { var result: [CUnsignedChar] let digestLength = Int(algorithm.digestLength) if let cdata = string.cString(using: String.Encoding.utf8) { result = Array(repeating: 0, count: digestLength) switch algorithm { case .MD5: CC_MD5(cdata, CC_LONG(cdata.count-1), &result) case .SHA1: CC_SHA1(cdata, CC_LONG(cdata.count-1), &result) case .SHA224: CC_SHA224(cdata, CC_LONG(cdata.count-1), &result) case .SHA256: CC_SHA256(cdata, CC_LONG(cdata.count-1), &result) case .SHA384: CC_SHA384(cdata, CC_LONG(cdata.count-1), &result) case .SHA512: CC_SHA512(cdata, CC_LONG(cdata.count-1), &result) } } else { fatalError("Nil returned when processing input strings as UTF8") } return (0..<digestLength).reduce("") { $0 + String(format: "%02hhx", result[$1])} } }extension Data { var md5: String { return digest(data: self, algorithm: .MD5) } var sha1: String { return digest(data: self, algorithm: .SHA1) } var sha224: String { return digest(data: self, algorithm: .SHA224) } var sha256: String { return digest(data: self, algorithm: .SHA256) } var sha384: String { return digest(data: self, algorithm: .SHA384) } var sha512: String { return digest(data: self, algorithm: .SHA512) } func digest(data: Data, algorithm: CryptoAlgorithm) -> String { var result: [CUnsignedChar] let digestLength = Int(algorithm.digestLength) let pdata = (data as NSData).bytes result = Array(repeating: 0, count: digestLength) switch algorithm { case .MD5: CC_MD5(pdata, CC_LONG(data.count), &result) case .SHA1: CC_SHA1(pdata, CC_LONG(data.count), &result) case .SHA224: CC_SHA224(pdata, CC_LONG(data.count), &result) case .SHA256: CC_SHA256(pdata, CC_LONG(data.count), &result) case .SHA384: CC_SHA384(pdata, CC_LONG(data.count), &result) case .SHA512: CC_SHA512(pdata, CC_LONG(data.count), &result) } return (0..<digestLength).reduce("") { $0 + String(format: "%02hhx", result[$1])} } }
艰难的图床优化方案(MWeb+PicGo+Github)
博客写作优化 写博客就肯定会遇到插入图片的情况,我之前的做法是先在Boostnote这种Markdown编辑器里面写个草稿,如果有图片就附上图片,然后再粘到简书的web端。当然图片也需要另拖,会生成一个简书的图片链接,相当于把简书作为图床,然后我再把简书排好版的文章发布到自己的博客。。。当然是很麻烦的方式,这种写法持续了大半年之后,开始寻找更高效的写博客的方式。首先客户端,最终选了MWeb。然后是要解决麻烦的图床问题,因为MWeb内嵌了七牛云图传服务,所以申请了七牛云账号,用了一段时间感觉真是如丝般顺滑。 图床历险记 直到我收到了这个:在七牛云上申请的只是测试域名服务,只有一个月的有效期。如果想继续使用就需要绑定自己的域名,而且这个域名还需要备案。 于是我在腾讯云上注册了两年的zhangferry.com这个域名。填写资料时,我看到系统提示域名要进行备案,备案过程中又发现,要有绑定腾讯云服务才能进行备案。??我只是要域名并不需要云服务啊。隐约感觉好像被下套了,需要解决的问题一个套一个。我还怀着侥幸的心理看了下腾讯云服务的价格,用不起用不起。。。 看到提示说如果不备案域名,三个月后会被回收,有点慌。询问客服之后得知,服务器在国内且没有备案的情况下才会被回收,因为我的博客是搭在Github上的,所以可以不备案,松了一口气,域名保住了。但是,不备案七牛云又不干了,what f。。。 纠结一段时间之后我只能放弃七牛云,寻找其他的图床服务,知乎上有一个总结比较全的文章盘点一下免费好用的图床。原来是有很多免费服务的,感觉找到了解决方案。但是,又是一个但是,图床这个东西,免费就意味着不稳定,万一哪天挂了,图片就都丢了。最后文末有总结:如果打算长期稳定使用请优先选择又拍云或者七牛云,如果是存储并不重要的图片可以使用免费不限大小的SM.MS图床。 再结合我对图床的需求:免费+稳定。SM.MS好像是最接近的方案了,虽然SM.MS知名度不算低,但是我还是有点担心它的稳定性。 再继续找,找到了这个PicGo,一个图床管理工具,顺着PicGo(v2.0.3)我找到了Github作为图床这一方案。Github,对啊,我直接就确认了这种方案。虽然有人说Github图片国内访问会慢一些,但是它肯定能保证数据的稳定性啊。这样图床工具和图床服务都找好了,喜大普奔! Github图床配置 说了很多废话,终于到了这个理想图床的配置阶段了,比较简单。 1、建一个仓库 用于存需要上传的图片。这个仓库最好是public的,因为private的仓库,图片链接会带token,这个token又存在过期的问题。 2、获取授权token 在Github的Developer setting界面生一个token。记得保存,因为它只会显示一次。3、配置PicGo 根据图示:4、使用 使用也很方便,PicGo有两种方式: 屏幕截图 截取图片之后,图片会出现在PicGo的待上传列表里面。我们手动点一下会触发上传,上传成功之后,剪贴板会有对应markdown格式的图片文本,直接粘贴可以使用。 固定图片 我们需要将需要使用的图片拖到PicGo的客户端内手动上传,上传成功之后,会自动复制成markdown文本,可以直接粘贴使用。 终于搞定了,撒花!
iOS开发月报#7|201901
- 31 Jan, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。 另外,马上就要过年了,提前祝大家春节快乐!新闻一览 Github宣布私有库免费 Github宣布提供免费且不限量的私有仓库服务,如果协作者超过3个仍需购买付费服务。 喜大普奔啊,得知消息之后,我赶紧就建立一个私有仓库,放一些不想让你们看到的东西🙃 Swift 5 Release Notes for Xcode 10.2 beta Swift5 版本终于发布了,最低可以使用Xcode10.2beta 查看。Swift5 带来了ABI稳定,届时系统将自带Swift动态库,由Swift编译的项目将不会自动把运行库打到包里。 Swift5 具体还有哪些特性,可以参考知识小集团队的翻译版本 Tips Dictionary的merge操作 Dictionary的merge可能平常用的不多,我是在一次bug排查的过程中,发现自己对merge操作理解也有些偏差。这里做个简单梳理。 看其中一种情况: var dictionary = ["a": 1, "b": 2]// Keeping existing value for key "a": dictionary.merge(["a": 3, "c": 4]) { (current, _) in current } // ["b": 2, "a": 1, "c": 4]// Taking the new value for key "a": dictionary.merge(["a": 5, "d": 6]) { (_, new) in new } // ["b": 2, "a": 5, "c": 4, "d": 6]注意尾随闭包中有两个参数,返回第一个参数,表示如果merge操作有重复key值,将保留merge前的value不变。如果闭包返回第二个参数,如果merge有重复key,将更新至最新值。 Dictionary向关的merge函数一共有四个,使用类似: public mutating func merge<S>(_ other: S, uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows where S : Sequence, S.Element == (Key, Value) //上面示例对应方法 public mutating func merge(_ other: [[Key : Value].Key : [Key : Value].Value], uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrowspublic func merging<S>(_ other: S, uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows -> [[Key : Value].Key : [Key : Value].Value] where S : Sequence, S.Element == (Key, Value)public func merging(_ other: [[Key : Value].Key : [Key : Value].Value], uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows -> [[Key : Value].Key : [Key : Value].Value]一些Git操作 删除子目录git 当一个含git的目录包含了其他含git目录时,外部git是不能将内部git纳入版本管理的。有一种解决方案是,移除内部的git仓库。 1、删除内部git cd 需要移除的git目录 rm -rf .git # 或者显示隐藏目录手动删除.git文件夹此时虽然已经删除了.git但是原目录还存在缓存,无法添加到外部git中。 2、删除缓存内容 git rm -r --cached nextgit pull强制覆盖本地文件 当我们需要将某一分支,跟远程对应分支保持一致时,可以做如下操作: git fetch git reset --hard origin/branch_name git pullfatal:refusing to merge unrelated histories 当我们为一个项目关联了一个远程仓库,执行pull的操作时出现 * branch master -> FETCH_HEAD * [new branch] master -> origin/master fatal: refusing to merge unrelated histories这是因为git从2.9.0版本开始,预设行为不允许合并没有共同祖先的分支。如果你非要合并,需要加上--allow-unrelated-histories参数才行: git pull origin master --allow-unrelated-histories几个提高效率的快捷键 最近在熟练使用了几个快捷键后,明显感觉开发效率提高了不少,这里总结了几个常用但不是非常常用的快捷键(Command + C 和 Command V等)。另外Command(或 Cmd)⌘ Shift ⇧ Option(或 Alt)⌥ Control(或 Ctrl)⌃ Power 电源键Xcode Command + Shift + J 文件目录指向当前文件 Command + Shift + O 文件、对象搜索 Command + Shift + F 全局搜索 Command + shift + , 编辑 scheme Command + Control + Left/Right 浏览历史切换 Command + Option + Left/Right 折叠、展开当前代码段 Command + Option + C 显示Commit界面 系统操作 Command + Option + Power 睡眠状态 Power 1.5s睡眠状态 Command + Control + Power 强制Mac重启 适用其他程序 Command + , 打开偏好设置 Command + W 关闭最前面的窗口 Command + Option + W 关闭应用所有窗口 Command + Q 推出应用 Command + M 最小化 Command + Shift + [/] 左右切换应用tab 推荐阅读 未来世界的幸存者 今年市场经济不景气,很多互联网公司经历“裁员潮”,其实不光是换联网行业,其他行业也面临同样的问题。那怎么提高自己的核心竞争力,是每一个从事互联网行业的人都应该考虑的。我们不应该只满足于日常开发,要找准自己的定位,提早做职业规划。另外不断尝试拓展自己的知识面,增加自己的影响力,这样才能让自己在不断变化的市场中立于不败之地。 除了阮老师的这本书,还推荐 卓同学的iOS 2019 隆中对 什么是真正的程序员 作者仿照《小王子》中的情节,通过小printf遇见的不同类型的程序员,最后悟出什么才是真正的程序员! 有两个概念比较有意思:达克效应:能力强的人总是低估自己,能力弱的人总是高估自己。 能够为人们解决真正需要解决的问题的程序员,才是真正的程序员Language Server Protocol 来自SwiftGG翻译组的一篇文章。 苹果公司 在 Swift.org 论坛上宣布,正在着手为 Swift 和 C 语言支持 Language Server Protocol(语言服务器协议,LSP)。 那这意味着什么呢?这可能是苹果自 2014 年将 Swift 作为开源软件发布以来,为 Swift 做出的最重要的决定。它意味着我们可以不必使用Xcode去开发Swift项目,而是可以选择像是Visual Studio, Atom这些IDE。Swift将变成一种更加通用的变成语言。 Hashable / Hasher Hash是程序开发过程中很重要的一个概念,它可以让我们查找集合特定元素的时间复杂度由O(n)降低到O(1)。 这篇文章介绍了Swift中关于哈希的实现,哈希冲突的改变,以及Swift4.1之后关于自动化实现Equatable的相关介绍。 Table View 太複雜?利用 MVVM 和 Protocol 就可以為它重構瘦身! TableView是我们常用的系统部件,随着业务逻辑的不断迭代它内部的逻辑可能会越来越复杂,代码量也越来越多,如果不能很好的组织逻辑架构,这里会变得非常臃肿。 作者利用MVVM pattern,加上一点Protocol技巧,来简化dataSource的工作,把UI和逻辑解耦合,并且最大化这个tableView模组的扩展性。 音视频 临近年关,可能很多人会考虑年后换工作的情况,最近几期的ggtalk也在讨论跳槽这个大家比较关注的话题,希望大家收听之后能够有所收获: 狭义跳槽论:面试官,大厂新人和准备起跳的某 C 广义跳槽论:简洁明了的方法论 附带之前的几期关于升职加薪的节目: 聊聊程序员的升职加薪(上) 聊聊程序员的升职加薪(下) 程序员的春节趣事 今天上班路上听了这期节目,全程非常的欢快。我是今天晚上回家的火车,那些相似的春节趣事我也马上要经历一次了,是需要提前准备下怎么应对了🙃。 以下是官方内容介绍: 快过年了,估计大家也没心情工作学习,准备进入放假状态。严肃的话题放到年后,这期我们请到了袁滚滚、张思琦和老朋友莲叔,围绕着春节轻松愉快的聊一期。 Github 本篇GitHub模块,介绍几个Git和GitHub相关的项目。 Git中文教程 来自geeeeeeeeek的高质量Git中文教程,在GitHub已经获得了1w+的start。对git操作还有疑问的同学可以借助这份教程,完整的梳理一下git相关的知识,或者扫一下git盲区。 Git的奇技淫巧 我们在使用git的时候,有时候会被一些具体的问题难住,这篇文章从具体操作入手,介绍一些我们知道但不一定都用过的git指令。 等等,我也想写一份我自己的Git奇淫技巧了,因为git这个东西是真的庞大。 HelloGitHub 这是一个面向编程新手、热爱编程、对开源社区感兴趣人群的项目,内容以月刊的形式更新发布。内容包括:流行项目、入门级项目、让生活变得更美好的工具、书籍、学习心得笔记、企业级项目等,这些开源项目大多都是非常容易上手、很 Cool,能够让你用很短时间感受到编程的魅力和便捷。从而让大家感受到编程的乐趣,动手开始编程。 GitHub漫游指南 介绍你可能在Github上遇到的所有问题,从如何起一个好名字到如何推广项目,GitHub用户分析,每一个环节都非常详细。无论你处于编程的哪个阶段,我认为你都能从这份指南中获取到灵感和帮助。 README 该文件用来测试和展示书写README的各种markdown语法。GitHub的markdown语法在标准的markdown语法基础上做了扩充,称之为GitHub Flavored Markdown。简称GFM,GFM在GitHub上有广泛应用,除了README文件外,issues和wiki均支持markdown语法。
iOS开发月报#6|201812
- 02 Jan, 2019
大事件 这个月的大事件是高通与苹果之间的专利大战。高通分别在中国和德国对苹果公司提起专利诉讼,结果均胜。苹果将在中国禁售iPhoneX及之前的机型。在德国则是全面禁售。加上新品手机的创新力度不够,导致苹果股票持续走低,四个月前,苹果还是首个突破万亿美元市值的上市公司,到现在股价已经下跌了 36%,市值只有 7160 亿美元,几个月内市值损失接近 3000 亿美元。Tips 更换启动图不生效 现象:更换启动图,不生效,即使删除旧图,运行程序启动图仍然是之前的版本。 尝试了删除app重装,clean、重启Xcode,删除DriverData,还试过挪图片位置,改名字,均无效。最后尝试了重启手机->再安装才正常。 问题分析:这个是系统问题,为了加快程序启动的速度系统会把启动图做个缓存,之后每次启动是加载缓存启动图,所以才会出现修改不生效问题。但我们也并不知道系统会什么时候更新启动图,这个就有点尴尬😓 stackoverflow上有关于这个问题的讨论: iOS Keeping old launch screen and app icon after update 有人说这个不影响线上版本,从上线之后的测试来看,大部分用户没有受到影响,但还是出现了部分手机升级却未更换启动图的问题。 因为是系统层面控制,没有太好的解决方案,如果有人遇到这个情况,需要注意一下。 赋值权限 在Mac 系统的终端上修改文件权限使用的是 Linux 中的 chmod 命令: r:可读,表示可以读取内容 w:可写,表示可以编辑内容,但是不可以删除文件 x: 可执行,表示可以执行文件。 r:4 w:2 x:1 rwx=4+2+1=7$ chmod 777 file这行命令的意思就是:文件所有者权限是7,同用户组的权限是7,其他非本地用户组的权限是7。同理可以出现这样的命令 chmod740.barshrc 再讲两个: chmod u=rwx, go=rx .barshrc 这个命令中u表示拥有者,g表示group中的用户,o表示others,和上面的想对应。注意go=rx,这里go是拼接起来的,表示g和o的用户有读和执行的权限。 $ chmod a+w .barshrc表示所有的用户(a表示all)增加写的权限。 App图片瘦身 随着项目版本的不断迭代,会很容易积累越来越多的图片,对这些图片我们有两个主要方面可以优化: 1、删除不用的图片 我们需要借助一个工具LSUnusedResources 它可以检索项目中未使用的图片资源,我们可以指定检索图片的路径,后缀进行过滤。 注意:无法区分png序列的引用。最好手动删除 2、图片压缩 可以通过ImageOptim,对图片进行无损压缩。使用时只需将要压缩的文件拖到程序中即可。 如果图片较多时,可以借助其命令行工具: /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $file_name对应的shell脚本: # 处理文件名出现空格问题 MY_IFS=$IFS IFS=$'\n' for file_name in $(find "$1" -name "*.png" -type f); do echo $file_name /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $file_name done IFS=$MY_IFS该工具还有一个Sketch插件:Plugin for Sketch ,可以在输出icon的时候直接进行压缩优化。 后台音频耗电问题 **功能:**一段助眠音频可以后台播放,播放完成后如果返回app,会跳到对应的完成界面。 **问题:**有用户反馈,听音频睡着了,早上醒来,电量掉了很多(40%)。 调研: 使用Xcode中 Debug Navigator栏的Energy Debug Gauge(需真机)测试。播放完成4分钟左右的后台耗电状态如下:分析可知虽然此时app停止了所有行为,但是生命周期仍处于Background状态,耗电量也还是存在的。暂时认为问题是客户端一直处于Background模式,并未被Suspend,导致一直被系统维护着,所以一直有耗电情况。 再一次查看苹果关于后台模式的文档:When the UIBackgroundModes key contains the audio value, the system’s media frameworks automatically prevent the corresponding app from being suspended when it moves to the background. As long as it is playing audio or video content or recording audio content, the app continues to run in the background. However, if recording or playback stops, the system suspends the app.可知,只要播放器未被销毁,就会一直占用后台模式! 解决方案: 在播放完成时销毁播放器。 检验: 再次观察播放音频完成一段时间之后的后台耗电情况:此时app被挂起,耗电量基本为0,唤醒app,仍能正常工作。问题解决。 推荐阅读 尝试分模块整理推荐阅读的内容 iOS开发过程中的设计模式设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。以上摘自菜鸟教程 Swift中的策略模式 从一个小例子入手,讲解策略模式在swift中的使用。 Swift 中的设计模式 #1 工厂方法与单例方法 Swift 中的设计模式 #2 观察者模式与备忘录模式 Swift 中的设计模式 #3 外观模式与适配器模式 SwiftGG翻译组翻译的设计模式系列文章。 设计模式资料整理 应该是目前总结最全的适合iOS开发的设计模式资料汇总。 iOS逆向iOS应用逆向工程,是指从目标应用的界面及功能表现入手,使用不同的工具和理论知识去分析其实现原理,得出应用的代码结构、整体设计、功能实现、执行流程等,然后利用iOS的系统知识和语言特性,借鉴或修改原有实现流程的技术。推荐两篇掘金上关于iOS逆向的文章: TikTok(抖音国际版)逆向,全球的小姐姐们,我来啦! 逆向 Mac 应用 Bartender 这两篇都是通过具体实例入手,讲解逆向常用的工具和一些逆向的思路,非常适合新手。最好跟着介绍实际操作一番,第一次成功逆向一个项目之后,那种爽歪歪的感觉,你懂的! 另外推荐一位逆向领域的牛人刘培庆(博客,github),了解逆向的同学大多应该都不陌生,iOS方面的逆向优先参照 MonkeyDev。 刘总今年出了新书《iOS应用逆向与安全》,非常适合对逆向有兴趣的同学。 利用脚本提高工作效率脚本语言(英语:Scripting language)是为了缩短传统的“编写、编译、链接、运行”(edit-compile-link-run)过程而创建的计算机编程语言。早期的脚本语言经常被称为批处理语言或工作控制语言。一个脚本通常是解释运行而非编译。脚本语言通常都有简单、易学、易用的特性,目的就是希望能让程序员快速完成程序的编写工作AppleScript入门:探索macOS自动化 文章选自少数派,讲述了:什么是 AppleScript? 我使用 AppleScript 的情境 AppleScript 基础语法 AppleScript suite 之外的解法:模拟键鼠非常清晰详细的AppleScript入门文章。 如何提高工作效率 - 自动化篇 文章选自掘金,串联Shell Script, Apple Script, Automator, Alfred & WorkFlows,告诉你如果灵活应用,达到利用脚本提高工作效率的目的。 我写了一个利用AppleScript和Alfred的WorkFlows在命令行打开指定路径的工具。on run argv tell application "Finder" -- get selection path set pathFile to selection as text set pathFile to get POSIX path of pathFile -- fix space problem in the directory set pathFile to quoted form of pathFile tell application "Terminal" activate tell window 1 do script "cd " & pathFile end tell end tell end tell end run使用方法是:点击选中文件夹,按下热键CMD + T。 音视频 创业那点事:听莲叔和羊叔回忆峥嵘岁月 莲叔和羊叔是 SwiftGG 翻译组的真·大佬。成绩好,学历高,思维缜密,能力很强。听过《升职加薪》那期节目的朋友应该都有印象。作为标准的技术人才,在创业大潮中自然也想试试身手,打拼一番。 和 Cee 聊聊如何拿 Google Offer 翻译组里真是人才辈出啊,前几天 Cee 在群里说了个好消息,他拿到了美国 Google 的 Offer。群友们纷纷表示祝贺并进行了隆重的认哥仪式,将 SwiftGG 诞生以来第一个 GG(哥哥) 称号颁发给我们公认的大哥 Cee。 以上两个音频来自于(ggtalk)[https://talk.swift.gg/],也是我最近上下班路上用来消遣的音频节目,墙裂推荐! 计算机科学速成课 由Carrie Anne Philbin主讲,Carrie Anne Philbin是一名计算机科学教师和作家。她是树莓派基金会的教育主管,也是学校(CAS)多样性和包容组的主席。她为青少年写了电脑书《树莓派历险记》。特意介绍是因为我感觉这个课程因为这个主讲人的个人魅力而趣味十足😄。不管你是不是计算机专业的学生,看过之后应该都能有所收获。 Github 12306ForMac 以前要么开Windows虚拟机,要么使用官方Web,现在可以使用12306ForMac订票助手啦。希望对大家有所帮助! LongestCocoa SoWhatIsTheLongestMethodOrConstantNamesInCocoaFramework? (那么,Cocoa框架中最长的方法名或常量名是什么?)答案是:outputImageProviderFromBufferWithPixelFormat:pixelsWide:pixelsHigh:baseAddress:bytesPerRow:releaseCallback:releaseContext:colorSpace:shouldColorMatch:kCMSampleBufferConduitNotificationParameter_UpcomingOutputPTSRangeMayOverlapQueuedOutputPTSRange这个项目有一定的调侃意味,so what? 我选择swift🤣
2018年总结
- 02 Jan, 2019
写的有点晚了,因为一些事情耽搁,本来想干脆不写了。但有股力量不断催促着自己要写下来,于是就有了这篇文章。今年对我是意义重大的一年,从几个方面说吧,工作,生活和2019年的目标。工作3月底进了新公司,换工作主要原因是在之前的公司遇到了上升瓶颈,对当时的我来说一直待下去也无法看到清晰的未来。从新公司开始,今年变化了很多,也成长了不少。开发语言 首先技术语言从OC转到了Swift,也是首次用Swift进行项目开发,并没有太大障碍。因为是经手新项目,所有也没有太多历史包袱(兼容OC),使用Swift+RxSwift+Clean architecture的进行开发。我对Swift的感觉还是很好的,值得大家花时间去学去适应,毕竟Swfit代表着未来,是苹果推荐语言。 都9102年了,ABI稳定版Swift5.0马上就要来了,没有什么理由不使用它了,不是吗。 开发工具 一直都是使用MacBook Pro+Xcode的环境进行开发,之后使用fastlane进行打包,每次编译打包都将近40分钟。为了提高开发效率,开始考虑黑苹果方案。配置一台i7处理器+16G内存的台式机,4000左右,开始搞黑苹果。 附送一个教程传送门,以下是黑苹果配置:有两个需要注意的地方: 1、显卡无法驱动。 使用免驱显卡。 当时装的时候Mojave版本还没有正式版,想升级时才发现macOS10.14及之后的版本将不再识别NVIDIA的显卡,除非换成AMD显卡,遂放弃升级。 2、网卡问题 可以在天猫购买一个支持黑苹果的网卡,收到货之后向店主要个网卡驱动,之后就能正常wifi上网啦。其他如蓝牙问题因为基本不用,没有搞,也可以使用黑苹果专用外设解决。 博客 2018年共写了12篇播放,看了下日期,第一篇是从7月份开始写的,也就是说这12篇都是后半年写出来的。 关于写博客这件事,之前是一直有种迷茫的状态,一查就懂的不想写,太复杂的又不愿花时间整理。后来慢慢不知怎么地就又找到了点感觉,其实也没有什么规律,只是写的时候不那么被动了。 之前博客都是发的简书,后来觉得简书环境不太好了,不光是对程序员不友好了,简书整体也脱离了那种小清新的感觉,不知道从那个版本开始简书客户端UI突变,整个头条风,就不爱了。转到了掘金,不过之前的文章并没有搬运过来。 如果有留意我最近博客的话,可以看到我正在弄一个iOS月报的东西。这个想法的产生是因为,每隔一段时间都会积攒一些知识点,一些学习感悟,因为是零碎的东西,又不便于整合成一篇博客。后来关注到老司机周报、知识小集、阮一峰的网络日志这几个栏目,收到启发,于是整合出iOS开发月报这种形式。毕竟一个月整理一次对我来说是一件可以做到的事。因为是一个人在做,每个月接触的东西也都会不一样,可能形式会有大大小小的差别。配图不好,语言不通顺也请大家多包涵,如果有什么意见或者好的内容可以给我推荐。我会持续努力的,争取做好这个栏目,给大家带来更多有质量的东西。 生活那些经历 翻了下相册来给过去的一年做下汇总: 在17年的最后一天跟一个团体,参加跨年夜行活动,从奥林匹克公园走到天安门广场,看1月1号的升旗仪式。 1月份在中关村创业大街跟一个高中时的朋友一起去听了哈默的如何成为斜杆青年的讲座。 同样是一月开始追《海贼》,从第一话开始,一直都是看的漫画,到顶上战争知道这将是场精彩的对决,转为动漫,之后就一直是动漫了,目前看到大妈篇打卡二。 一月份还参加了极客公园创新大会,见到了傅盛、王小川、黎万强、李彦宏、罗振宇等一众大佬本人。 3月份裸辞,在家闲置了半个月。 跟女朋友从苹果园开始骑行,去了位于门头沟的京西古道。到了地方,视野所见游客不足十人,人家都快下班了,说要关门不推荐我们进去,于是歇了会就原路返回了。 4月份进了新公司。 第二次参加北京国际半程马拉松,提前一个月开始准备,期间每天早上两公里,周末五公里。成绩是2小时20分钟。 5月份跟女朋友去了青岛,当时天气还比较冷,只记得吃了两次九龙餐厅的饭,真香。 7月份跟女朋友去了秦皇岛的孤独图书馆,她预约了一个月才预约上。到了之后感觉一般,图书馆很小,书也不多,不让拍照。图书馆旁边是阿那亚教堂,也感觉一般吧,不推荐去。 还去了十渡,玩了玻璃栈道和漂流。这个非常推荐。 8月份对我来说是灾难性的,母亲被一场突如其来的疾病夺去了年轻的生命。我无助绝望过,一蹶不振过,也悲观厌世过,好在女朋友陪在身边,给了我足够的陪伴与安慰,才让我重新振作起来,真的非常感谢她。后来我不断的回忆与后悔,为什么没有坚持每年对父母做一个体检,我明明是知道的。 所以我也再次郑重建议大家,每年带父母至少体检一次,多陪陪他们,真的这没那么难。 之后的下半年除了跟我爸,我女朋友分别爬了两次香山,基本就没什么活动了。 关于理财 16年接触基金,17年开始定投,一开始对定投的理解就是分摊风险,比股票稳定,大概率会盈利。18年年初收益率达到15%时,当时定投已经达到我的设定预期。但我却仍坚定的相信,继续持有会获得更大收益。之后经历中美贸易战,股市重挫,之后持续的经济低迷,我都相信会涨回来,直到年末基金亏损达30%。我才意识到,当初自己那盲目的乐观是多么不正确。 后来听了一些大佬对19年经济形势的预测,大概率经济不会转好。所以基金定投这个事也会停止下来,这对我来说是一个教训,以后对理财会更加谨慎。 关于阅读 今年阅读量比较小,就这几本: 《Objective-C高级编程》 《Swift面向协议编程》 《大话设计模式》 《人类群星闪耀时》 《巨人的陨落》 《精进》 《钝感力》 《把生命浪费在美好的事物上》 《你一定爱的欧洲极简史》 正在看的有: 《重构》 《未来简史》 《iOS逆向与安全》 计划要看的是: 《代码的未来》 《学习之道》 《老人与海》 《天才在左疯子在右》 《解忧杂货铺》 《怪诞行为学》 《百年孤独》 《菊与刀》 2019年OKRO1:精进技术栈 KR1:刷50道LeetCode题,写5篇左右博客总结解题思路。 KR2:做3个iOS、mac客户端的逆向。 KR3:翻译5篇Swift相关技术文章。 KR4:学习前端知识,给博客换个面貌。 KR5:开发一款微习惯的APP KR6:博客输出24篇 O2:攒更多的钱 KR1:工资外收入达到1000 KR2:总收入达到买房需要的首付一半 O3:运动健身 KR1:半程马拉松跑到2小时以内 KR2:咕咚累积里程达到500公里
iOS开发月报#5|201811
- 01 Dec, 2018
大事件 11月27号,苹果AppStore下架包括拼多多、搜狗地图、讯飞阅读和悦跑圈在内的共718个App。据称下架原因是触发了AppStroe审核指南的2.5.2条例,即使用了热更新技术。去年6月份,苹果层因热更新下架数万款App。Tips 图片渲染开销 我们知道,解压后的图片是由无数像素数据组成。每个像素点通常包括红、绿、蓝和 alpha 数据,每个值都是 8 位(0–255),因此一个像素通常会占用 4 个字节(32 bit per pixel。少数专业的 app 可能会用更大的空间来表示色深,消耗的内存会相应线性增加)。 下面我们来计算一些通常的图片开销: 普通图片大小,如 500 * 600 * 32bpp = 1MB 跟 iPhone X 屏幕一样大的:1125 * 2436 * 32bpp = 10MB 即刻中允许最大的图片,总像素不超过1500w:15000000 * 32bpp = 57MB 有了大致的概念,以后看到一张图能简单预估,大概会吃掉多少内存。 关于iOS开发证书在钥匙串没有秘钥的问题 当前现象只需要将证书拖到登录,即可生成秘钥APP内切换语言 APP内切换语言有一种比较简单的实现方式,实现步骤是: 1、存储语言状态 2、刷新KeyWindow.rootViewController 3、刷新Bundle 4、切换语言对应的更新操作 实现过程: //1、切换语言,AppleLanguages对应内容是数组,语言符合可以通过Bundle.main.localizations查看 //切换至繁体中文,存储状态 UserDefaults.standard.set(["zh-Hant"], forKey: "AppleLanguages") //2、刷新keyWindow,createRootViewController为创建rootViewController的方法 UIApplication.shared.keyWindow?.rootViewController = Application.shared.createRootViewController()//3、更换bundle //在切换语言的时候,我们需要执行一下,Bundle的exchangeBundle方法 extension Bundle { static func getLocalizedBundle() -> Bundle { if let languages = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String], let lan = languages.first, let path = Bundle.main.path(forResource: lan, ofType: "lproj"), let bundle = Bundle.init(path: path){ return bundle } else { return Bundle.main } } static func exchangeBundle(){ //替换Bundle.main为自定义的BundleLocal object_setClass(Bundle.main, BundleLocal.self) } }class BundleLocal: Bundle { override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String { return Bundle.getLocalizedBundle().localizedString(forKey: key, value: value, table: tableName) } }//4、其他更新操作,如接口,UI界面测试内购时长 当我们测试内购时,需要考虑内购到期的情况。比如我测试购买了一个月的服务,那么购买到期的情况呢,不能傻傻地等一个月时间吧? 其实苹果给出了不同内购时长在Debug情况下对应的测试时间。AVPlayer进入前台自动播放 在我们不进行任何前后台设置的情况下,AVPlayer进入后台,会自动暂停播放。再次回到前台时,播放器会有一定概率自动恢复播放(多大概率未知,测试得出的结论)。 如果这个恢复播放不是我们需要的,或者我们需要避免这种不确定的情况发生,我们可以手动管理这个过程: NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: self, queue: nil) { (notification) in self.player.pause() }NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: self, queue: nil) { (notification) in //根据需要是否需要恢复播放 if shouleRecoverPlayer { self.player.play() } else { self.player.pause() }}iOS12.1关于tabbar的一个bug 触发条件:使用 UITabBarController + UINavigationController 组合 UITabBar带半透明效果,isTranslucent 属性为 true UIViewController的 hidesBottomBarWhenPushed 属性为 true 通过导航栏返回上一页时(导航栏返回按钮 or 屏幕左侧的滑动返回手势)问题现象:经确认是iOS12.1上的一个bug。 解决方案: //MARK: - fix ios12.1 tabbar bug let tabbar = UITabBar.appearance() tabbar.isTranslucent = false详细内容可以参考: 修复 iOS12.1 UITabBar 布局位移bug - ZHFDBK的博客 - CSDN博客 swift - UITabBar items jumping on back navigation on iOS 12.1 - Stack Overflow 推荐内容 重构Swift中的Identifiers 面对越来越多的identifier,改如何优雅地处理? 文中讲了一些UITableViewCell、UICollectionViewCell、segues、UIStoryboards、UIViewController中关于identifiers的重构方法。 The End Is Near for Mobile Apps 移动应用的末日将要来临? Medium科技板块最近比较火的一篇文章,分上下两篇,讲述移动应用的前景和趋势。 Realm学院 这是Realm建立的一个用于学习编程技术的网站,主要方式是视频,内容配有字幕,并且都全部整理成文章。涵盖范围有Realm, Android, iOS, JavaScript, Architecture等。 文章质量非常高! 开发利器 KTVHTTPCache 应该是iOS端目前最好的流媒体缓存框架了。 Realm Realm是一个开源的对象数据库管理系统,最初用于移动(Android/iOS),也可用于Xamarin或React Native等平台,等平台,包括桌面应用(Windows),并获得Apache许可。 对比FMDB,他的使用成本,维护成本都更低。 uTools uTools是一个极简、插件化、跨平台的现代化桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。 通过快捷键(默认alt+space)就可以快速呼出这个搜索框。它相当聪明,可以支持输入、拖拽、自动粘贴等作为输入源,相应的插件也早已准备就绪,统一的设计风格和操作方式,助你高效的得到正确的结果。 当你熟悉它后,能够为你节约大量时间,让你可以更加专注的改变世界。 对比Alfred,他的不同就在于免费,使用简单,更符合国人的习惯。
AVPlayer支持的视频格式
发现很多人对视频格式存在一些误解,之前写的一篇文章讲AVPlayer的支持格式也有一些问题,所以这里单独出一篇文章讲一下,希望大家能明白。基本概念 一个在线视频能够播放,大致是经过了如下步骤:可以总结为:拉数据->解协议->解封装->音视频解码->音视频同步->播放。 下面就针对这几个概念一一做下解释: 播放协议 一般点播采用HTTP,而直播的话,大部分还是采用RTMP或者私有协议,原因是延时会比较小,RTMP本身也是为了直播设计的。常见的流媒体协议:简写 全称 推出机构 目前使用领域HLS HTTP Live Streaming 苹果 多应用于苹果RTP 实时传输协议 IETF 范围较广RTCP 实时传输控制协议 IETF 范围较广RTSP 实时串流协议 RealNetworks等 范围较广RTMP 实时消息协议 Adobe 较流行MMS 串流媒体协议 Microsoft 范围较广RTP/RTSP/RTCP的区别RTSP发起/终结流媒体、RTP传输流媒体数据 、RTCP对RTP进行控制,同步。HLS(Http Live Streaming) HLS是苹果推出,实现的基于HTTP的流媒体传输协议: 优点: 1、通过m3u8索引文件可实现针对当前浏览设备的智能选择播放源, 2、通过m3u8索引文件可实现添加备份索引文件,防止服务器崩溃视频播放失败 3、和http视频一样 不需要太多服务器额外配置 缺点: 1、并非真正实时视频,30s左右时间差 2、需要视频处理 3、因为需要请求索引文件(ts视频文件)请求次数相对较多,对服务器负载较大 视频编码 所谓视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。它是一种为了减少视频体积,同时保证画面质量的压缩技术,常见的视频编码有:名称 推出机构 推出时间 目前使用领域HEVC(H.265) MPEG/ITU-T 2013 研发中H.264 MPEG/ITU-T 2003 各个领域MPEG4 MPEG 2001 不温不火MPEG2 MPEG 1994 数字电视XviD OpenDivX 2002 流行VP9 Google 2013 研发中VP8 Google 2008 不普及VC-1 Microsoft Inc. 2006 微软H.264H.264又叫AVC,是国际标准化组织(ISO)和国际电信联盟(ITU)共同提出的继MPEG4之后的新一代数字视频压缩格式,它集合了H.263和MPEG4的优点,拥有更高的数据压缩比。在同等的图像质量条件下,H.264的数据压缩比能比H.263高2倍,比MPEG-4高1.5倍。 也是目前苹果支持最好的编码格式。 音频编码 和视频编码类似,音频编码的作用是减少音频中的冗余,同时在保证一定音频质量的条件下作的压缩处理。名称 推出机构 推出时间 目前使用领域AAC MPEG 1997 流媒体AC-3 Dolby Inc. 1992 DVD, 数字电视MP3 MPEG 1993 各个领域(旧)WMA Microsoft Inc. 1999 WindowsAACAAC(高级音频编码技术 Advanced Audio Coding),出现于1997年,是基于MPEG-2的音频编码技术。由Fraunhofer IIS、杜比、苹果、AT&T、索尼等公司共同开发,以取代mp3格式。2000年,MPEG-4标准出台,AAC从新整合了其特性,故现又称MPEG-4 AAC,即m4a。苹果的CoreAudio对AAC有较好的支持。封装格式 我们常见的视频格式MP4, AVI, RMVB, 3GP, MKV都是指视频的封装格式,大部分情况就是视频文件的后缀。常见的组合方式有:名称 简介 常用编码格式 扩展名Flash Video 由Adobe Flash延伸出来的的一种流行网络视频封装格式。随着视频网站的丰富,这个格式已经非常普及。 H.264+MP3 flvAVI(Audio Video Interleave) 比较早的AVI是微软开发的。其含义是Audio Video Interactive,就是把视频和音频编码混合在一起存储。AVI也是最长寿的格式,已经存在10余年了,虽然发布过改版(V2.0于1996年发布),但已显老态。AVI格式上限制比较多,只能有一个视频轨道和一个音频轨道(现在有非标准插件可加入最多两个音频轨道),还可以有一些附加轨道,如文字等。AVI格式不提供任何控制功能。 Xvid+MP3 aviWMV(Windows Media Video) 同样是微软开发的一组数字视频编解码格式的通称,ASF(Advanced Systems Format)是其封装格式。ASF封装的WMV档具有“数字版权保护”功能。 VC-1+WMA wmv/asfMPEG(Moving Picture Experts Group) 是一个国际标准化组织(ISO)认可的媒体封装形式,受到大部分机器的支持。其存储方式多样,可以适应不同的应用环境。MPEG-4档的档容器格式在Part 1(mux)、14(asp)、15(avc)等中规定。MPEG的控制功能丰富,可以有多个视频(即角度)、音轨、字幕(位图字幕)等等。MPEG的一个简化版本3GP还广泛的用于准3G手机上。 H.264+AAC,H263+AAC dat(VCD),vob(DVD), mp4, 3gpMatroska 是一种新的多媒体封装格式,这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个Matroska Media档内。它也是其中一种开放源代码的多媒体封装格式。Matroska同时还可以提供非常好的交互功能,而且比MPEG更方便、强大。 各种编码格式的组合 mkvReal Video Real Media(RM) 是由RealNetworks开发的一种档容器。它通常只能容纳Real Video和Real Audio编码的媒体。该档带有一定的交互功能,允许编写脚本以控制播放。RM,尤其是可变比特率的RMVB格式,没有复杂的Profile/Level,制作起来较H.264视频格式简单,非常受到网络上传者的欢迎。此外很多人仍有RMVB体积小高质量的错误认知,这个不太正确的观念也导致很多人倾向使用rmvb,事实上在相同码率下,rmvb编码和H.264这个高度压缩的视频编码相比,体积会较大。 RealVideo+RealAudio rm/rmvbQuickTime File Format 是由苹果公司开发的容器。1998年2月11日,国际标准化组织(ISO)认可QuickTime文件格式作为MPEG-4标准的基础。QuickTime可存储的内容相当丰富,除了视频、音频以外还可支持图片、文字(文本字幕)等。 H.264+AAC mov, qtMP4 mp4格式是H.264编码指定使用的标准封装格式,3GP是MP4格式的一种简化版本,减少了储存空间和较低的频宽需求,让手机上有限的储存空间可以使用。 实际上这些封装格式对应的音频视频编码格式也不是固定的,就拿MP4来说,常见的MP4是由H.264+AAC封装,但是也由Xvid+AAC编码的可能。如果解码器不支持Xvid,则可能会出现无法播放,或者播放播放过程有声音无画面的情况。 苹果支持哪些音视频编码格式 我们可以在手机介绍界面,找到手机支持的视频格式iPhone - Compare Models - Apple 这里我们可以找到对应iPhone7支持的视频编码格式:Video formats supported: HEVC, H.264, MPEG-4 Part 2, and Motion JPEGHEVC 又叫H.265,iOS11+A9芯片才开始对HEVC的支持,iPhone6s及以前的设备不支持HEVC解码。 音频格式用红框标出来了,内容较多,可自行对比。 AVPlayer支持哪些视频格式 苹果设备支持音视频格式并不是就代表AVPlayer也支持那么多格式,确定AVPlayer的支持格式,我们可以查看AVKit中的一个API: //展示当前支持的音视频格式 let asset = AVURLAsset.audiovisualTypes() //打印asset可以得到(已经转过展示格式) asset type ( "audio/aacp", "video/3gpp2", "audio/mpeg3", "audio/mp3", "audio/x-caf", "audio/mpeg", "video/quicktime", "audio/x-mpeg3", "video/mp4", "audio/wav", "video/avi", "audio/scpls", "audio/mp4", "audio/x-mpg", "video/x-m4v", "audio/x-wav", "audio/x-aiff", "application/vnd.apple.mpegurl", "video/3gpp", "text/vtt", "audio/x-mpeg", "audio/wave", "audio/x-m4r", "audio/x-mp3", "audio/AMR", "audio/aiff", "audio/3gpp2", "audio/aac", "audio/mpg", "audio/mpegurl", "audio/x-m4b", "application/mp4", "audio/x-m4p", "audio/x-scpls", "audio/x-mpegurl", "audio/x-aac", "audio/3gpp", "audio/basic", "audio/x-m4a", "application/x-mpegurl" )还有一个方式用来判断当前格式是否可播: //An extended MIME type string such as video/3gpp2; codecs="mp4v.20.9, mp4a.E1" or audio/aac; codecs="mp4a.E1". let playable: Bool = AVURLAsset.isPlayableExtendedMIMEType("video/3gpp2; codecs=\"mp4v.20.9, mp4a.E1\"")这里的MIMEType可以在这里找到 System-Declared Uniform Type Identifiers 总结 由此我们可以下一个总结,AVPlayer支持的: 视频编码格式:H.264、HEVC(iPhone7及以后设备)、MPEG-4。 视频格式(封装格式):.mp4、.mov、.m4v、.3gp、.avi等。 如果想支持更多的视频格式,可以使用使用第三方的框架,常用的视频编码和解码框架有VLC和ffmpeg。 参考文献 视频文件格式 - 维基百科,自由的百科全书 音频编码格式的比较 - 维基百科,自由的百科全书
iOS开发月报#4|201810
- 01 Nov, 2018
记录本月开发遇到的知识点,小tips,和bug总结。 大事件 新版iPad Pro、MacBook Air、Mac mini发布,全线涨价,但是真香。。。Tips 适配swift4.2 1、利用xcode快速迁移 升级到Xcode10之后,我们打开项目会出现如下提示,点击会有一个版本升级窗口,如果你的的项目包含一些第三方库的话,第三方库的选型也会出现在上面:默认勾选第三方库,但是我们适配的时候不应该让Xcode去自动检索第三方库代码。只对我们的app进行代码迁移就够了。 适配完后可以在这里查看我们当前的swift版本:对于第三方库,如果都适配了swift4.2,那么更新到对应版本就行了。如果有没适配的,可以通过制定版本的方式解决冲突,在Podfile文件末尾添加如下代码: post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '4.0' end end end属性访问权限 swift中提供的访问权限关键词由低到高有以下五种: private < fileprivate < internal < public < open 其中internal是Swift中的默认控制级,一下介绍了这几种关键字的区别:private:当前作用域或者当前类中访问。 fileprivate:表示代码可以在当前文件中访问。 internal:在当前target中访问 public:可跨target使用,但不能被集成或者重写。 open:可跨target使用,允许被集成或者重写。对于一个严格的项目来说,精确的最小化访问控制级别对于代码的维护来说是很重要的。能用public的就别用open。 修改文件权限 当我们执行某些命令行操作,收到如下提示时: Linking /usr/local/Cellar/the_silver_searcher/2.1.0... Error: Could not symlink etc/bash_completion.d/ag.bashcomp.sh /usr/local/etc/bash_completion.d is not writable.就表明我们对需要修改的文件权限不够,我们可以给文件加上修改的权限: sudo chown -R $(whoami) /usr/local/etc/bash_completion.d其中-R表示对目前目录下的所有文件与子目录进行相同的拥有者变更(即以递回的方式逐个变更) 默认关键字 public static let `default` = ImageCache(name: "default")default是默认关键字,如果使用要加单斜号。 tabbar手动跳转 func tabBarController(UITabBarController, didSelect: UIViewController)In iOS v3.0 and later, the tab bar controller calls this method regardless of whether the selected view controller changed. In addition, it is called only in response to user taps in the tab bar and is not called when your code changes the tab bar contents programmatically. In versions of iOS prior to version 3.0, this method is called only when the selected view controller actually changes. In other words, it is not called when the same view controller is selected. In addition, the method was called for both programmatic and user-initiated changes to the selected view controller.tabbar的didSelect代理方法,只有在手动点击的时候才会触发。通过代码跳转是不会触发的。 自定义tabbar动画 // 当点击tabBar的时候,自动执行该代理方法(不需要手动设置代理) override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { for view in tabBar.subviews { //控制view实现各种动画效果 } }NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END 从xcode6.3开始,为了能让OC也能表示swift的?(optional)和!功能,增加了对对象的可选指定。指定属性是否可选,可以: @property (nonatomic, copy, nonnull) NSString * tickets; //或者 @property (nonatomic, copy) NSString * __nonnull tickets;如果属性多了,每一个都这么写会很麻烦,苹果增加了一对新的宏命令,就是NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。放在里面的对象就相当于都增加了nonnull命令,nonull为默认值。 一个自定义collectionView布局的bug bug描述: NSInternalInconsistencyExceptionUICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x280d2b200> {length = 2, path = 1 - 5}原因: layoutAttributesForElementsInRect返回的UICollectionViewLayoutAttributes数组有indexPath没有被 [NSIndexPath indexPathForRow:numberOfSection]覆盖。 当数据量增加时不会出问题,当数量减少时出现。有人反映这是iOS10的bug,但实际上,我拿iOS10模拟器跑并没有问题,反而是在崩溃后台看到是iOS12的用户上报的。那究竟什么原因不详,附stackoverflow(iOS 10 bug: UICollectionView received layout attributes for a cell with an index path that does not exist - Stack Overflow]地址。 解决方案: 当我们自定义layout时,需要清除UICollectionViewLayoutAttributes的缓存 //方案一: 在自定义layout的类里 override func prepare() { super.prepare() attributesArr.removeAll() } //方案二:在collectionview刷新出 collectionView.reloaData() collectionView.collectionViewLayout.invalidateLayout()配置git SSH 1、设置git的user name和email $ git config --global user.name "username" $ git config --global user.email "username@gmail.com"2、生成秘钥 $ ssh-keygen -t rsa -C "username@gmail.com"如果不需要设置密码,连按三个回车。最后得到了两个文件:id_rsa和id_rsa.pub。 3、添加秘钥到ssh-agent中 $ ssh-add ~/.ssh/id_rsa4、复制公钥内容到剪贴板 $ pbcopy < ~/.ssh/id_rsa.pub5、登录git仓库(Github或者Bitbucket)将公钥内容粘贴到指定配置位置。 Github地址为:https://github.com/settings/keys Apple Watch开发注意事项 1、watch没有UIKit,对于UI的操作只能通过storyboard进行 2、watch只支持帧动画,我们只能通过png序列来实现动画效果。WKInterfaceGroup 和 WKInterfaceImage均可以实现帧动画。 3、开发的watch应用内存被限定为80M,太多帧的动画会不支持 4、提交应用watch也需要配置市场截图。 5、watch分为两个target。当新建一个Target为WatchDemo,xcode会自动生成一个WatchDemo Extension。前者负责UI,后者负责逻辑。引用cocoapods可以这么写: target 'WatchDemo Extension' do platform :watchos, '3.0' use_frameworks! pod 'Alamofire' end6、Always Embed Swift Standard Libraries 在Build Settings里面,这两个target,需要将WatchDemo Extension中设置为Yes,另一个设置为No Github Sizes 可以在一个界面,显示各个屏幕尺寸。这样我们就不用每个模拟器跑一遍看效果了。方便调试。 iOS-DeviceSupport 当手机升级,而xcode未升级时,我们会遇到Device Support的弹框,此时要么升级xcode,要么需要导入对应的Device Support文件。这个库就是提供这种文件的。 InjectionIII 用于解决烦人的UI调试问题。当修改了一些UI属性之后,在xcode中我们只能运行程序才能看到效果,如果是处理大量的UI问题,这个过程是很烦人的。好在InjectionIII帮我们解决了这个问题,一起了解一下吧!