使用FastCoder写缓存单例

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介:

使用FastCoder写缓存单例

FastCoder可以存储字典,数组,鄙人将FastCoder封装,CoreData可以缓存的东西,用这个都可以缓存,但是只适合缓存少量的数据(不适合存储几万条数据)。

基于文件的类请参考上一章节内容

使用详情:

源码:

使用的缓存文件

SharedFile.h 与 SharedFile.m

//
//  SharedFile.h
//  Array
//
//  Created by YouXianMing on 14/12/1.
//  Copyright (c) 2014年 YouXianMing. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SharedFile : NSObject

/**
 *  存储数组
 *
 *  @return YES,成功,NO,失败
 */
+ (BOOL)storeArray;

/**
 *  返回原始的可以修改的数组
 *
 *  @return 原始可以修改的数组
 */
+ (NSMutableArray *)sharedOriginalArray;

/**
 *  返回原始数组的拷贝
 *
 *  @return 原始数组的拷贝
 */
+ (NSMutableArray *)sharedCopiedArray;

@end


//
//  SharedFile.m
//  Array
//
//  Created by YouXianMing on 14/12/1.
//  Copyright (c) 2014年 YouXianMing. All rights reserved.
//

#import "SharedFile.h"
#import "NSString+File.h"
#import "NSObject+FastCoder.h"

static NSString *filePath = @"/Library/Caches/YouXianMing";

NSMutableArray *storedArray = nil;

@implementation SharedFile

+ (void)initialize {
    if (self == [SharedFile class]) {
        
        if ([filePath exist] == NO) {
            storedArray = [NSMutableArray array];
        } else {
            storedArray = [@"SharedFile" useFastCoderToRecoverFromFilePath:[filePath path]];
        }
    }
}

+ (BOOL)storeArray {
    return [storedArray useFastCoderToWriteToFilePath:[filePath path]];
}

+ (NSMutableArray *)sharedOriginalArray {
    return storedArray;
}

+ (NSMutableArray *)sharedCopiedArray {
    return [NSMutableArray arrayWithArray:storedArray];
}

@end

FastCoder.h 与  FastCoder.m
//
//  FastCoding.h
//
//  Version 3.0.2
//
//  Created by Nick Lockwood on 09/12/2013.
//  Copyright (c) 2013 Charcoal Design
//
//  Distributed under the permissive zlib License
//  Get the latest version from here:
//
//  https://github.com/nicklockwood/FastCoding
//
//  This software is provided 'as-is', without any express or implied
//  warranty.  In no event will the authors be held liable for any damages
//  arising from the use of this software.
//
//  Permission is granted to anyone to use this software for any purpose,
//  including commercial applications, and to alter it and redistribute it
//  freely, subject to the following restrictions:
//
//  1. The origin of this software must not be misrepresented; you must not
//  claim that you wrote the original software. If you use this software
//  in a product, an acknowledgment in the product documentation would be
//  appreciated but is not required.
//
//  2. Altered source versions must be plainly marked as such, and must not be
//  misrepresented as being the original software.
//
//  3. This notice may not be removed or altered from any source distribution.
//


#import <Foundation/Foundation.h>


extern NSString *const FastCodingException;


@interface NSObject (FastCoding)

+ (NSArray *)fastCodingKeys;
- (id)awakeAfterFastCoding;
- (Class)classForFastCoding;
- (BOOL)preferFastCoding;

@end


@interface FastCoder : NSObject

+ (id)objectWithData:(NSData *)data;
+ (id)propertyListWithData:(NSData *)data;
+ (NSData *)dataWithRootObject:(id)object;

@end


//
//  FastCoding.m
//
//  Version 3.0.2
//
//  Created by Nick Lockwood on 09/12/2013.
//  Copyright (c) 2013 Charcoal Design
//
//  Distributed under the permissive zlib License
//  Get the latest version from here:
//
//  https://github.com/nicklockwood/FastCoding
//
//  This software is provided 'as-is', without any express or implied
//  warranty.  In no event will the authors be held liable for any damages
//  arising from the use of this software.
//
//  Permission is granted to anyone to use this software for any purpose,
//  including commercial applications, and to alter it and redistribute it
//  freely, subject to the following restrictions:
//
//  1. The origin of this software must not be misrepresented; you must not
//  claim that you wrote the original software. If you use this software
//  in a product, an acknowledgment in the product documentation would be
//  appreciated but is not required.
//
//  2. Altered source versions must be plainly marked as such, and must not be
//  misrepresented as being the original software.
//
//  3. This notice may not be removed or altered from any source distribution.
//

#import "FastCoder.h"
#import <objc/runtime.h>
#import <CoreGraphics/CoreGraphics.h>


#import <Availability.h>
#if __has_feature(objc_arc)
#pragma clang diagnostic ignored "-Wpedantic"
#warning FastCoding runs slower under ARC. It is recommended that you disable it for this file
#endif


#pragma clang diagnostic ignored "-Wgnu"
#pragma clang diagnostic ignored "-Wpointer-arith"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wfour-char-constants"
#pragma clang diagnostic ignored "-Wobjc-missing-property-synthesis"
#pragma clang diagnostic ignored "-Wdirect-ivar-access"


NSString *const FastCodingException = @"FastCodingException";


static const uint32_t FCIdentifier = 'FAST';
static const uint16_t FCMajorVersion = 3;
static const uint16_t FCMinorVersion = 0;


typedef struct
{
    uint32_t identifier;
    uint16_t majorVersion;
    uint16_t minorVersion;
}
FCHeader;


typedef NS_ENUM(uint8_t, FCType)
{
    FCTypeNil = 0,
    FCTypeNull,
    FCTypeObjectAlias8,
    FCTypeObjectAlias16,
    FCTypeObjectAlias32,
    FCTypeStringAlias8,
    FCTypeStringAlias16,
    FCTypeStringAlias32,
    FCTypeString,
    FCTypeDictionary,
    FCTypeArray,
    FCTypeSet,
    FCTypeOrderedSet,
    FCTypeTrue,
    FCTypeFalse,
    FCTypeInt8,
    FCTypeInt16,
    FCTypeInt32,
    FCTypeInt64,
    FCTypeFloat32,
    FCTypeFloat64,
    FCTypeData,
    FCTypeDate,
    FCTypeMutableString,
    FCTypeMutableDictionary,
    FCTypeMutableArray,
    FCTypeMutableSet,
    FCTypeMutableOrderedSet,
    FCTypeMutableData,
    FCTypeClassDefinition,
    FCTypeObject8,
    FCTypeObject16,
    FCTypeObject32,
    FCTypeURL,
    FCTypePoint,
    FCTypeSize,
    FCTypeRect,
    FCTypeRange,
    FCTypeVector,
    FCTypeAffineTransform,
    FCType3DTransform,
    FCTypeMutableIndexSet,
    FCTypeIndexSet,
    FCTypeNSCodedObject,
  
    FCTypeCount // sentinel value
};


#if !__has_feature(objc_arc)
#define FC_AUTORELEASE(x) [(x) autorelease]
#else
#define FC_AUTORELEASE(x) (x)
#endif


#import <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define OR_IF_MAC(x)
#else
#define OR_IF_MAC(x) || (x)
#endif


#define FC_ASSERT_FITS(length, offset, total) { if ((NSUInteger)((offset) + (length)) > (total)) \
[NSException raise:FastCodingException format:@"Unexpected EOF when parsing object starting at %i", (int32_t)(offset)]; }

#define FC_READ_VALUE(type, offset, input, total) type value; { \
FC_ASSERT_FITS (sizeof(type), offset, total); \
value = *(type *)(input + offset); \
offset += sizeof(value); }

#define FC_ALIGN_INPUT(type, offset) { \
unsigned long align = offset % sizeof(type); \
if (align) offset += sizeof(type) - align; }

#define FC_ALIGN_OUTPUT(type, output) { \
unsigned long align = [output length] % sizeof(type); \
if (align) [output increaseLengthBy:sizeof(type) - align]; }


@interface FCNSCoder : NSCoder

@end


@interface FCNSCoder ()
{
    
@public
    __unsafe_unretained id _rootObject;
    __unsafe_unretained NSMutableData *_output;
    __unsafe_unretained NSMutableDictionary *_objectCache;
    __unsafe_unretained NSMutableDictionary *_classCache;
    __unsafe_unretained NSMutableDictionary *_stringCache;
    __unsafe_unretained NSMutableDictionary *_classesByName;
}

@end


@interface FCNSDecoder : NSCoder

@end


typedef id FCTypeConstructor(FCNSDecoder *);


@interface FCNSDecoder ()
{
    
@public
    NSUInteger *_offset;
    const void *_input;
    NSUInteger _total;
    FCTypeConstructor **_constructors;
    __unsafe_unretained NSData *_objectCache;
    __unsafe_unretained NSData *_classCache;
    __unsafe_unretained NSData *_stringCache;
    __unsafe_unretained NSMutableArray *_propertyDictionaryPool;
    __unsafe_unretained NSMutableDictionary *_properties;
}

@end


@interface FCClassDefinition : NSObject

@end


