背景
随着博客越写越多, 难免会遇到需要插入图片来说明的情况.
图床选择
首先调研了市面上的图床服务, 本着稳定长期的目标, 过滤掉了打一枪换一个地方的野鸡小网站, 剩余比较靠谱的优缺点如下.
图床 | 优点 | 缺点 |
---|---|---|
腾讯云 | 免费 无需域名 | 未来可能会收费 |
七牛 | 免费 | 需要域名和备案 |
又拍云 | 免费 无需域名 | 未来可能会收费 |
阿里云 | 目前最完备 | 收费 需要域名 |
微博 | 免费 无需域名 | 不稳定 匿名上传 |
作为一个刚起步的小博客, 应该把精力更多关注于内容, 以后再考虑域名备案或者大流量套餐, 因此尽量选择免费的图床. 其实是穷
微博作为国内首屈一指的流量大户, 其图床的 CDN 和质量肯定没有问题, 但是上传图片会自带水印, 且匿名上传总觉得不靠谱.
剩下的选择还有两个, 又拍云进军对象存储领域比腾讯云早而且更成熟, 但是就规模和技术来说, 我还是更愿意相信腾讯.
工具
注册完腾讯云账号后, 下一个问题就是怎么更方便的将图床与 Markdown 结合起来使用, 提高效率和体验.
iPic 完美符合我的需求, 这是一款 Mac 上的状态栏软件, 支持上传本地图片到设定的图床, 获取图片地址后按照 ![](url) 格式复制到剪贴板.
那么好的应用为啥不用呢?
因为不想按年交钱. 应用默认是微博图床, 如果要使用其他图床就需要购买专业版, 每年 60 元. 如果是一次买断的话, 也就买了, 年费心里总有疙瘩 矫情.
突然, 我就想到! 自己开发一个! 闲的蛋疼
开发 iPhone 应用已经好多年了, 还从未开发过 Mac 上的状态栏软件, 正好还能锻炼下 Swift, 于是说干就干. 没想到开发了一个月
需求设计
产品使用逻辑基本与 iPic 一致, 基于状态栏交互, 选择 PNG jpg 文件上传.
可以设置是否压缩图片, 压缩会压到 500K 以下.
还需要有一个登录界面记录腾讯云的账号和存储库信息.
文件上传成功后, 弹出通知提醒, 并复制到剪贴板.
如果不慎复制了其他文本导致丢失了链接, 再点击一次通知就可以重新获取.
遇到的难题
Swift
第一关就是编程语言.
虽然也曾系统的学过 Swift, 但由于常年使用 Objective-C 开发, 思维方式还转不过来.
严格的空变量
比较明显的区别就是处理空变量的方式.
在 ObjC 中, 指针变量可以是 nil(也就是 0), 对 nil 执行方法不会发生任何事情, 因此可以算是部分安全.
Swift 对待空变量更严格,! 修饰的变量必须有具体值,? 修饰的变量才具有空值的可能性.
nil 不再表示为空对象, 而是一个空值, 向空值调用方法会导致闪退. 对待? 修饰的变量必须要小心, 最好先判断是否有值再使用, 好在有语法糖可以解决这类问题.
- // 默认为 nil
- var money : String?
- // 变量有值
- money = "million"
- // 判断肯定有值后再使用
- if money != nil {
- print("I have \(money!) dollars.")
- }
- // 保证变量有值并赋值给安全变量后执行
- if let account = money {
- print("I have \(account) dollars.")
- }
- // 变量如果没有值就执行 else 事件并 return
- guard let account = money else {
- print("I have no money.")
- }
- print("I have \(account) dollars.")
合理使用! ? 会使我们的代码更安全与简洁.
Swift 的 nil 和 Objective-C 中的 nil 并不一样. 在 Objective-C 中, nil 是一个指向不存在对象的指针. 在 Swift 中, nil 不是指针 -- 它是一个确定的值, 用来表示值缺失. 任何类型的可选状态都可以被设置为 nil, 不只是对象类型.
抛出警告
ObjC 有 @throw 的用法, 但是根据苹果官方的描述, 执行的成本很大. 究其原因在于 ObjC 基于 C 语言而不是 C++, 所以只能使用 setjmp() 和 longjmp() 方法实现, 因此可能会造成内存泄漏.
Important: Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors (such as a file not being accessible)
Swift 从根本解决了这个问题, 并结合枚举优化了整个流程.
- enum CompressError : Error {
- case NoImage
- case OverSize(size : Int)
- }
- func compressImage(_ imageData: Data?) throws -> Data? {
- guard var compressData = imageData else {
- throw CompressError.NoImage
- }
- if compressData.count> maxSize {
- throw CompressError.OverSize(size: compressData.count)
- }
- }
- func uploadImage(_ imageData: Data?) {
- var compressData : Data? = nil
- do {
- compressData = try self.compressImage(imageData)
- } catch CompressError.NoImage {
- print("Image Not Exist")
- } catch CompressError.OverSize(let size) {
- print("Image over size of \(size)")
- } catch _ {}
- // 简洁的方式, 忽略处理警告
- let compressData = try? self.compressImage(imageData)
- }
利用 Swift 强大的枚举类型, 可以定制化警告从而传递出我们需要的信息, 使得整个流程更为顺畅.
语法还支持 try? 忽略警告获取一个可能为空值的变量, 如果自信绝对不会抛出异常的话, 还能使用 try! 获取一个肯定值.
Mac OS 开发
实际编写 Cocoa 代码过程中, 发现与 UIKit 相差还是比较多的.
控件逻辑
UIKit 的层级一般是 UINavigationController -> UIViewController
Cocoa 的层级则不太一样, NSWindowController -> NSViewController
原因也很简单, 手机上一般只有一个窗口, 依靠导航栏进行页面跳转. 但是桌面端逻辑就不太一样, 新页面一般都是以新窗口的形式弹出.
其次桌面端拥有特定的状态栏控件 NSMenu, 在其中操作菜单项也是一个新的挑战.
腾讯云相关
由于腾讯云只提供了 iOS 的库, 所以我还需要先把库文件重新调整为 Cocoa 代码. 这一部分也是吃了不少苦头, 需要把设备相关的代码与应用, 进出后台的通知等都去除, 还要处理类似功能的转换 (比如 UIImage -> NSImage).
同时还有第二个坑, 腾讯云的库都是 ObjC 代码, 所以需要混编.
创建一个工作空间后拖入两个工程, 在主工程的 Targets / Build Phases / Embed Frameworks 中加入 SDK 库.
接着在 Swift 工程中创建 Project-Bridging-Header.h 头文件, 在其中引用 SDK 库.
最后在 Targets / Build Settings / Objective-C Bridging Header 设置头文件, 就可以解决代码混编的问题.
其原理在于自动创建了基于头文件的 pch, 把头文件中引用到的 ObjC 代码, 都桥接到工程中.
图片压缩算法
之所以不使用现成的软件还有一个原因, 就是我想自己控制压缩图片的参数和效果.
通过调研和实验图片压缩效果, 最终我选择压制成 jpg 格式, 500k 大小限制, 压缩率限制为最小 0.75, 等比宽度限制为 1280px.
文首那张美女图, 初始是 1.9M 5087x3661, 由于尺寸过大, 第一次压缩图片质量后, 容量反而增加到了 2.4M.
将宽高等比缩小到 1280x922, 图片又变大了, 这次增加到了 4.7M.(改变宽高需要新建一张画布, 创建时必须要有 alpha 通道等其他设置, 所以会变大)
我们接着压缩, 最终在压缩率为 0.9 的情况下把图片压到了 260K, 成功达到了目标.
总结
距离上一次博客已经有两个月的间隔, 其中一部分原因在于生活上的一些变故, 另一个原因就在于不熟悉 Cocoa + Swift 开发.
好在最终还是啃出来了, GitHub 项目 https://github.com/VanchChen/Image2Url 已开源, 欢迎大家指点与吐槽.
这次项目最大的收获在于脱离自己的舒适区. 人的本性包含惰性, 总是趋向于在熟悉的领域干熟悉的活. 但是就和企业一样, 不创新就死, 技术不断在发展, 如果没有跟上潮流, 最终就会被淘汰. 以此共勉!
参考资料
Swift 4.0 教程 http://www.swift51.com/swift4.0/
App 图片压缩裁剪原理和上传方案 https://www.jianshu.com/p/9b47fc25f526
来源: https://juejin.im/post/5c22023e6fb9a04a01645b6e