ios Object Encoding and Decoding with NSSecureCoding Protocol
Object Encoding and Decoding with NSSecureCoding Protocol
NSCoding is a fantastically simple and convenient way to store data on iOS or Mac OS by turning your model objects directly into a file and then loading it back into memory again, without needing to write any file parsing and serialization logic. To save any object to a data file (assuming that it implements the NSCoding protocol), you can just do this:
Foo *someFoo = [[Foo alloc] init]; |
And to load it again later:
Foo *someFoo = [NSKeyedUnarchiver unarchiveObjectWithFile:someFile]; |
That’s fine for resources that are compiled into your app (such as nib files, which use NSCoding under the hood), but the problem with using NSCoding for reading and writing user data files is that, because you are encoding whole classes in a file, you are implicitly giving that file (with potentially unknown origin) permission to instantiate classes inside your app.
Although you cannot store executable code in an NSCoded file (on iOS at least), a hacker could use a specially crafted file to trick your app into instantiating classes that you never intended it to, or do so in a different context to what you intended. Whilst it would be difficult to do any real harm in this way, it could certainly lead to app crashes and/or data loss for users.
In iOS6, Apple introduced a new protocol built on top of NSCoding called NSSecureCoding. NSSecureCoding is identical to NSCoding, except that when decoding objects you specify both the key and class of the object you are decoding, and if the expected class doesn’t match the class of the object decoded from the file, the NSCoder will throw an exception to tell you that the data has been tampered with.
Most of the system objects that support NSCoding have been upgraded to support NSSecureCoding, so by enabling secure coding on your NSKeyedUnarchiver, you can make sure that the data file you are loading is safe. You would do that like this:
// Set up NSKeyedUnarchiver to use secure coding |
Note that if you enable secure coding on your NSKeyedUnarchiver, every object stored in the file must conform to NSSecureCoding, otherwise you will get an exception. To tell the framework that your own classes support NSSecureCoding, you have to implement the new decoding logic in your initWithCoder: method, and return YES from the supportsSecureCoding method. There are no changes to the encodeWithCoder: method needed, as the security concerns are around loading, not saving.
@interface Foo : NSObject @property (nonatomic, strong) NSNumber *property1; |
A few weeks ago, I wrote about how to implement NSCoding automatically by using introspection to determine the properties of your class at runtime.
This is a great way to add NSCoding support to all of your model objects without having to write repetitive and error-prone initWithCoder:/encodeWithCoder: methods. But the approach we used would not support NSSecureCoding because we do not attempt to validate the types of the objects being loaded.
So how can we enhance our automatic NSCoding system to support NSSecureCoding right out of the box?
If you recall, the original implementation worked by using the class_copyPropertyList() and property_getName() runtime functions to generate a list of property names, which we stored in an array:
// Import the Objective-C runtime headers |
Using KVC (Key-Value Coding), we were then able to set and get all the properties of an object by name and encode/decode them in an NSCoder object.
To implement NSSecureCoding, we’ll follow the same principle, but instead of just getting the property names, we’ll also need to get their types. Fortunately, the objective C runtime stores detailed information about the types of class properties, so we can easily extract this data along with the names.
Properties of a class can be either primitive types (such as integers, BOOLs and structs) or objects (such as NSString, NSArray, etc). The KVC valueForKey: and setValue:forKey: methods implement automatic “boxing” of primitive types, meaning that they will turn integers, BOOLs and structs into NSNumber and NSValue objects, respectively. That makes things a bit easier for us because we only have to deal with boxed types (objects), so we can represent all of our property types as classes, instead of having to call different decoding methods for different property types.
The runtime methods don’t give us the boxed class name for each property though, instead they give us the type encoding – a specially formatted C-string containing the type information (this is the same format returned by the @encode(var); syntax). There’s no automatic way to get the equivalent boxed class for a primitive type, so we’ll need to parse this string and then specify the appropriate class ourselves.
The format of a type encoding string is documented here.
The first character represents the primitive type. Objective C uses a unique character for each supported primitive, for example ‘i’ represents an integer, ‘f’ a float, ‘d’ a double, and so on. Objects are represented by ‘@’ (followed by the class name) and then there are other more obscure types such as ‘:’ for selectors, or ‘#’ for classes.
Struct and union types are represented by expressions wrapped in {…} brackets. Only some of these types are supported by the KVC mechanism, but the ones that are supported are always boxed as NSValue objects, so we can treat any value starting with ‘{‘ the same way.
If we switch based on the first character of the string, we can handle all the known types:
Class propertyClass = nil; |
To handle ‘@’ types, we need to extract the class name. The class name may include protocols (which we don’t really need to worry about), so we’ll split the string to extract just the class name, and then use NSClassFromString to get the class:
case '@': |
Finally, we can combine this parsing logic with the propertyNames method logic from our previous implementation to create a method that returns a dictionary of property classes, keyed by property name. Here is the complete implementation:
- (NSDictionary *)propertyClassesByName |
That’s the hard part done. Now, to implement NSSecureCoding, we’ll just modify the initWithCoder: method from our previous automatic coding implementation to take the property class into account when parsing. We’ll also need to return YES from the supportsSecureCoding method:
+ (BOOL)supportsSecureCoding |
And there you have it: A simple base class for your models that supports NSSecureCoding out of the box. Alternatively, you can use my AutoCoding category that uses this approach to add automatic NSCoding and NSSecureCoding support to any object that doesn’t already implement it.
![]() |
Nick Lockwood is the author of iOS Core Animation: Advanced Techniques. Nick also wrote iCarousel, iRate and other Mac and iOS open source projects.
|
ios Object Encoding and Decoding with NSSecureCoding Protocol的更多相关文章
- Direct Access to Video Encoding and Decoding
来源:http://asciiwwdc.com/2014/sessions/513 Direct Access to Video Encoding and Decoding Session 5 ...
- Redis 数据结构与编码技术 (Object Encoding)
数据结构实现 相信大家对 redis 的数据结构都比较熟悉: string:字符串(可以表示字符串.整数.位图) list:列表(可以表示线性表.栈.双端队列.阻塞队列) hash:哈希表 set:集 ...
- Node.js Base64 Encoding和Decoding
如何在Node.js中encode一个字符串呢?是否也像在PHP中使用base64_encode()一样简单? 在Node.js中有许多encoding字符串的方法,而不用像在JavaScript中那 ...
- Object C学习笔记15-协议(protocol)
在.NET中有接口的概念,接口主要用于定义规范,定义一个接口关键字使用interface.而在Object C 中@interface是用于定义一个类的,这个和.NET中有点差别.在Object C中 ...
- IOS Object和javaScript相互调用
在IOS开发中有时会用到Object和javaScript相互调用,详细过程例如以下: 1. Object中运行javascript代码,这个比較简单,苹果提供了非常好的方法 - (NSString ...
- Thinking in file encoding and decoding?
> General file encoding ways We most know, computer stores files with binary coding like abc\xe4\ ...
- ios回调函数的标准实现:protocol+delegate
一.项目结构
- IOS开发之----协议与委托(Protocol and Delegate) 实例解析
1 协议: 协议,类似于Java或C#语言中的接口,它限制了实现类必须拥有哪些方法. 它是对对象行为的定义,也是对功能的规范. 在写示例之前我给大家说下@required和@optional这两个关键 ...
- 【iOS】Swift扩展extension和协议protocol
加上几个关节前Playground摘要码进入github在,凝视写了非常多,主要是为了方便自己的未来可以Fanfankan. Swift语法的主要部分几乎相同的. 当然也有通用的.运算符重载.ARC. ...
随机推荐
- JZ2440开发笔记(8)——FCLK、HCLK和PCLK
S3C2440中有三种时钟,分别是FCLK,HCLK和PCLK.这三种时钟的功能各不相同,其中FCLK主要是为ARM920T的内核提供工作频率,如图: HCLK主要是为S3C2440 AHB总线(Ad ...
- 以后坚持用java
1.不要贪多,现在专心学习java.读一些jvm的书. 2.研究lucene,hadoop.mahout,和日后用的自然语言分析lingpipe. 3.对于数据挖掘方向,专注与weka的学习,同时注意 ...
- str*函数和大小端判断
#include <stdio.h> #include <assert.h> size_t mstrlen(const char *s) { assert(s != NULL) ...
- 把普通的git库变成bare库
$ cd your_repo $ mv .git .. && rm -fr * $ mv ../.git . $ mv .git/* . $ rmdir .git $ git conf ...
- Go语言简介
Go语言简介 - Go语言是由Google开发的一个开源项目,目的之一为了提高开发人员的编程效率. Go语言简介 Go语言是由Google开发的一个开源项目,目的之一为了提高开发人员的编程效率. Go ...
- Hibernate拦截器(Interceptor)与事件监听器(Listener)
拦截器(Intercept):与Struts2的拦截器机制基本一样,都是一个操作穿过一层层拦截器,每穿过一个拦截器就会触发相应拦截器的事件做预处理或善后处理. 监听器(Listener):其实功能与拦 ...
- String的点点滴滴
一.String 的 equals()到底比较的是什么?equals() 与 == 的区别? 当使用关系运算符==比较两个对象时,是比较两个对象使用的内存地址和内容是否相同,如果两个对象使用的是同一个 ...
- NLog使用说明
NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码. NLog允许我们自定义从跟踪消息的来源(source)到记录跟踪信息的目标(target)的规则 ...
- macos ssh host配置及免密登陆
windows下面有xshell 这样的可视化ssh管理工具 macos 下面使用终端做下简单配置,也非常方便,具体过程如下 生成秘钥 cd ~/.sshssh-keygen -t rsa 生成了私钥 ...
- Oracle Linux 6.3下安装Oracle 11g R2(11.2.0.3)
本文主要描写叙述了在Oracle Linux 6.3下安装Oracle 11gR2(11.2.0.3).从Oracle 11g開始,Oracle官方站点不再提供其Patch的下载链接,须要使用Meat ...