@interface FCClassDefinition ()
{

@public
    __unsafe_unretained NSString *_className;
    __unsafe_unretained NSArray *_propertyKeys;
}

@end


@interface NSObject (FastCoding_Private)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder;

@end


static inline NSUInteger FCCacheReadObject(__unsafe_unretained id object, __unsafe_unretained NSData *cache)
{
    NSUInteger offset = (NSUInteger)CFDataGetLength((__bridge CFMutableDataRef)cache);
    CFDataAppendBytes((__bridge CFMutableDataRef)cache, (void *)&object, sizeof(id));
    return offset;
}

static inline void FCReplaceCachedObject(NSUInteger index, __unsafe_unretained id object, __unsafe_unretained NSData *cache)
{
    CFDataReplaceBytes((__bridge CFMutableDataRef)cache, CFRangeMake((CFIndex)index, sizeof(id)), (void *)&object, sizeof(id));
}

static inline id FCCachedObjectAtIndex(NSUInteger index, __unsafe_unretained NSData *cache)
{
    return ((__unsafe_unretained id *)(void *)CFDataGetBytePtr((__bridge CFMutableDataRef)cache))[index];
}


static id FCReadObject(__unsafe_unretained FCNSDecoder *decoder);
static id FCReadObject_2_3(__unsafe_unretained FCNSDecoder *decoder);

static inline uint8_t FCReadType(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(uint8_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static inline uint8_t FCReadRawUInt8(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(uint8_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static inline uint16_t FCReadRawUInt16(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(uint16_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static inline uint32_t FCReadRawUInt32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(uint32_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static inline double FCReadRawDouble(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(double_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static id FCReadRawString(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSString *string = nil;
    NSUInteger length = strlen(decoder->_input + *decoder->_offset) + 1;
    FC_ASSERT_FITS(length, *decoder->_offset, decoder->_total);
    if (length > 1)
    {
        string = CFBridgingRelease(CFStringCreateWithBytes(NULL, decoder->_input + *decoder->_offset,
                                                           (CFIndex)length - 1, kCFStringEncodingUTF8, false));
    }
    else
    {
        string = @"";
    }
    *decoder->_offset += length;
    return string;
}

static id FCReadNil(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return nil;
}

static id FCReadNull(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return [NSNull null];
}

static id FCReadAlias8(__unsafe_unretained FCNSDecoder *decoder)
{
  FC_ALIGN_INPUT(uint8_t, *decoder->_offset);
  return FCCachedObjectAtIndex(FCReadRawUInt8(decoder), decoder->_objectCache);
}

static id FCReadAlias16(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint16_t, *decoder->_offset);
    return FCCachedObjectAtIndex(FCReadRawUInt16(decoder), decoder->_objectCache);
}

static id FCReadAlias32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    return FCCachedObjectAtIndex(FCReadRawUInt32(decoder), decoder->_objectCache);
}

static id FCReadStringAlias8(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint8_t, *decoder->_offset);
    return FCCachedObjectAtIndex(FCReadRawUInt8(decoder), decoder->_stringCache);
}

static id FCReadStringAlias16(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint16_t, *decoder->_offset);
    return FCCachedObjectAtIndex(FCReadRawUInt16(decoder), decoder->_stringCache);
}

static id FCReadStringAlias32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    return FCCachedObjectAtIndex(FCReadRawUInt32(decoder), decoder->_stringCache);
}

static id FCReadString(__unsafe_unretained FCNSDecoder *decoder)
{
    NSString *string = FCReadRawString(decoder);
    FCCacheReadObject(string, decoder->_stringCache);
    return string;
}

static id FCReadMutableString(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSMutableString *string = nil;
    NSUInteger length = strlen(decoder->_input + *decoder->_offset) + 1;
    FC_ASSERT_FITS(length, *decoder->_offset, decoder->_total);
    if (length > 1)
    {
        string = FC_AUTORELEASE([[NSMutableString alloc] initWithBytes:decoder->_input + *decoder->_offset length:length - 1 encoding:NSUTF8StringEncoding]);
    }
    else
    {
        string = [NSMutableString string];
    }
    *decoder->_offset += length;
    FCCacheReadObject(string, decoder->_objectCache);
    return string;
}

static id FCReadDictionary(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSDictionary *dict = nil;
    if (count)
    {        
        __autoreleasing id *keys = (__autoreleasing id *)malloc(count * sizeof(id));
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject(decoder);
            keys[i] = FCReadObject(decoder);
        }
        
        dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count];
        free(objects);
        free(keys);
    }
    else
    {
        dict = @{};
    }
    FCCacheReadObject(dict, decoder->_objectCache);
    return dict;
}

static id FCReadMutableDictionary(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSMutableDictionary *dict = CFBridgingRelease(CFDictionaryCreateMutable(NULL, (CFIndex)count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    FCCacheReadObject(dict, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        __autoreleasing id object = FCReadObject(decoder);
        __autoreleasing id key = FCReadObject(decoder);
        CFDictionarySetValue((__bridge CFMutableDictionaryRef)dict, (__bridge const void *)key, (__bridge const void *)object);
    }
    return dict;
}

static id FCReadArray(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSArray *array = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject(decoder);
        }
        array = [NSArray arrayWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        array = @[];
    }
    FCCacheReadObject(array, decoder->_objectCache);
    return array;
}

static id FCReadMutableArray(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
    FCCacheReadObject(array, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        CFArrayAppendValue((__bridge CFMutableArrayRef)array, (__bridge void *)FCReadObject(decoder));
    }
    return array;
}

static id FCReadSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSSet *set = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject(decoder);
        }
        set = [NSSet setWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        set = [NSSet set];
    }
    FCCacheReadObject(set, decoder->_objectCache);
    return set;
}

static id FCReadMutableSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSMutableSet *set = [NSMutableSet setWithCapacity:count];
    FCCacheReadObject(set, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        [set addObject:FCReadObject(decoder)];
    }
    return set;
}

static id FCReadOrderedSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSOrderedSet *set = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject(decoder);
        }
        set = [NSOrderedSet orderedSetWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        set = [NSOrderedSet orderedSet];
    }
    FCCacheReadObject(set, decoder->_objectCache);
    return set;
}

static id FCReadMutableOrderedSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    __autoreleasing NSMutableOrderedSet *set = [NSMutableOrderedSet orderedSetWithCapacity:count];
    FCCacheReadObject(set, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        [set addObject:FCReadObject(decoder)];
    }
    return set;
}

static id FCReadTrue(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return @YES;
}

static id FCReadFalse(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return @NO;
}

