iOS 序列化与反序列化(runtime) 01

//联系人:石虎  QQ: 1224614774昵称:嗡嘛呢叭咪哄

一、变量声明

为便于下文讨论,提前创建父类Biology以及子类Person

Biology:

@interface Biology : NSObject
{
    NSInteger *_hairCountInBiology;
}
@property (nonatomic, copy) NSString *introInBiology;
@end

@implementation Biology
@end

Person:

#import <Foundation/Foundation.h>
#import "Biology.h"
#import <objc/runtime.h>

@interface Person : Biology
{
    NSString *_father;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

@implementation Person

@end

补充说明
凡是在父类中定义的属性或者变量,末尾都有InBiology标志;反之也成立


二、问题引入

在iOS中一个自定义对象是无法直接存入到文件中的,必须先转化成二进制流才行。从对象到二进制数据的过程我们一般称为对象的序列化(Serialization),也称为归档(Archive)。同理,从二进制数据到对象的过程一般称为反序列化或者反归档。
在序列化实现中不可避免的需要实现NSCoding以及NSCopying(非必须)协议的以下方法:

- (id)initWithCoder:(NSCoder *)coder;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)copyWithZone:(NSZone *)zone;

假设我们现在需要对直接继承自NSObject的Person类进行序列化,代码一般长这样子:

