惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
爱范儿
爱范儿
Y
Y Combinator Blog
T
Tor Project blog
V
Visual Studio Blog
U
Unit 42
B
Blog RSS Feed
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
G
Google Developers Blog
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Hackread – Cybersecurity News, Data Breaches, AI and More
F
Fortinet All Blogs
人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research
The GitHub Blog
The GitHub Blog
Recorded Future
Recorded Future
博客园_首页
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
P
Proofpoint News Feed
Jina AI
Jina AI
博客园 - 【当耐特】
S
Security @ Cisco Blogs
I
Intezer
MyScale Blog
MyScale Blog
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
腾讯CDC
T
Tenable Blog
A
Arctic Wolf
T
Threat Research - Cisco Blogs
S
Securelist
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
C
Check Point Blog
NISL@THU
NISL@THU
Microsoft Security Blog
Microsoft Security Blog
V
Vulnerabilities – Threatpost

isaced

使用 TypeScript 构建的 appstore-connect-sdk 归档 我用 SwiftUI 写了一个 V2ex 客户端 Docker 容器化多应用部署 Swift Server-side SDWebImage 为什么无法读取 webpmux 生成的 WebP 动图? [译] 探索 Swift 4 中新的 String API 对一个 Django 站点做 ab 压力测试 fir-mac 开发笔记 iOS10 Safari 引导用户信任企业签名 Swift Web 开发之 Vapor - 模版 Leaf(三) Swift Web 开发之 Vapor - 路由(二) HoloLens 初体验 Swift Web 开发之 Vapor - 入门(一) Jenkins for iOS 搭建日记 iOS HOOK 注入与重签名 Xcode 使用 Configuration 来配置不同的项目环境 图床从SAE迁移到七牛 给 UIProgressView 脱掉那层微弱的渐变 初用 CocoaAsyncSocket 记一次 SQLite 性能优化 有多色 - 开发小记 海岛风情 - 泰国普吉岛六日游 EGOCache 源码解析 日语五十音假名临摹校验算法 [iOS] UITextView 追加文字自动跳到顶部解决方法 Cocoa 新的依赖管理工具:Carthage 新年快乐! 博客主机换到搬瓦工 写了个iOS小游戏:有多色 iOS 后台任务之 Long-running background task 悄悄的就必须用 XCode 5.1.1 上传了! HTTP Content-type 与 AFNetworking Objective-Cloud 初体验(1) 掌中站上架小记 NSLoger 上线了 Python Markdown 做语法限制 Nginx + Gunicorn + Django 部署小记 用Shell脚本批量裁剪App Icon各个尺寸 LaunchRocket使用小记 2014新年伊始:近期读过的好文推荐 [iOS] stringWithFormat 和 initWithFormat 有何不同? Apache + Flask + mod_wsgi + Koding 部署小记 收集几个Objective-C的HTML解析库 NSMapTable: 不只是一个能放weak指针的 NSDictionary 2013,再见! Emlog到Typecho的迁移工具:emlog2typecho [iOS]给上拉下拉刷新PullToRefresh做本地化 [iOS]代码推荐:大图缩放视图 - Vertigo iOS:hidesBottomBarWhenPushed的正确用法 Xcode 5新的Interface Builder,你准备好了吗? [翻译]如何使用Django创建简单的博客 我的Mac音乐播放器 初识 NSDataDetector Objective-C 计算代码运行时间 WordPress上传文件自动重命名 iOS:仿jing.fm的音乐播放视图 我的啃黑苹果之路 关于 搜索 项目
用宏提速NSCoding
2014-02-10 · via isaced

还记得你要为每个类实现dealloc方法的日子吗?从头文件复制每一个实例变量到dealloc方法不只是很烦,简直是一场灾难。忘了一个实例变量?内存泄漏,有个含有 30 个实例变量的类,并且意外地释放了两次相同的实例变量?崩溃。

幸运的是,那种内存管理的日子已经过去了,但归档和解档(序列化和反序列化)对象仍然存在,那是有多惨地写下面的代码:

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if(self) {
        _val = [aDecoder decodeIntForKey:@"_val"];
        _obj = [aDecoder decodeObjectForKey:@"_obj"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_obj forKey:@"_obj"];
    [aCoder encodeInt:_val forKey:@"_val"];
}