static id FCReadInt8(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(int8_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadInt16(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(int16_t, *decoder->_offset);
    FC_READ_VALUE(int16_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadInt32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(int32_t, *decoder->_offset);
    FC_READ_VALUE(int32_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadInt64(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(int64_t, *decoder->_offset);
    FC_READ_VALUE(int64_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadFloat32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(float_t, *decoder->_offset);
    FC_READ_VALUE(float_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadFloat64(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    FC_READ_VALUE(double_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    return number;
}

static id FCReadData(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t length = FCReadRawUInt32(decoder);
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    __autoreleasing NSData *data = [NSData dataWithBytes:(decoder->_input + *decoder->_offset) length:length];
    *decoder->_offset += paddedLength;
    FCCacheReadObject(data, decoder->_objectCache);
    return data;
}

static id FCReadMutableData(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t length = FCReadRawUInt32(decoder);
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    __autoreleasing NSMutableData *data = [NSMutableData dataWithBytes:(decoder->_input + *decoder->_offset) length:length];
    *decoder->_offset += paddedLength;
    FCCacheReadObject(data, decoder->_objectCache);
    return data;
}

static id FCReadDate(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(NSTimeInterval, *decoder->_offset);
    FC_READ_VALUE(NSTimeInterval, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSDate *date = [NSDate dateWithTimeIntervalSince1970:value];
    return date;
}

static id FCReadClassDefinition(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing FCClassDefinition *definition = FC_AUTORELEASE([[FCClassDefinition alloc] init]);
    FCCacheReadObject(definition, decoder->_classCache);
    definition->_className = FCReadRawString(decoder);
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t count = FCReadRawUInt32(decoder);
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadRawString(decoder);
        }
        __autoreleasing NSArray *propertyKeys = [NSArray arrayWithObjects:objects count:count];
        definition->_propertyKeys = propertyKeys;
        free(objects);
    }
    
    //now return the actual object instance
    return FCReadObject(decoder);
}

static id FCReadObjectInstance(__unsafe_unretained FCNSDecoder *decoder, NSUInteger classIndex)
{
    __autoreleasing FCClassDefinition *definition = FCCachedObjectAtIndex(classIndex, decoder->_classCache);
    __autoreleasing Class objectClass = NSClassFromString(definition->_className);
    __autoreleasing id object = nil;
    if (objectClass)
    {
        object = FC_AUTORELEASE([[objectClass alloc] init]);
    }
    else if (definition->_className)
    {
        object = [NSMutableDictionary dictionaryWithObject:definition->_className forKey:@"$class"];
    }
    else if (object)
    {
        object = [NSMutableDictionary dictionary];
    }
    NSUInteger cacheIndex = FCCacheReadObject(object, decoder->_objectCache);
    for (__unsafe_unretained NSString *key in definition->_propertyKeys)
    {
        [object setValue:FCReadObject(decoder) forKey:key];
    }
    id newObject = [object awakeAfterFastCoding];
    if (newObject != object)
    {
        //TODO: this is only a partial solution, as any objects that referenced
        //this object between when it was created and now will have received incorrect instance
        FCReplaceCachedObject(cacheIndex, newObject, decoder->_objectCache);
    }
    return newObject;
}

static id FCReadObject8(__unsafe_unretained FCNSDecoder *decoder)
{
    return FCReadObjectInstance(decoder, FCReadRawUInt8(decoder));
}

static id FCReadObject16(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint16_t, *decoder->_offset);
    return FCReadObjectInstance(decoder, FCReadRawUInt16(decoder));
}

static id FCReadObject32(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    return FCReadObjectInstance(decoder, FCReadRawUInt32(decoder));
}

static id FCReadURL(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSURL *URL = [NSURL URLWithString:FCReadObject(decoder) relativeToURL:FCReadObject(decoder)];
    FCCacheReadObject(URL, decoder->_stringCache);
    return URL;
}

static id FCReadPoint(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGPoint point = {(CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)};
    NSValue *value = [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
    return value;
}

static id FCReadSize(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGSize size = {(CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)};
    NSValue *value = [NSValue valueWithBytes:&size objCType:@encode(CGSize)];
    return value;
}

static id FCReadRect(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGRect rect =
    {
        {(CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)},
        {(CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)}
    };
    NSValue *value = [NSValue valueWithBytes:&rect objCType:@encode(CGRect)];
    return value;
}

static id FCReadRange(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    NSRange range = {FCReadRawUInt32(decoder), FCReadRawUInt32(decoder)};
    NSValue *value = [NSValue valueWithBytes:&range objCType:@encode(NSRange)];
    return value;
}

static id FCReadVector(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGVector point = {(CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)};
    NSValue *value = [NSValue valueWithBytes:&point objCType:@encode(CGVector)];
    return value;
}

static id FCReadAffineTransform(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGAffineTransform transform =
    {
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)
    };
    NSValue *value = [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
    return value;
}

static id FCRead3DTransform(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(double_t, *decoder->_offset);
    CGFloat transform[] =
    {
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder),
        (CGFloat)FCReadRawDouble(decoder), (CGFloat)FCReadRawDouble(decoder)
    };
    NSValue *value = [NSValue valueWithBytes:&transform objCType:@encode(CGFloat[16])];
    return value;
}

static id FCReadMutableIndexSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t rangeCount = FCReadRawUInt32(decoder);
    __autoreleasing NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
    FCCacheReadObject(indexSet, decoder->_objectCache);
    for (uint32_t i = 0; i < rangeCount; i++)
    {
        NSRange range = {FCReadRawUInt32(decoder), FCReadRawUInt32(decoder)};
        [indexSet addIndexesInRange:range];
    }
    return indexSet;
}

static id FCReadIndexSet(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_ALIGN_INPUT(uint32_t, *decoder->_offset);
    uint32_t rangeCount = FCReadRawUInt32(decoder);
    __autoreleasing NSIndexSet *indexSet;
    if (rangeCount == 1)
    {
        //common case optimisation
        NSRange range = {FCReadRawUInt32(decoder), FCReadRawUInt32(decoder)};
        indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
    }
    else
    {
        indexSet = [NSMutableIndexSet indexSet];
        for (uint32_t i = 0; i < rangeCount; i++)
        {
            NSRange range = {FCReadRawUInt32(decoder), FCReadRawUInt32(decoder)};
            [(NSMutableIndexSet *)indexSet addIndexesInRange:range];
        }
        indexSet = [indexSet copy];
        
    }
    FCCacheReadObject(indexSet, decoder->_objectCache);
    return indexSet;
}

static id FCReadNSCodedObject(__unsafe_unretained FCNSDecoder *decoder)
{
    NSString *className = FCReadObject(decoder);
    NSMutableDictionary *oldProperties = decoder->_properties;
    if ([decoder->_propertyDictionaryPool count])
    {
        decoder->_properties = [decoder->_propertyDictionaryPool lastObject];
        [decoder->_propertyDictionaryPool removeLastObject];
        [decoder->_properties removeAllObjects];
    }
    else
    {
        const CFDictionaryKeyCallBacks stringKeyCallbacks =
        {
            0,
            NULL,
            NULL,
            NULL,
            CFEqual,
            CFHash
        };
      
        decoder->_properties = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, &stringKeyCallbacks, NULL));
    }
    while (true)
    {
        id object = FCReadObject(decoder);
        if (!object) break;
        NSString *key = FCReadObject(decoder);
        decoder->_properties[key] = object;
    }
    id object = [[NSClassFromString(className) alloc] initWithCoder:decoder];
    [decoder->_propertyDictionaryPool addObject:decoder->_properties];
    decoder->_properties = oldProperties;
    FCCacheReadObject(object, decoder->_objectCache);
    return object;
}

static id FCReadObject(__unsafe_unretained FCNSDecoder *decoder)
{
    FCType type = FCReadType(decoder);
    FCTypeConstructor *constructor = NULL;
    if (type < FCTypeCount)
    {
        constructor = decoder->_constructors[type];
    }
    if (!constructor)
    {
        [NSException raise:FastCodingException format:@"FastCoding cannot decode object of type: %i", type];
        return nil;
    }
    return constructor(decoder);
}

id FCParseData(NSData *data, FCTypeConstructor *constructors[])
{
    NSUInteger length = [data length];
    if (length < sizeof(FCHeader))
    {
        //not a valid FastArchive
        return nil;
    }
    
    //read header
    FCHeader header;
    const void *input = data.bytes;
    memcpy(&header, input, sizeof(header));
    if (header.identifier != FCIdentifier)
    {
        //not a FastArchive
        return nil;
    }
    if (header.majorVersion < 2 || header.majorVersion > FCMajorVersion)
    {
        //not compatible
        NSLog(@"This version of the FastCoding library doesn't support FastCoding version %i.%i files", header.majorVersion, header.minorVersion);
        return nil;
    }
    
    //create decoder
    NSUInteger offset = sizeof(header);
    FCNSDecoder *decoder = FC_AUTORELEASE([[FCNSDecoder alloc] init]);
    decoder->_constructors = constructors;
    decoder->_input = input;
    decoder->_offset = &offset;
    decoder->_total = length;
    
    //read data
    __autoreleasing NSMutableData *objectCache = [NSMutableData dataWithCapacity:FCReadRawUInt32(decoder) * sizeof(id)];
    decoder->_objectCache = objectCache;
    if (header.majorVersion < 3)
    {
        return FCReadObject_2_3(decoder);
    }
    else
    {
        __autoreleasing NSMutableData *classCache = [NSMutableData dataWithCapacity:FCReadRawUInt32(decoder) * sizeof(id)];
        __autoreleasing NSMutableData *stringCache = [NSMutableData dataWithCapacity:FCReadRawUInt32(decoder) * sizeof(id)];
        __autoreleasing NSMutableArray *propertyDictionaryPool = CFBridgingRelease(CFArrayCreateMutable(NULL, 0, NULL));
      
        decoder->_classCache = classCache;
        decoder->_stringCache = stringCache;
        decoder->_propertyDictionaryPool = propertyDictionaryPool;
      
        @try
        {
            return FCReadObject(decoder);
        }
        @catch (NSException *exception)
        {
            NSLog(@"%@", [exception reason]);
            return nil;
        }
    }
}

static inline NSUInteger FCCacheWrittenObject(__unsafe_unretained id object, __unsafe_unretained NSMutableDictionary *cache)
{
    NSUInteger count = (NSUInteger)CFDictionaryGetCount((CFMutableDictionaryRef)cache);
    CFDictionarySetValue((CFMutableDictionaryRef)cache, (__bridge const void *)(object), (const void *)(count + 1));
    return count;
}

static inline NSUInteger FCIndexOfCachedObject(__unsafe_unretained id object, __unsafe_unretained NSMutableDictionary *cache)
{
    const void *index = CFDictionaryGetValue((__bridge CFMutableDictionaryRef)cache, (__bridge const void *)object);
    if (index)
    {
        return ((NSUInteger)index) - 1;
    }
    return NSNotFound;
}

static inline void FCWriteType(FCType value, __unsafe_unretained NSMutableData *output)
{
    [output appendBytes:&value length:sizeof(value)];
}

static inline void FCWriteUInt8(uint8_t value, __unsafe_unretained NSMutableData *output)
{
    [output appendBytes:&value length:sizeof(value)];
}

static inline void FCWriteUInt16(uint16_t value, __unsafe_unretained NSMutableData *output)
{
    [output appendBytes:&value length:sizeof(value)];
}

static inline void FCWriteUInt32(uint32_t value, __unsafe_unretained NSMutableData *output)
{
    [output appendBytes:&value length:sizeof(value)];
}

static inline void FCWriteDouble(double_t value, __unsafe_unretained NSMutableData *output)
{
    [output appendBytes:&value length:sizeof(value)];
}

static inline void FCWriteString(__unsafe_unretained NSString *string, __unsafe_unretained NSMutableData *output)
{
    const char *utf8 = [string UTF8String];
    NSUInteger length = strlen(utf8) + 1;
    [output appendBytes:utf8 length:length];
}

static inline BOOL FCWriteObjectAlias(__unsafe_unretained id object, __unsafe_unretained FCNSCoder *coder)
{
    NSUInteger index = FCIndexOfCachedObject(object, coder->_objectCache);
    if (index <= UINT8_MAX)
    {
        FCWriteType(FCTypeObjectAlias8, coder->_output);
        FCWriteUInt8((uint8_t)index, coder->_output);
        return YES;
    }
    else if (index <= UINT16_MAX)
    {
        FCWriteType(FCTypeObjectAlias16, coder->_output);
        FC_ALIGN_OUTPUT(uint16_t, coder->_output);
        FCWriteUInt16((uint16_t)index, coder->_output);
        return YES;
    }
    else if (index != NSNotFound)
    {
        FCWriteType(FCTypeObjectAlias32, coder->_output);
        FC_ALIGN_OUTPUT(uint32_t, coder->_output);
        FCWriteUInt32((uint32_t)index, coder->_output);
        return YES;
    }
    else
    {
        return NO;
    }
}

static inline BOOL FCWriteStringAlias(__unsafe_unretained id object, __unsafe_unretained FCNSCoder *coder)
{
    NSUInteger index = FCIndexOfCachedObject(object, coder->_stringCache);
    if (index <= UINT8_MAX)
    {
      FCWriteType(FCTypeStringAlias8, coder->_output);
      FCWriteUInt8((uint8_t)index, coder->_output);
      return YES;
    }
    else if (index <= UINT16_MAX)
    {
        FCWriteType(FCTypeStringAlias16, coder->_output);
        FC_ALIGN_OUTPUT(uint16_t, coder->_output);
        FCWriteUInt16((uint16_t)index, coder->_output);
        return YES;
    }
    else if (index != NSNotFound)
    {
        FCWriteType(FCTypeStringAlias32, coder->_output);
        FC_ALIGN_OUTPUT(uint32_t, coder->_output);
        FCWriteUInt32((uint32_t)index, coder->_output);
        return YES;
    }
    else
    {
        return NO;
    }
}

static void FCWriteObject(__unsafe_unretained id object, __unsafe_unretained FCNSCoder *coder)
{
    if (object)
    {
        [object FC_encodeWithCoder:coder];
    }
    else
    {
        FCWriteType(FCTypeNil, coder->_output);
    }
}


@implementation FastCoder

+ (id)objectWithData:(NSData *)data
{
    static FCTypeConstructor *constructors[] =
    {
        FCReadNil,
        FCReadNull,
        FCReadAlias8,
        FCReadAlias16,
        FCReadAlias32,
        FCReadStringAlias8,
        FCReadStringAlias16,
        FCReadStringAlias32,
        FCReadString,
        FCReadDictionary,
        FCReadArray,
        FCReadSet,
        FCReadOrderedSet,
        FCReadTrue,
        FCReadFalse,
        FCReadInt8,
        FCReadInt16,
        FCReadInt32,
        FCReadInt64,
        FCReadFloat32,
        FCReadFloat64,
        FCReadData,
        FCReadDate,
        FCReadMutableString,
        FCReadMutableDictionary,
        FCReadMutableArray,
        FCReadMutableSet,
        FCReadMutableOrderedSet,
        FCReadMutableData,
        FCReadClassDefinition,
        FCReadObject8,
        FCReadObject16,
        FCReadObject32,
        FCReadURL,
        FCReadPoint,
        FCReadSize,
        FCReadRect,
        FCReadRange,
        FCReadVector,
        FCReadAffineTransform,
        FCRead3DTransform,
        FCReadMutableIndexSet,
        FCReadIndexSet,
        FCReadNSCodedObject
    };
  
    return FCParseData(data, constructors);
}

+ (id)propertyListWithData:(NSData *)data
{
    static FCTypeConstructor *constructors[] =
    {
        NULL,
        FCReadNull,
        FCReadAlias8,
        FCReadAlias16,
        FCReadAlias32,
        FCReadStringAlias8,
        FCReadStringAlias16,
        FCReadStringAlias32,
        FCReadString,
        FCReadDictionary,
        FCReadArray,
        FCReadSet,
        FCReadOrderedSet,
        FCReadTrue,
        FCReadFalse,
        FCReadInt8,
        FCReadInt16,
        FCReadInt32,
        FCReadInt64,
        FCReadFloat32,
        FCReadFloat64,
        FCReadData,
        FCReadDate,
        FCReadMutableString,
        FCReadMutableDictionary,
        FCReadMutableArray,
        FCReadMutableSet,
        FCReadMutableOrderedSet,
        FCReadMutableData,
        NULL,
        NULL,
        NULL,
        NULL,
        FCReadURL,
        FCReadPoint,
        FCReadSize,
        FCReadRect,
        FCReadRange,
        FCReadVector,
        FCReadAffineTransform,
        FCRead3DTransform,
        FCReadIndexSet,
        FCReadIndexSet,
        NULL
    };
  
    return FCParseData(data, constructors);
}

+ (NSData *)dataWithRootObject:(id)object
{
    if (object)
    {
        //write header
        FCHeader header = {FCIdentifier, FCMajorVersion, FCMinorVersion};
        NSMutableData *output = [NSMutableData dataWithLength:sizeof(header)];
        memcpy(output.mutableBytes, &header, sizeof(header));
        
        //object count placeholders
        FCWriteUInt32(0, output);
        FCWriteUInt32(0, output);
        FCWriteUInt32(0, output);
        
        //set up cache
        const CFDictionaryKeyCallBacks stringKeyCallbacks =
        {
            0,
            NULL,
            NULL,
            NULL,
            CFEqual,
            CFHash
        };
        
        @autoreleasepool
        {
            __autoreleasing id objectCache = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, NULL, NULL));
            __autoreleasing id classCache = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, NULL, NULL));
            __autoreleasing id stringCache = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, &stringKeyCallbacks, NULL));
            __autoreleasing id classesByName = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, &stringKeyCallbacks, NULL));
          
            //create coder
            FCNSCoder *coder = FC_AUTORELEASE([[FCNSCoder alloc] init]);
            coder->_rootObject = object;
            coder->_output = output;
            coder->_objectCache = objectCache;
            coder->_classCache = classCache;
            coder->_stringCache = stringCache;
            coder->_classesByName = classesByName;
            
            //write object
            FCWriteObject(object, coder);
            
            //set object count
            uint32_t objectCount = (uint32_t)[objectCache count];
            [output replaceBytesInRange:NSMakeRange(sizeof(header), sizeof(uint32_t)) withBytes:&objectCount];
          
            //set class count
            uint32_t classCount = (uint32_t)[classCache count];
            [output replaceBytesInRange:NSMakeRange(sizeof(header) + sizeof(uint32_t), sizeof(uint32_t)) withBytes:&classCount];
          
            //set string count
            uint32_t stringCount = (uint32_t)[stringCache count];
            [output replaceBytesInRange:NSMakeRange(sizeof(header) + sizeof(uint32_t) * 2, sizeof(uint32_t)) withBytes:&stringCount];
            
            return output;
        }
    }
    return nil;
}

