使用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
时间: 2024-10-12 12:47:32

使用FastCoder写缓存单例的相关文章

如何根据用例图写出用例描述

前言:因为用例描述中的执行者和用例名很容易通过用例图得出来,所以下面讲的主要内容是如何通过用例图获得用例描述中的交互动作序列. 第一步 用例分类 A.用例分类是什么??? 用例分类是指把有关系(包含或扩展)的用例放到一起,与其他用例无关系的用例单独成排. B.为什么要进行用例分类这步操作???(Benefits) 1对于有关系的用例而言.它们的用例描述修改更方便. 2.能够有效提高书写用例描述的速度与质量. C.如何进行用例分类??? 分类标准:有无包含或扩展关系 例如:在教务管理系统中有学生和

写一个单例(Singleton),并说明单例的目的和好处

单例的目的:保证一个类只有单一的实例,也就是说你无法通过new来创建这个类的一个新实例. 单例的好处:当一个对象在程序内部只能有一个实例的时候,它可以保证我们不会重复创建,而是始终指向同一个对象. Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问. 第一种:在声明变量时实例化(也叫饿汉式单例模式),代码如下: public class Singleton { private s

关于java写一个单例类(面试手写题)

package com.shundong.javacore; /** * java写一个简单的单例类 * @author shundong * */ class Singleton { //使用一个类变量来缓存曾经创建的实例 private static Singleton instance; //对构造进行隐藏(private) private Singleton(){} /** * 提供一个静态方法 * 该方法加入了自定义控制 保证只产生一个Singleton对象 * @return 返回S

一个缓存容灾写的样例

背景 有时我们能够使用缓存进行容灾的处理.场景例如以下:我们当前有一个专门提供各种数据的应用DataCore,该应用开放多个RFC方法供其它应用使用.      我们平时在读写数据时,会在Cache备份一份(为平时DataCore提高响应速度.减少DB.CPU压力所用),当DB挂掉的时候.Cache还能够用来容灾.使用缓存容灾的优点是:性能足够好,坏处是缓存可比数据库成本高多了. 让我们想象得更猛烈些,当DataCore整个挂掉的时候,A.B.C.D方怎么才干安然的执行下去? 我们能够在A.B.

使用python写appium用例

安装Python依赖 pip3.4 install nose pip3.4 install selenium pip3.4 install Appium-Python-Client 执行測试用例android_contacts.py import os import unittest from appium import webdriver from time import sleep # Returns abs path relative to this file and not cwd PA

更换主板后,网卡用不了。------linux清除网卡缓存一例

案例描述:当实际生产环境下,服务器主板挂了,厂商换了一块同型号主板,网卡是板载的,所以换主板等于把网卡也换了.接着问题就来了,网卡全都乱了.网卡起不来.现在不知什么原因. 分析过程:既然是主板换了,换的也是同一个型号的主板,驱动问题可以排除.主板一共有4块网卡.按顺序依次是eth0,eth1,eth2,eth3.换了主板,用ifconfig –a 查看竟然多出了好几个网卡多出了eth4,eth5,eth6,eth7.但是在配置目录(/etc/sysconfig/network-scripts/)

AOP执行增强-Spring 源码系列(5)

AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcessor-Spring 源码(3) 事件机制-Spring 源码(4) AOP执行增强-Spring 源码系列(5) AOP的核心就是个动态代理,Spring进行了大量抽象和封装形成一个方便上层使用的基础模块. 而动态代理的两种实现都在上一篇中提供了代码 直接ProxyFactoryBean入手来

第二章 IOC + AOP 底层原理

<精通Spring4.x 企业应用开发实战>读书笔记 一.概念 IOC: 假设B类调用了A类,那么A类的对象的创建是由B类来实现: IOC是指将A对象的创建由容器来完成,并且将创建好的对象注入到B类中供B类对象使用 好处: 减少对象的创建工作 解耦B类与A类对象的创建过程 二.Resource接口 "classpath:":只会在第一个加载的com.xxx包的类路径下查找: "classpath*:":会扫描所有类路径下的com.xxx包中的查找 三.B

你研究过单例么?这样写单例效率最高.

首先,小汤我在这里,要表示一下歉意,本来是想要每天写一篇Swift的学习小tip的,无奈近期手头的money花差的差点儿相同了,仅仅能迫不得已,出门找工作去了,没能履行承诺之处还请大家见谅. 那么,废话不多说了,開始我们今天的主题: 单例 ! 单例介绍: 说到单例,大家应该都不陌生,在传说中的那23种 (为啥我就会6种捏o(╯□╰)o-) 设计模式中,单例应该是属于和简单工厂模式并列的最简单的设计模式了,也应该是最经常使用的. 像这样简单易懂,又能有效提高程序执行效率的设计模式,作为一个iOS程