这不仅是代码惨,它容易出错,如果在方法中拼错了 key 会发现什么?要么你不保存这些数据要么不重新加载它,这两种都是错误的,是不明显的。然而你的应用程序其他地方会出错,而且你会花时间来追踪问题最后发现则是你的NSCoding方法中一个简单的拼写错误。

有一个很简单的解决方法,虽然它依赖于 C 预处理器一个相对比较陌生的功能:字符串化(stringification),在任何 C 文件,当然也是 Objective-C 文件,你可以创建一个宏包含#符号在一个标识(symbol)前,这个宏就可以转换一个标识到一个 C 的字符串(char *),例如:

#define STRINGIFY(x) #x
int myVariable = 5;
NSLog(@"%s", STRINGIFY(myVariable));

打印出什么?“myVariable”

好,我还可以创建一个这样的宏:

#define OBJC_STRINGIFY(x) @#x

这意味着返回的不是一个 C 字符串,而是一个 NSString:

int myVariable = 5;
NSString *foo = OBJC_STRINGIFY(myVariable);
NSLog(@"%@", foo);

好,这样也打印出了"myVariable",那这跟NSCoding有啥关系?当然,归档中实例变量的编码和解码可以使用变量名作为 key。

[aCoder encodeObject:_myInstanceVariable forKey:@"_myInstanceVariable"];

你可能会发现,我们是代表在这行代码两种方式相同的“字符串”:曾经作为编译器的象征,曾经作为一个 NSString。它会更简单和无差错的,只写_myInstanceVariable 一次,让编译器检查。随着 OBJC_STRINGIFY,那很简单:

#define OBJC_STRINGIFY(x) @#x
#define encodeObject(x) [aCoder encodeObject:x forKey:OBJC_STRINGIFY(x)]
#define decodeObject(x) x = [aDecoder decodeObjectForKey:OBJC_STRINGIFY(x)]

现在你的NSCoding方法看起来像这样:

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if(self) {
        decodeObject(_obj);
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    encodeObject(_obj);
}

其中一件事是有些人不喜欢这样的宏,他们无法验证,实际上是正在编写的代码 - 假的。打开.m 文件,选择菜单 Product -> Generate Output -> Preprocessed File。 Xcode 中会产生你的执行文件看起来像所有的预处理器指令进行了解析之后一样。

请记住,虽然 #import 是一个预处理器指令。所以一个预处理的 .m 文件将从 Foundation 、 UIKit 中包含所有头文件,和其他任何你可能已引入的 - 所以一定要向下滚动到的预处理结果的底部。(您可能还注意到,你可以以类似的方式生成程序集,它总是有趣的。)

所以,你在哪里定义这些宏像 OBJC_STRINGIFY? 通常,我把它们放在头文件中,有许多快速实用的宏,我喜欢用在很多项目中。然后给创建的每一个新项目,导入头文件到我的预编译头文件(每个项目都有的 .pch 文件)。对一个项目的预编译头文件被包含在项目中的每个文件。这意味着两件事情:在 .pch 文件中任何事情都是可以在每个文件在您的项目,无论合适更改了 .pch 文件,你必须重新编译整个项目。

现在,回到 NSCoding:记得归档时,你可能不只是编码或解码对象。您可能会编码整数,浮点数,甚至是结构体。所以,你可能还会需要这样的宏:

#define encodeFloat(x) [aCoder encodeFloat:x forKey:OBJC_STRINGIFY(x)]

对于结构体,我通常归档无论如何都是分解结构的每个成员,所以如果我编码 CGPoint ,例如,它看起来像这样:

encodeFloat(_point.x);
encodeFloat(_point.y);

当然,如果不相信我的话代码可不会骗你,你可以自己写和生成的预处理文件中看到的结果。


原文链接:http://stablekernel.com/blog/speeding-up-nscoding-with-macros/

这翻译的真辛苦,差点看不下去,太渣了…不过确实是一篇好文

关于字符串化(stringification)的一点参考资料:http://gcc.gnu.org/onlinedocs/cpp/Stringification.html