@end


@implementation FCNSCoder

- (BOOL)allowsKeyedCoding
{
    return YES;
}

- (void)encodeObject:(__unsafe_unretained id)objv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(objv, self);
    FCWriteObject(key, self);
}

- (void)encodeConditionalObject:(id)objv forKey:(__unsafe_unretained NSString *)key
{
    if (FCIndexOfCachedObject(objv, _objectCache) != NSNotFound)
    {
        FCWriteObject(objv, self);
        FCWriteObject(key, self);
    }
}

- (void)encodeBool:(BOOL)boolv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(boolv), self);
    FCWriteObject(key, self);
}

- (void)encodeInt:(int)intv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(intv), self);
    FCWriteObject(key, self);
}

- (void)encodeInteger:(NSInteger)intv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(intv), self);
    FCWriteObject(key, self);
}

- (void)encodeInt32:(int32_t)intv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(intv), self);
    FCWriteObject(key, self);
}

- (void)encodeInt64:(int64_t)intv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(intv), self);
    FCWriteObject(key, self);
}

- (void)encodeFloat:(float)realv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(realv), self);
    FCWriteObject(key, self);
}

- (void)encodeDouble:(double)realv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject(@(realv), self);
    FCWriteObject(key, self);
}

- (void)encodeBytes:(const uint8_t *)bytesp length:(NSUInteger)lenv forKey:(__unsafe_unretained NSString *)key
{
    FCWriteObject([NSData dataWithBytes:bytesp length:lenv], self);
    FCWriteObject(key, self);
}

@end


@implementation FCNSDecoder

- (BOOL)containsValueForKey:(NSString *)key
{
    return _properties[key] != nil;
}

- (id)decodeObjectForKey:(__unsafe_unretained NSString *)key
{
    return _properties[key];
}

- (BOOL)decodeBoolForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] boolValue];
}

- (int)decodeIntForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] intValue];
}