//对变量编码
- (void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeObject:@(self.age) forKey:@"age"];
    [coder encodeObject:_father forKey:@"_father"];
  //... ... other instance variables
}
//对变量解码
- (id)initWithCoder:(NSCoder *)coder
{
    self.name = [coder decodeObjectForKey:@"name"];
    self.age = [[coder decodeObjectForKey:@"age"] integerValue];
    _father = [coder decodeObjectForKey:@"_father"];
  //... ... other instance variables

似乎so easy?至少到目前为止是这样的。但是请考虑以下问题:

  • 若Person是个很大的类,有非常多的变量需要进行encode/decode处理呢?
  • 若你的工程中有很多像Person的自定义类需要做序列化操作呢?
  • 若Person不是直接继承自NSObject而是有多层的父类呢?(请注意,序列化的原则是所有层级的父类的属性变量也要需要序列化);

如果采用开始的传统的序列化方式进行序列化,在碰到以上问题时容易暴露出以下缺陷(仅仅是缺陷,不能称为问题):

  • 工程代码中冗余代码很多
  • 父类层级复杂容易导致遗漏点一些父类中的属性变量

那是不是有更优雅的方案来回避以上问题呢?那是必须的。这里我们将共同探讨使用runtime来实现一种接口简洁并且十分通用的iOS序列化与反序列方案。


还有第二页.
谢谢!!!
<think>在C++中,序列化是指将对象的状态信息转换为可以存储或传输的形式(如字节流)的过程,而反序列化则是将这种形式重新转换为对象的过程。序列化常用于网络传输、持久化存储等场景。C++标准库本身不直接提供序列化机制,但我们可以通过以下方式实现:1.使用第三方库(如Boost.Serialization、Google的ProtocolBuffers等)2.手动实现序列化/反序列化函数下面我将展示手动实现一个简单的序列化反序列化示例,以二进制形式读写一个简单的类。假设有一个简单的类`Person`:```cpp#include<iostream>#include<fstream>#include<string>#include<cstring>//formemcpyclassPerson{public:Person()=default;Person(conststd::string&name,intage):name_(name),age_(age){}//序列化:将对象转换为字节流voidSerialize(std::ostream&out)const{//先写入字符串长度,再写入字符串内容size_tnameSize=name_.size();out.write(reinterpret_cast<constchar*>(&nameSize),sizeof(nameSize));out.write(name_.data(),nameSize);out.write(reinterpret_cast<constchar*>(&age_),sizeof(age_));}//反序列化:从字节流恢复对象voidDeserialize(std::istream&in){//读取字符串长度size_tnameSize;in.read(reinterpret_cast<char*>(&nameSize),sizeof(nameSize));//根据长度读取字符串name_.resize(nameSize);in.read(&name_[0],nameSize);in.read(reinterpret_cast<char*>(&age_),sizeof(age_));}voidPrint()const{std::cout<< "Name:"<< name_<< ",Age:"<< age_<< std::endl;}private:std::stringname_;intage_=0;};```使用示例:```cppintmain(){Personp1("Alice",30);//序列化到文件std::ofstreamoutFile("person.dat",std::ios::binary);if(outFile){p1.Serialize(outFile);outFile.close();}//反序列化Personp2;std::ifstreaminFile("person.dat",std::ios::binary);if(inFile){p2.Deserialize(inFile);inFile.close();}p2.Print();//输出:Name:Alice,Age:30return0;}```解释:1.`Serialize`函数将`Person`对象写入输出流。对于字符串成员,我们先写入字符串的长度(`size_t`类型),然后写入字符串内容。对于整数类型,直接写入。2.`Deserialize`函数按照同样的顺序读取:先读取字符串长度,然后根据长度读取字符串,最后读取整数。3.注意:这种方法在不同平台(如不同大小的`size_t`)或编译器之间可能不兼容,因此通常用于同一环境。对于更复杂的对象(包含指针、容器等)或需要跨平台,建议使用第三方库。注意事项:-序列化指针时,不能直接写入指针的值(因为这是内存地址,没有意义),需要序列化指针指向的数据。-需要处理字节序(大端/小端)问题,如果需要在不同架构的机器间传输。相关问题:§§相关问题§§:1.在C++中,序列化一个包含动态分配内存的类时,需要注意什么?2.如何序列化一个包含多态对象的容器?3.使用Boost.Serialization库进行序列化的基本步骤是什么?4.什么是字节序?在序列化中如何处理字节序问题?</think> 在C++中,序列化是指将数据结构或对象转换为可存储或传输的格式(如字节流),反序列化则是将这种格式恢复为原始数据结构的过程。C++标准库没有内置序列化机制,但可通过多种方式实现: ### 手动实现序列化/反序列化 ```cpp #include <iostream> #include <fstream> #include <string> class Person { public: std::string name; int age; // 序列化到文件 void serialize(const std::string& filename) { std::ofstream file(filename, std::ios::binary); if (!file) throw std::runtime_error("Cannot open file"); size_t nameLen = name.size(); file.write(reinterpret_cast<const char*>(&nameLen), sizeof(nameLen)); file.write(name.data(), nameLen); file.write(reinterpret_cast<const char*>(&age), sizeof(age)); } // 从文件反序列化 void deserialize(const std::string& filename) { std::ifstream file(filename, std::ios::binary); if (!file) throw std::runtime_error("Cannot open file"); size_t nameLen; file.read(reinterpret_cast<char*>(&nameLen), sizeof(nameLen)); name.resize(nameLen); file.read(&name[0], nameLen); file.read(reinterpret_cast<char*>(&age), sizeof(age)); } }; int main() { Person p1{"Alice", 30}; p1.serialize("person.dat"); Person p2; p2.deserialize("person.dat"); std::cout << p2.name << ": " << p2.age << "\n"; // 输出: Alice: 30 } ``` ### 关键实现细节解释: 1. **二进制写入**:使用`std::ios::binary`避免文本转换导致的格式问题 2. **长度前缀**:对于字符串等变长数据,先写入长度再写内容 3. **内存布局**:`reinterpret_cast`直接读写内存,但需注意: - 确保相同硬件/编译器环境 - 避免序列化指针(仅序列化指向的数据) - 处理字节序问题 4. **错误处理**:检查文件操作状态 ### 推荐的第三方库: 1. **Boost.Serialization**:最完整的解决方案,支持复杂类型和多态 2. **Protocol Buffers**:跨语言、高性能,需要IDL定义 3. **JSON/nlohmann**:人类可读格式(文本序列化---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值