- (NSInteger)decodeIntegerForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] integerValue];
}

- (int32_t)decodeInt32ForKey:(__unsafe_unretained NSString *)key
{
    return (int32_t)[_properties[key] longValue];
}

- (int64_t)decodeInt64ForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] longLongValue];
}

- (float)decodeFloatForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] floatValue];
}

- (double)decodeDoubleForKey:(__unsafe_unretained NSString *)key
{
    return [_properties[key] doubleValue];
}

- (const uint8_t *)decodeBytesForKey:(__unsafe_unretained NSString *)key returnedLength:(NSUInteger *)lengthp
{
    __autoreleasing NSData *data = _properties[key];
    *lengthp = [data length];
    return data.bytes;
}

@end


@implementation FCClassDefinition : NSObject

//no encoding implementation needed

@end


@implementation NSObject (FastCoding)

+ (NSArray *)fastCodingKeys
{
    __autoreleasing NSMutableArray *codableKeys = [NSMutableArray array];
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList(self, &propertyCount);
    for (unsigned int i = 0; i < propertyCount; i++)
    {
        //get property
        objc_property_t property = properties[i];
        const char *propertyName = property_getName(property);
        NSString *key = @(propertyName);
        
        //see if there is a backing ivar
        char *ivar = property_copyAttributeValue(property, "V");
        if (ivar)
        {
            //check if ivar has KVC-compliant name
            NSString *ivarName = @(ivar);
            if ([ivarName isEqualToString:key] || [ivarName isEqualToString:[@"_" stringByAppendingString:key]])
            {
                //setValue:forKey: will work
                [codableKeys addObject:key];
            }
            free(ivar);
        }
    }
    free(properties);
    return codableKeys;
}

+ (NSArray *)FC_aggregatePropertyKeys
{
    __autoreleasing NSArray *codableKeys = nil;
    codableKeys = objc_getAssociatedObject(self, _cmd);
    if (codableKeys == nil)
    {
        codableKeys = [NSMutableArray array];
        Class subclass = [self class];
        while (subclass != [NSObject class])
        {
            [(NSMutableArray *)codableKeys addObjectsFromArray:[subclass fastCodingKeys]];
            subclass = [subclass superclass];
        }
        codableKeys = [NSArray arrayWithArray:codableKeys];
        
        //make the association atomically so that we don't need to bother with an @synchronize
        objc_setAssociatedObject(self, _cmd, codableKeys, OBJC_ASSOCIATION_RETAIN);
    }
    return codableKeys;
}

- (id)awakeAfterFastCoding
{
    return self;
}

- (Class)classForFastCoding
{
    return [self classForCoder];
}

- (BOOL)preferFastCoding
{
    return NO;
}

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    
    //handle NSCoding
    if (![self preferFastCoding] && [self conformsToProtocol:@protocol(NSCoding)])
    {
        //write object
        FCWriteType(FCTypeNSCodedObject, coder->_output);
        FCWriteObject(NSStringFromClass([self classForCoder]), coder);
        [(id <NSCoding>)self encodeWithCoder:coder];
        FCWriteType(FCTypeNil, coder->_output);
        FCCacheWrittenObject(self, coder->_objectCache);
        return;
    }
    
    //write class definition
    Class objectClass = [self classForFastCoding];
    NSUInteger classIndex = FCIndexOfCachedObject(objectClass, coder->_classCache);
    __autoreleasing NSArray *propertyKeys = [objectClass FC_aggregatePropertyKeys];
    if (classIndex == NSNotFound)
    {
        classIndex = FCCacheWrittenObject(objectClass, coder->_classCache);
        FCWriteType(FCTypeClassDefinition, coder->_output);
        FCWriteString(NSStringFromClass(objectClass), coder->_output);
        FC_ALIGN_OUTPUT(uint32_t, coder->_output);
        FCWriteUInt32((uint32_t)[propertyKeys count], coder->_output);
        for (__unsafe_unretained id value in propertyKeys)
        {
            FCWriteString(value, coder->_output);
        }
    }

    //write object
    FCCacheWrittenObject(self, coder->_objectCache);
    if (classIndex <= UINT8_MAX)
    {
        FCWriteType(FCTypeObject8, coder->_output);
        FCWriteUInt8((uint8_t)classIndex, coder->_output);
    }
    else if (classIndex <= UINT16_MAX)
    {
        FCWriteType(FCTypeObject16, coder->_output);
        FC_ALIGN_OUTPUT(uint16_t, coder->_output);
        FCWriteUInt16((uint16_t)classIndex, coder->_output);
    }
    else
    {
        FCWriteType(FCTypeObject32, coder->_output);
        FC_ALIGN_OUTPUT(uint32_t, coder->_output);
        FCWriteUInt32((uint32_t)classIndex, coder->_output);
    }
    for (__unsafe_unretained NSString *key in propertyKeys)
    {
        FCWriteObject([self valueForKey:key], coder);
    }
}

@end


@implementation NSString (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if ([self classForCoder] == [NSMutableString class])
    {
        if (FCWriteObjectAlias(self, coder)) return;
        FCCacheWrittenObject(self, coder->_objectCache);
        FCWriteType(FCTypeMutableString, coder->_output);
    }
    else
    {
        if (FCWriteStringAlias(self, coder)) return;
        FCCacheWrittenObject(self, coder->_stringCache);
        FCWriteType(FCTypeString, coder->_output);
    }
    FCWriteString(self, coder->_output);
}

@end


@implementation NSNumber (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    switch (CFNumberGetType((CFNumberRef)self))
    {
        case kCFNumberFloat32Type:
        case kCFNumberFloatType:
        {
            FCWriteType(FCTypeFloat32, coder->_output);
            float_t value = [self floatValue];
            FC_ALIGN_OUTPUT(float_t, coder->_output);
            [coder->_output appendBytes:&value length:sizeof(value)];
            break;
        }
        case kCFNumberFloat64Type:
        case kCFNumberDoubleType:
        case kCFNumberCGFloatType:
        {
            FCWriteType(FCTypeFloat64, coder->_output);
            double_t value = [self doubleValue];
            FC_ALIGN_OUTPUT(double_t, coder->_output);
            [coder->_output appendBytes:&value length:sizeof(value)];
            break;
        }
        case kCFNumberSInt64Type:
        case kCFNumberLongLongType:
        case kCFNumberNSIntegerType:
        {
            int64_t value = [self longLongValue];
            if (value > (int64_t)INT32_MAX || value < (int64_t)INT32_MIN)
            {
                FCWriteType(FCTypeInt64, coder->_output);
                FC_ALIGN_OUTPUT(int64_t, coder->_output);
                [coder->_output appendBytes:&value length:sizeof(value)];
                break;
            }
            //otherwise treat as 32-bit
        }
        case kCFNumberSInt32Type:
        case kCFNumberIntType:
        case kCFNumberLongType:
        case kCFNumberCFIndexType:
        {
            int32_t value = (int32_t)[self intValue];
            if (value > (int32_t)INT16_MAX || value < (int32_t)INT16_MIN)
            {
                FCWriteType(FCTypeInt32, coder->_output);
                FC_ALIGN_OUTPUT(int32_t, coder->_output);
                [coder->_output appendBytes:&value length:sizeof(value)];
                break;
            }
            //otherwise treat as 16-bit
        }
        case kCFNumberSInt16Type:
        case kCFNumberShortType:
        {
            int16_t value = (int16_t)[self intValue];
            if (value > (int16_t)INT8_MAX || value < (int16_t)INT8_MIN)
            {
                FCWriteType(FCTypeInt16, coder->_output);
                FC_ALIGN_OUTPUT(int16_t, coder->_output);
                [coder->_output appendBytes:&value length:sizeof(value)];
                break;
            }
            //otherwise treat as 8-bit
        }
        case kCFNumberSInt8Type:
        case kCFNumberCharType:
        {
            int8_t value = (int8_t)[self intValue];
            if (value == 1)
            {
                FCWriteType(FCTypeTrue, coder->_output);
            }
            else if (value == 0)
            {
                FCWriteType(FCTypeFalse, coder->_output);
            }
            else
            {
                FCWriteType(FCTypeInt8, coder->_output);
                [coder->_output appendBytes:&value length:sizeof(value)];
            }
        }
    }
}

@end


@implementation NSDate (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(FCTypeDate, coder->_output);
    NSTimeInterval value = [self timeIntervalSince1970];
    FC_ALIGN_OUTPUT(NSTimeInterval, coder->_output);
    [coder->_output appendBytes:&value length:sizeof(value)];
}

@end


@implementation NSData (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(([self classForCoder] == [NSMutableData class])? FCTypeMutableData: FCTypeData, coder->_output);
    uint32_t length = (uint32_t)[self length];
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32(length, coder->_output);
    [coder->_output appendData:self];
    coder->_output.length += (4 - ((length % 4) ?: 4));
}

@end


@implementation NSNull (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    FCWriteType(FCTypeNull, coder->_output);
}

@end


@implementation NSDictionary (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    
    //alias keypath
    __autoreleasing NSString *aliasKeypath = self[@"$alias"];
    if ([self count] == 1 && aliasKeypath)
    {
        __autoreleasing id node = coder->_rootObject;
        NSArray *parts = [aliasKeypath componentsSeparatedByString:@"."];
        for (__unsafe_unretained NSString *key in parts)
        {
            if ([node isKindOfClass:[NSArray class]])
            {
                node = ((NSArray *)node)[(NSUInteger)[key integerValue]];
            }
            else
            {
                node = [node valueForKey:key];
            }
        }
        FCWriteObject(node, coder);
        return;
    }
    
    //object bootstrapping
    __autoreleasing NSString *className = self[@"$class"];
    if (className)
    {
        //get class definition
        __autoreleasing NSArray *propertyKeys = [[self allKeys] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self != '$class'"]];
        __autoreleasing FCClassDefinition *objectClass = coder->_classesByName[className];
        if (objectClass)
        {
            //check that existing class definition contains all keys
            __autoreleasing NSMutableArray *keys = nil;
            for (__unsafe_unretained id key in propertyKeys)
            {
                if (![objectClass->_propertyKeys containsObject:key])
                {
                    keys = keys ?: [NSMutableArray array];
                    [keys addObject:key];
                }
            }
            propertyKeys = objectClass->_propertyKeys;
            if (keys)
            {
                //we need to create a new class definition that includes extra keys
                propertyKeys = [propertyKeys arrayByAddingObjectsFromArray:keys];
                objectClass = nil;
            }
        }
        if (!objectClass)
        {
            //create class definition
            objectClass = FC_AUTORELEASE([[FCClassDefinition alloc] init]);
            objectClass->_className = className;
            objectClass->_propertyKeys = propertyKeys;
            coder->_classesByName[className] = objectClass;
        }
        
        //write class definition
        NSUInteger classIndex = FCIndexOfCachedObject(objectClass, coder->_classCache);
        if (classIndex == NSNotFound)
        {
            classIndex = FCCacheWrittenObject(objectClass, coder->_classCache);
            FCWriteType(FCTypeClassDefinition, coder->_output);
            FCWriteString(objectClass->_className, coder->_output);
            FC_ALIGN_OUTPUT(uint32_t, coder->_output);
            FCWriteUInt32((uint32_t)[propertyKeys count], coder->_output);
            for (__unsafe_unretained id key in propertyKeys)
            {
                //convert each to a string using -description, just in case
                FCWriteString([key description], coder->_output);
            }
        }
        
        //write object
        FCCacheWrittenObject(self, coder->_objectCache);
        if (classIndex <= UINT8_MAX)
        {
            FCWriteType(FCTypeObject8, coder->_output);
            FCWriteUInt8((uint8_t)classIndex, coder->_output);
        }
        else if (classIndex <= UINT16_MAX)
        {
            FCWriteType(FCTypeObject16, coder->_output);
            FC_ALIGN_OUTPUT(uint16_t, coder->_output);
            FCWriteUInt16((uint16_t)classIndex, coder->_output);
        }
        else
        {
            FCWriteType(FCTypeObject32, coder->_output);
            FC_ALIGN_OUTPUT(uint32_t, coder->_output);
            FCWriteUInt32((uint32_t)classIndex, coder->_output);
        }
        for (__unsafe_unretained NSString *key in propertyKeys)
        {
            FCWriteObject(self[key], coder);
        }
        return;
    }
    
    //ordinary dictionary
    BOOL mutable = ([self classForCoder] == [NSMutableDictionary class]);
    if (mutable) FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(mutable? FCTypeMutableDictionary: FCTypeDictionary, coder->_output);
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32((uint32_t)[self count], coder->_output);
    [self enumerateKeysAndObjectsUsingBlock:^(__unsafe_unretained id key, __unsafe_unretained id obj, __unused BOOL *stop) {
        FCWriteObject(obj, coder);
        FCWriteObject(key, coder);
    }];
    if (!mutable) FCCacheWrittenObject(self, coder->_objectCache);
}

@end


@implementation NSArray (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    BOOL mutable = ([self classForCoder] == [NSMutableArray class]);
    if (mutable) FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(mutable? FCTypeMutableArray: FCTypeArray, coder->_output);
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32((uint32_t)[self count], coder->_output);
    for (__unsafe_unretained id value in self)
    {
        FCWriteObject(value, coder);
    }
    if (!mutable) FCCacheWrittenObject(self, coder->_objectCache);
}

@end



@implementation NSSet (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    BOOL mutable = ([self classForCoder] == [NSMutableSet class]);
    if (mutable) FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(mutable? FCTypeMutableSet: FCTypeSet, coder->_output);
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32((uint32_t)[self count], coder->_output);
    for (__unsafe_unretained id value in self)
    {
        FCWriteObject(value, coder);
    }
    if (!mutable) FCCacheWrittenObject(self, coder->_objectCache);
}

@end


@implementation NSOrderedSet (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    BOOL mutable = ([self classForCoder] == [NSMutableOrderedSet class]);
    if (mutable) FCCacheWrittenObject(self, coder->_objectCache);
    FCWriteType(mutable? FCTypeMutableOrderedSet: FCTypeOrderedSet, coder->_output);
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32((uint32_t)[self count], coder->_output);
    for (__unsafe_unretained id value in self)
    {
        FCWriteObject(value, coder);
    }
    if (!mutable) FCCacheWrittenObject(self, coder->_objectCache);
}

@end


@implementation NSIndexSet (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteObjectAlias(self, coder)) return;
    
    BOOL mutable = ([self classForCoder] == [NSMutableIndexSet class]);
    if (mutable) FCCacheWrittenObject(self, coder->_objectCache);
    
    uint32_t __block rangeCount = 0; // wish we could get this directly from NSIndexSet...
    [self enumerateRangesUsingBlock:^(__unused NSRange range, __unused BOOL *stop) {
        rangeCount ++;
    }];

    FCWriteType(mutable? FCTypeMutableIndexSet: FCTypeIndexSet, coder->_output);
    FC_ALIGN_OUTPUT(uint32_t, coder->_output);
    FCWriteUInt32(rangeCount, coder->_output);
    [self enumerateRangesUsingBlock:^(NSRange range, __unused BOOL *stop) {
        FCWriteUInt32((uint32_t)range.location, coder->_output);
        FCWriteUInt32((uint32_t)range.length, coder->_output);
    }];
    
    if (!mutable) FCCacheWrittenObject(self, coder->_objectCache);
}

@end


@implementation NSURL (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    if (FCWriteStringAlias(self, coder)) return;
    FCWriteType(FCTypeURL, coder->_output);
    FCWriteObject(self.relativeString, coder);
    FCWriteObject(self.baseURL, coder);
    FCCacheWrittenObject(self, coder->_stringCache);
}

@end


@implementation NSValue (FastCoding)

- (void)FC_encodeWithCoder:(__unsafe_unretained FCNSCoder *)coder
{
    FCCacheWrittenObject(self, coder->_objectCache);
    const char *type = [self objCType];
    if (strcmp(type, @encode(CGPoint)) == 0 OR_IF_MAC(strcmp(type, @encode(NSPoint)) == 0))
    {
        CGFloat point[2];
        [self getValue:&point];
        FCWriteType(FCTypePoint, coder->_output);
        FC_ALIGN_OUTPUT(double_t, coder->_output);
        FCWriteDouble((double_t)point[0], coder->_output);
        FCWriteDouble((double_t)point[1], coder->_output);
    }
    else if (strcmp(type, @encode(CGSize)) == 0 OR_IF_MAC(strcmp(type, @encode(NSSize)) == 0))
    {
        CGFloat size[2];
        [self getValue:&size];
        FCWriteType(FCTypeSize, coder->_output);
        FC_ALIGN_OUTPUT(double_t, coder->_output);
        FCWriteDouble((double_t)size[0], coder->_output);
        FCWriteDouble((double_t)size[1], coder->_output);
    }
    else if (strcmp(type, @encode(CGRect)) == 0 OR_IF_MAC(strcmp(type, @encode(NSRect)) == 0))
    {
        CGFloat rect[4];
        [self getValue:&rect];
        FCWriteType(FCTypeRect, coder->_output);
        FC_ALIGN_OUTPUT(double_t, coder->_output);
        FCWriteDouble((double_t)rect[0], coder->_output);
        FCWriteDouble((double_t)rect[1], coder->_output);
        FCWriteDouble((double_t)rect[2], coder->_output);
        FCWriteDouble((double_t)rect[3], coder->_output);
    }
    else if (strcmp(type, @encode(NSRange)) == 0)
    {
        NSUInteger range[2];
        [self getValue:&range];
        FCWriteType(FCTypeRange, coder->_output);
        FC_ALIGN_OUTPUT(uint32_t, coder->_output);
        FCWriteUInt32((uint32_t)range[0], coder->_output);
        FCWriteUInt32((uint32_t)range[1], coder->_output);
    }
    else if (strcmp(type, @encode(CGVector)) == 0)
    {
        CGFloat vector[2];
        [self getValue:&vector];
        FCWriteType(FCTypeVector, coder->_output);
        FC_ALIGN_OUTPUT(double_t, coder->_output);
        FCWriteDouble((double_t)vector[0], coder->_output);
        FCWriteDouble((double_t)vector[1], coder->_output);
    }
    else if (strcmp(type, @encode(CGAffineTransform)) == 0)
    {
        CGFloat transform[6];
        [self getValue:&transform];
        FCWriteType(FCTypeAffineTransform, coder->_output);
        for (NSUInteger i = 0; i < 6; i++)
        {
            FCWriteDouble((double_t)transform[i], coder->_output);
        }
    }
    else if ([@(type) hasPrefix:@"{CATransform3D"])
    {
        CGFloat transform[16];
        [self getValue:&transform];
        FCWriteType(FCType3DTransform, coder->_output);
        FC_ALIGN_OUTPUT(double_t, coder->_output);
        for (NSUInteger i = 0; i < 16; i++)
        {
            FCWriteDouble((double_t)transform[i], coder->_output);
        }
    }
    else
    {
        [NSException raise:FastCodingException format:@"Unable to encode NSValue data of type %@", @(type)];
    }
}

@end


#pragma mark -
#pragma mark legacy decoding


static inline uint32_t FCReadRawUInt32_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(uint32_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static inline double FCReadRawDouble_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(double_t, *decoder->_offset, decoder->_input, decoder->_total);
    return value;
}

static id FCReadRawString_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSString *string = nil;
    NSUInteger length = strlen(decoder->_input + *decoder->_offset) + 1;
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    if (length > 1)
    {
        string = CFBridgingRelease(CFStringCreateWithBytes(NULL, decoder->_input + *decoder->_offset,
                                                           (CFIndex)length - 1, kCFStringEncodingUTF8, false));
    }
    else
    {
        string = @"";
    }
    *decoder->_offset += paddedLength;
    return string;
}

static id FCReadNull_2_3(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return [NSNull null];
}

static id FCReadAlias_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    return FCCachedObjectAtIndex(FCReadRawUInt32_2_3(decoder), decoder->_objectCache);
}

static id FCReadString_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    NSString *string = FCReadRawString_2_3(decoder);
    FCCacheReadObject(string, decoder->_objectCache);
    return string;
}

static id FCReadMutableString_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSMutableString *string = nil;
    NSUInteger length = strlen(decoder->_input + *decoder->_offset) + 1;
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    if (length > 1)
    {
        string = FC_AUTORELEASE([[NSMutableString alloc] initWithBytes:decoder->_input + *decoder->_offset length:length - 1 encoding:NSUTF8StringEncoding]);
    }
    else
    {
        string = [NSMutableString string];
    }
    *decoder->_offset += paddedLength;
    FCCacheReadObject(string, decoder->_objectCache);
    return string;
}

static id FCReadDictionary_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSDictionary *dict = nil;
    if (count)
    {
        __autoreleasing id *keys = (__autoreleasing id *)malloc(count * sizeof(id));
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject_2_3(decoder);
            keys[i] = FCReadObject_2_3(decoder);
        }
        
        dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count];
        free(objects);
        free(keys);
    }
    else
    {
        dict = @{};
    }
    FCCacheReadObject(dict, decoder->_objectCache);
    return dict;
}

static id FCReadMutableDictionary_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSMutableDictionary *dict = CFBridgingRelease(CFDictionaryCreateMutable(NULL, (CFIndex)count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    FCCacheReadObject(dict, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        __autoreleasing id object = FCReadObject_2_3(decoder);
        __autoreleasing id key = FCReadObject_2_3(decoder);
        CFDictionarySetValue((__bridge CFMutableDictionaryRef)dict, (__bridge const void *)key, (__bridge const void *)object);
    }
    return dict;
}

static id FCReadArray_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSArray *array = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject_2_3(decoder);
        }
        array = [NSArray arrayWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        array = @[];
    }
    FCCacheReadObject(array, decoder->_objectCache);
    return array;
}

static id FCReadMutableArray_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
    FCCacheReadObject(array, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        CFArrayAppendValue((__bridge CFMutableArrayRef)array, (__bridge void *)FCReadObject_2_3(decoder));
    }
    return array;
}

static id FCReadSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSSet *set = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject_2_3(decoder);
        }
        set = [NSSet setWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        set = [NSSet set];
    }
    FCCacheReadObject(set, decoder->_objectCache);
    return set;
}

static id FCReadMutableSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSMutableSet *set = [NSMutableSet setWithCapacity:count];
    FCCacheReadObject(set, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        [set addObject:FCReadObject_2_3(decoder)];
    }
    return set;
}

static id FCReadOrderedSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSOrderedSet *set = nil;
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadObject_2_3(decoder);
        }
        set = [NSOrderedSet orderedSetWithObjects:objects count:count];
        free(objects);
    }
    else
    {
        set = [NSOrderedSet orderedSet];
    }
    FCCacheReadObject(set, decoder->_objectCache);
    return set;
}

static id FCReadMutableOrderedSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSMutableOrderedSet *set = [NSMutableOrderedSet orderedSetWithCapacity:count];
    FCCacheReadObject(set, decoder->_objectCache);
    for (uint32_t i = 0; i < count; i++)
    {
        [set addObject:FCReadObject_2_3(decoder)];
    }
    return set;
}

static id FCReadTrue_2_3(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return @YES;
}

static id FCReadFalse_2_3(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return @NO;
}

static id FCReadInt32_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(int32_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    FCCacheReadObject(number, decoder->_objectCache);
    return number;
}

static id FCReadInt64_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(int64_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    FCCacheReadObject(number, decoder->_objectCache);
    return number;
}

static id FCReadfloat_t_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(float_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    FCCacheReadObject(number, decoder->_objectCache);
    return number;
}

static id FCReaddouble_t_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(double_t, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSNumber *number = @(value);
    FCCacheReadObject(number, decoder->_objectCache);
    return number;
}

static id FCReadData_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t length = FCReadRawUInt32_2_3(decoder);
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    __autoreleasing NSData *data = [NSData dataWithBytes:(decoder->_input + *decoder->_offset) length:length];
    *decoder->_offset += paddedLength;
    FCCacheReadObject(data, decoder->_objectCache);
    return data;
}

static id FCReadMutableData_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t length = FCReadRawUInt32_2_3(decoder);
    NSUInteger paddedLength = length + (4 - ((length % 4) ?: 4));
    FC_ASSERT_FITS(paddedLength, *decoder->_offset, decoder->_total);
    __autoreleasing NSMutableData *data = [NSMutableData dataWithBytes:(decoder->_input + *decoder->_offset) length:length];
    *decoder->_offset += paddedLength;
    FCCacheReadObject(data, decoder->_objectCache);
    return data;
}

static id FCReadDate_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    FC_READ_VALUE(NSTimeInterval, *decoder->_offset, decoder->_input, decoder->_total);
    __autoreleasing NSDate *date = [NSDate dateWithTimeIntervalSince1970:value];
    FCCacheReadObject(date, decoder->_objectCache);
    return date;
}

static id FCReadClassDefinition_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing FCClassDefinition *definition = FC_AUTORELEASE([[FCClassDefinition alloc] init]);
    FCCacheReadObject(definition, decoder->_objectCache);
    definition->_className = FCReadRawString_2_3(decoder);
    uint32_t count = FCReadRawUInt32_2_3(decoder);
    if (count)
    {
        __autoreleasing id *objects = (__autoreleasing id *)malloc(count * sizeof(id));
        for (uint32_t i = 0; i < count; i++)
        {
            objects[i] = FCReadRawString_2_3(decoder);
        }
        __autoreleasing NSArray *propertyKeys = [NSArray arrayWithObjects:objects count:count];
        definition->_propertyKeys = propertyKeys;
        free(objects);
    }
    
    //now return the actual object instance
    return FCReadObject_2_3(decoder);
}

static id FCReadObjectInstance_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing FCClassDefinition *definition = FCCachedObjectAtIndex(FCReadRawUInt32_2_3(decoder), decoder->_objectCache);
    __autoreleasing Class objectClass = NSClassFromString(definition->_className);
    __autoreleasing id object = nil;
    if (objectClass)
    {
        object = FC_AUTORELEASE([[objectClass alloc] init]);
    }
    else if (definition->_className)
    {
        object = [NSMutableDictionary dictionaryWithObject:definition->_className forKey:@"$class"];
    }
    else if (object)
    {
        object = [NSMutableDictionary dictionary];
    }
    NSUInteger cacheIndex = FCCacheReadObject(object, decoder->_objectCache);
    for (__unsafe_unretained NSString *key in definition->_propertyKeys)
    {
        [object setValue:FCReadObject_2_3(decoder) forKey:key];
    }
    id newObject = [object awakeAfterFastCoding];
    if (newObject != object)
    {
        //TODO: this is only a partial solution, as any objects that referenced
        //this object between when it was created and now will have received incorrect instance
        FCReplaceCachedObject(cacheIndex, newObject, decoder->_objectCache);
    }
    return newObject;
}

static id FCReadNil_2_3(__unused __unsafe_unretained FCNSDecoder *decoder)
{
    return nil;
}

static id FCReadURL_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSURL *URL = [NSURL URLWithString:FCReadObject_2_3(decoder) relativeToURL:FCReadObject_2_3(decoder)];
    FCCacheReadObject(URL, decoder->_objectCache);
    return URL;
}

static id FCReadPoint_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    CGPoint point = {(CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)};
    NSValue *value = [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCReadSize_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    CGSize size = {(CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)};
    NSValue *value = [NSValue valueWithBytes:&size objCType:@encode(CGSize)];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCReadRect_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    CGRect rect =
    {
        {(CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)},
        {(CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)}
    };
    NSValue *value = [NSValue valueWithBytes:&rect objCType:@encode(CGRect)];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCReadRange_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    NSRange range = {FCReadRawUInt32_2_3(decoder), FCReadRawUInt32_2_3(decoder)};
    NSValue *value = [NSValue valueWithBytes:&range objCType:@encode(NSRange)];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCReadAffineTransform_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    CGAffineTransform transform =
    {
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)
    };
    NSValue *value = [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCRead3DTransform_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    CGFloat transform[] =
    {
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder),
        (CGFloat)FCReadRawDouble_2_3(decoder), (CGFloat)FCReadRawDouble_2_3(decoder)
    };
    NSValue *value = [NSValue valueWithBytes:&transform objCType:@encode(CGFloat[16])];
    FCCacheReadObject(value, decoder->_objectCache);
    return value;
}

static id FCReadMutableIndexSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    uint32_t rangeCount = FCReadRawUInt32_2_3(decoder);
    __autoreleasing NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
    FCCacheReadObject(indexSet, decoder->_objectCache);
    for (uint32_t i = 0; i < rangeCount; i++)
    {
        NSRange range = {FCReadRawUInt32_2_3(decoder), FCReadRawUInt32_2_3(decoder)};
        [indexSet addIndexesInRange:range];
    }
    return indexSet;
}

static id FCReadIndexSet_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    __autoreleasing NSIndexSet *indexSet;
    uint32_t rangeCount = FCReadRawUInt32_2_3(decoder);
    if (rangeCount == 1)
    {
        //common case optimisation
        NSRange range = {FCReadRawUInt32_2_3(decoder), FCReadRawUInt32_2_3(decoder)};
        indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
    }
    else
    {
        indexSet = [NSMutableIndexSet indexSet];
        for (uint32_t i = 0; i < rangeCount; i++)
        {
            NSRange range = {FCReadRawUInt32_2_3(decoder), FCReadRawUInt32_2_3(decoder)};
            [(NSMutableIndexSet *)indexSet addIndexesInRange:range];
        }
        indexSet = [indexSet copy];
        
    }
    FCCacheReadObject(indexSet, decoder->_objectCache);
    return indexSet;
}

static id FCReadNSCodedObject_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    NSString *className = FCReadObject_2_3(decoder);
    NSMutableDictionary *oldProperties = decoder->_properties;
    decoder->_properties = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    while (true)
    {
        id object = FCReadObject_2_3(decoder);
        if (!object) break;
        NSString *key = FCReadObject_2_3(decoder);
        decoder->_properties[key] = object;
    }
    id object = [[NSClassFromString(className) alloc] initWithCoder:decoder];
    decoder->_properties = oldProperties;
    FCCacheReadObject(object, decoder->_objectCache);
    return object;
}

static id FCReadObject_2_3(__unsafe_unretained FCNSDecoder *decoder)
{
    static FCTypeConstructor *constructors[] =
    {
        FCReadNull_2_3,
        FCReadAlias_2_3,
        FCReadString_2_3,
        FCReadDictionary_2_3,
        FCReadArray_2_3,
        FCReadSet_2_3,
        FCReadOrderedSet_2_3,
        FCReadTrue_2_3,
        FCReadFalse_2_3,
        FCReadInt32_2_3,
        FCReadInt64_2_3,
        FCReadfloat_t_2_3,
        FCReaddouble_t_2_3,
        FCReadData_2_3,
        FCReadDate_2_3,
        FCReadMutableString_2_3,
        FCReadMutableDictionary_2_3,
        FCReadMutableArray_2_3,
        FCReadMutableSet_2_3,
        FCReadMutableOrderedSet_2_3,
        FCReadMutableData_2_3,
        FCReadClassDefinition_2_3,
        FCReadObjectInstance_2_3,
        FCReadNil_2_3,
        FCReadURL_2_3,
        FCReadPoint_2_3,
        FCReadSize_2_3,
        FCReadRect_2_3,
        FCReadRange_2_3,
        FCReadAffineTransform_2_3,
        FCRead3DTransform_2_3,
        FCReadMutableIndexSet_2_3,
        FCReadIndexSet_2_3,
        FCReadNSCodedObject_2_3
    };
  
    uint32_t type = FCReadRawUInt32_2_3(decoder);
    if (type > sizeof(constructors))
    {
        [NSException raise:FastCodingException format:@"FastCoding cannot decode object of type: %i", type];
        return nil;
    }
    return constructors[type](decoder);
}

NSObject+FastCoder.h 与  NSObject+FastCoder.m
//
//  NSObject+FastCoder.h
//  Array
//
//  Created by YouXianMing on 14/12/1.
//  Copyright (c) 2014年 YouXianMing. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (FastCoder)

/**
 *  使用FastCoder将对象写文件
 *
 *  @param path 文件路径
 *
 *  @return YES,成功,NO,失败
 */
- (BOOL)useFastCoderToWriteToFilePath:(NSString *)filePath;

/**
 *  使用FastCoder从文件路径中恢复对象
 *
 *  @param filePath 文件路径
 *
 *  @return 对象
 */
- (id)useFastCoderToRecoverFromFilePath:(NSString *)filePath;

/**
 *  使用FastCoder将对象转换成NSData
 *
 *  @return NSData
 */
- (NSData *)useFastCoderToCreateData;

@end


//
//  NSObject+FastCoder.m
//  Array
//
//  Created by YouXianMing on 14/12/1.
//  Copyright (c) 2014年 YouXianMing. All rights reserved.
//

#import "NSObject+FastCoder.h"
#import "FastCoder.h"

@implementation NSObject (FastCoder)

- (BOOL)useFastCoderToWriteToFilePath:(NSString *)filePath {
    BOOL sucess = NO;
    
    if (self) {
        NSData *data = [FastCoder dataWithRootObject:self];
        sucess       = [data writeToFile:filePath atomically:YES];
    }
    
    return sucess;
}

- (id)useFastCoderToRecoverFromFilePath:(NSString *)filePath {
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    
    return [FastCoder objectWithData:data];
}

- (NSData *)useFastCoderToCreateData {
    return [FastCoder dataWithRootObject:self];
}

@end

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
7月前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
85 1
|
设计模式 SQL Java
哪些情况下的单例对象可能会破坏?
有位小伙伴在评论区留言,希望我分享一些设计模式相关的面试题。设计模式本身是很抽象的,但是在很多面试中又经常被问到,很多小伙伴其实都能答得上,但是又不知道怎么样回答才能让面试官满意,往往越简单的知识越能够体现出核心竞争力。
73 1
|
缓存 安全 Java
双重检查锁单例
双重检查锁单例
|
安全 Java 开发者
Spring框架中的单例bean是线程安全的吗?
Spring框架中的单例bean是线程安全的吗?
133 0
|
设计模式 Java 应用服务中间件
不是单例的单例——巧用ClassLoader
本文通过如何将一个单例类实例化两次的案例,用代码实践来引入 Java 类加载器相关的概念与工作机制。理解并熟练掌握相关知识之后可以扩宽解决问题的思路,另辟蹊径,达到目的。
5791 1
|
SQL Java
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
164 0
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
|
缓存 Java
浅析类加载缓存机制
从上文我们分析了类加载器的过程与定义,我们从中得知,在JVM类加载器中有三大特性,其中缓存机制有效的保证了在同一个类加载器实例下,相同全名的类只加载一次,即loadClass方法不会被重复调用。我们这个小节就来看看它是如何利用缓存机制的已经它的流程是如何的。
181 0
浅析类加载缓存机制
|
SQL 设计模式 安全
一个单例还能写出花来吗?
单例可以说是最简单的一个设计模式了,单例模式要求只能创建一个对象实例。通常的写法是声明私有的构造函数,提供静态方法获取单例的对象实例。 常见的单例写法就是饿汉式、懒汉式、双重加锁验证、静态内部类和枚举的方式,写法可能大家都知道,不过针对不同的写法还是有可以继续深挖一下的地方,让我们从最简单的几种写法开始回顾单例,不想看前面的话直接往后翻好了。
一个单例还能写出花来吗?
C# 创建单例你会几种方式?
关于为什么需要创建单例?这里不过多介绍,具体百度知。 关于C# 创建单例步骤或条件吧 1、声明静态变量;2、私有构造函数(无法实例化)3、静态创建实例的方法;至于我这里的Singleton是sealed ,只是为了防止被继承,其实有私有构造函数足够了,这里只是为了代码可读性。
200 0
|
测试技术 iOS开发 开发者
你真的能写好一个单例么?
单例可能是 iOS 开发者最熟悉设计模式之一了。 我们的项目里头也使用了很多单例。 最近为了解决项目中单例的 bug 而花费了两天多的时间,发现用 ObjC 写好一个单例真的不容易! V1.
800 0