Version 0.2.0. See changelog.

This commit is contained in:
Kevin Renskers
2012-12-19 10:42:42 +00:00
parent 0915a8e9e7
commit 37ee8041ab
11 changed files with 672 additions and 6 deletions

View File

@@ -1,2 +1,6 @@
0.2.0 - December 19, 2012
- Don't automatically synchronize in the setter
- Performance logging in the example app
0.1.0 - December 18, 2012
- First public release on CocoaPods.
- First public release on CocoaPods

View File

@@ -7,13 +7,23 @@
//
#import "AppDelegate.h"
#import "ViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *defaults = @{
@"NSUSerDefault:userName": @"default",
@"NSUSerDefault:userId": @1
};
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUSerDefault:userName"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUSerDefault:userId"];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
[self.window makeKeyAndVisible];
return YES;
}

76
Example/CodeTimestamps.h Normal file
View File

@@ -0,0 +1,76 @@
//
// CodeTimestamps.h
//
// Created by Tyler Neylon on 12/29/10.
//
// Functions to assist in debugging.
//
// Sample usage, for timing within a single function/method:
//
// - (void)myMethod {
// LogTimestampStartChunk;
// /* do stuff */
// LogTimestampMidChunk;
// /* do stuff */
// LogTimestampMidChunk;
// /* do stuff */
// LogTimestampEndChunk;
// }
//
// For simpler time measurements between any two code points,
// just use LogTimestamp; for example:
//
// - (void)myMethod {
// LogTimestamp;
// /* do stuff */
// LogTimestamp;
// }
//
// All these macros measure time intervals down to the nanosecond,
// and the chunk methods provide aggregated feedback. It can be very
// useful to use the chunk methods in several methods at once, to help
// understand which is eating the most time, and why.
//
#import <Foundation/Foundation.h>
// Comment out this line to disable timestamp logging.
#define USE_TIMESTAMPS 1
// How often timing data is output to the logs.
#define kLogTimeInterval 10.0
// Macro to give us an efficient one-time function call.
// The token trickiness is from here:
// http://bit.ly/fQ6Glh
#define TokenPasteInternal(x,y) x ## y
#define TokenPaste(x,y) TokenPasteInternal(x,y)
#define UniqueTokenMacro TokenPaste(unique,__LINE__)
#define OneTimeCall(x) \
{ static BOOL UniqueTokenMacro = NO; \
if (!UniqueTokenMacro) {x; UniqueTokenMacro = YES; }}
// Speed performance-tuning functions & macros.
void LogTimeStampInMethod(const char *fnName, int lineNum);
void LogTimestampChunkInMethod(const char *fnName, int lineNum, BOOL isStart, BOOL isEnd);
void printAllLogs();
#ifdef USE_TIMESTAMPS
#define LogTimestamp LogTimeStampInMethod(__FUNCTION__, __LINE__)
#define LogTimestampStartChunk LogTimestampChunkInMethod(__FUNCTION__, __LINE__, YES, NO)
#define LogTimestampMidChunk LogTimestampChunkInMethod(__FUNCTION__, __LINE__, NO, NO)
#define LogTimestampEndChunk LogTimestampChunkInMethod(__FUNCTION__, __LINE__, NO, YES)
#else
#define LogTimestamp
#define LogTimestampStartChunk
#define LogTimestampMidChunk
#define LogTimestampEndChunk
#endif
#ifndef PrintName
#define PrintName NSLog(@"%s", __FUNCTION__)
#endif

322
Example/CodeTimestamps.m Normal file
View File

@@ -0,0 +1,322 @@
//
// CodeTimestamps.m
//
// Created by Tyler Neylon on 12/29/10.
//
//
// Reference for the timestamp functions:
// http://developer.apple.com/library/mac/#qa/qa2004/qa1398.html
//
// This code would be cleaner if it used NSObject+Be for memory
// management, but I've decided to use standard methods instead,
// to make it easier to use CodeTimestamps.{h,m} on their own.
// I'm also aware that the Build & Analyze tool produces some warnings
// for this code, but I've examined them and I don't think they're legit problems.
// I would like to tweak things to get rid of the warnings with zero performance
// cost, but that's a low priority for me, so I may not do it anytime soon.
#import "CodeTimestamps.h"
#import <mach/mach.h>
#import <mach/mach_time.h>
#define kNumSlowestChunks 5
#define kNumMidPoints 5
static NSMutableArray *chunkData = nil;
@class ChunkTimeInterval;
@interface LogHelper : NSObject {
@private
NSTimer *logTimer;
NSMutableArray *pendingLines;
NSMutableArray *slowestChunks;
}
+ (LogHelper *)sharedInstance;
- (void)startLoggingTimer;
- (void)printOutTimingData:(NSTimer *)timer;
- (void)addLogString:(NSString *)newString;
- (void)maybeAddTimeIntervalAsSlowest:(ChunkTimeInterval *)timeInterval;
- (void)logSlowestChunks;
- (void)consolidateTimeIntervals:(NSMutableArray *)timeIntervals;
@end
@interface ChunkStamp : NSObject {
@public
const char *fnName;
int lineNum;
uint64_t timestamp;
NSThread *thread;
BOOL isStart;
BOOL isEnd;
}
- (NSComparisonResult)compare:(id)other;
@end
void printAllLogs() {
[[LogHelper sharedInstance] printOutTimingData:nil];
}
uint64_t NanosecondsFromTimeInterval(uint64_t timeInterval) {
static struct mach_timebase_info timebase_info;
OneTimeCall(mach_timebase_info(&timebase_info));
timeInterval *= timebase_info.numer;
timeInterval /= timebase_info.denom;
return timeInterval;
}
// This function needs to be _fast_ to minimize interfering with
// timing data. So we don't actually NSLog during it, using LogHelper.
void LogTimeStampInMethod(const char *fnName, int lineNum) {
OneTimeCall([[LogHelper sharedInstance] startLoggingTimer]);
static uint64_t lastTimestamp = 0;
uint64_t thisTimestamp = mach_absolute_time();
NSString *logStr = nil;
if (lastTimestamp == 0) {
logStr = [NSString stringWithFormat:@"* %s:%4d", fnName, lineNum];
} else {
uint64_t elapsed = NanosecondsFromTimeInterval(thisTimestamp - lastTimestamp);
logStr = [NSString stringWithFormat:@"* %s:%4d - %9llu nsec since last timestamp",
fnName, lineNum, elapsed];
}
[[LogHelper sharedInstance] addLogString:logStr];
lastTimestamp = thisTimestamp;
}
void InitChunkData() {
if (chunkData) return;
chunkData = [NSMutableArray new];
}
void LogTimestampChunkInMethod(const char *fnName, int lineNum, BOOL isStart, BOOL isEnd) {
OneTimeCall(InitChunkData());
OneTimeCall([[LogHelper sharedInstance] startLoggingTimer]);
ChunkStamp *stamp = [[ChunkStamp new] autorelease];
stamp->fnName = fnName;
stamp->lineNum = lineNum;
stamp->timestamp = mach_absolute_time();
stamp->thread = [NSThread currentThread];
stamp->isStart = isStart;
stamp->isEnd = isEnd;
@synchronized(chunkData) {
[chunkData addObject:stamp];
}
}
@interface ChunkTimeInterval : NSObject {
@public
NSString *intervalName; // strong
uint64_t nanoSecsElapsed;
}
- (id)initFromStamp:(ChunkStamp *)stamp1 toStamp:(ChunkStamp *)stamp2;
@end
@implementation ChunkTimeInterval
- (id)initFromStamp:(ChunkStamp *)stamp1 toStamp:(ChunkStamp *)stamp2 {
if (![super init]) return nil;
intervalName = [[NSString stringWithFormat:@"%s:%d - %s:%d",
stamp1->fnName, stamp1->lineNum, stamp2->fnName, stamp2->lineNum] retain];
nanoSecsElapsed = NanosecondsFromTimeInterval(stamp2->timestamp - stamp1->timestamp);
return self;
}
- (void)dealloc {
[intervalName release];
[super dealloc];
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@ %p> %@ %llu", [self class], self, intervalName, nanoSecsElapsed];
}
@end
@implementation LogHelper
+ (LogHelper *)sharedInstance {
static LogHelper *instance = nil;
if (instance == nil) instance = [LogHelper new];
return instance;
}
- (id)init {
if (![super init]) return nil;
pendingLines = [NSMutableArray new];
slowestChunks = [NSMutableArray new];
return self;
}
- (void)startLoggingTimer {
if (logTimer) return;
logTimer = [NSTimer scheduledTimerWithTimeInterval:kLogTimeInterval
target:self
selector:@selector(printOutTimingData:)
userInfo:nil
repeats:YES];
}
- (void)printOutTimingData:(NSTimer *)timer {
BOOL didLogAnything = NO;
// Handle pending lines.
if ([pendingLines count]) {
NSLog(@"==== Start non-chunk timestamp data (from \"LogTimestamp\") ====");
for (NSString *logString in pendingLines) {
NSLog(@"%@", logString);
}
[pendingLines removeAllObjects];
didLogAnything = YES;
}
// Handle chunk data.
if ([chunkData count]) {
NSLog(@"==== Start chunk timestamp data (from \"LogTimestamp{Start,Mid,End}Chunk\") ====");
@synchronized(chunkData) {
[chunkData sortUsingSelector:@selector(compare:)];
NSThread *thread = nil;
NSMutableArray *timeIntervals = [NSMutableArray array];
uint64_t totalNanoSecsThisChunk;
uint64_t totalNanoSecsThisThread;
int numRunsThisThread;
BOOL thisThreadHadChunks = NO;
BOOL midChunk = NO;
ChunkStamp *lastStamp = nil;
NSString *chunkName = nil;
for (ChunkStamp *chunkStamp in chunkData) {
if (chunkStamp->thread != thread) {
if (thisThreadHadChunks) {
NSLog(@"++ Chunk = %@, avg time = %.4fs", chunkName,
(float)totalNanoSecsThisThread / numRunsThisThread / 1e9);
}
thread = chunkStamp->thread;
NSLog(@"--- Data for thread %p ---", thread);
[timeIntervals removeAllObjects];
midChunk = NO;
thisThreadHadChunks = NO;
totalNanoSecsThisChunk = 0;
totalNanoSecsThisThread = 0;
numRunsThisThread = 0;
}
if (chunkStamp->isStart) {
if (midChunk) {
NSLog(@"ERROR: LogTimestampStartChunk hit twice without a LogTimestampEndChunk between them.");
}
midChunk = YES;
thisThreadHadChunks = YES;
chunkName = [NSString stringWithFormat:@"%s:%d", chunkStamp->fnName, chunkStamp->lineNum];
} else if (midChunk) {
ChunkTimeInterval *timeInterval = [[[ChunkTimeInterval alloc] initFromStamp:lastStamp toStamp:chunkStamp] autorelease];
[timeIntervals addObject:timeInterval];
totalNanoSecsThisChunk += timeInterval->nanoSecsElapsed;
if (chunkStamp->isEnd) {
totalNanoSecsThisThread += totalNanoSecsThisChunk;
numRunsThisThread++;
chunkName = [NSString stringWithFormat:@"%@ - %s:%d", chunkName, chunkStamp->fnName, chunkStamp->lineNum];
NSLog(@"+ Chunk = %@, time = %.4fs", chunkName, (float)totalNanoSecsThisChunk/1e9);
[self consolidateTimeIntervals:timeIntervals];
for (int i = 0; i < [timeIntervals count] && i < kNumMidPoints; ++i) {
ChunkTimeInterval *timeInterval = [timeIntervals objectAtIndex:i];
int percentTime = (int)round(100.0 * (float)timeInterval->nanoSecsElapsed / totalNanoSecsThisChunk);
NSLog(@" %2d%% in %@", percentTime, timeInterval->intervalName);
}
ChunkTimeInterval *totalInterval = [[ChunkTimeInterval new] autorelease];
totalInterval->intervalName = [chunkName retain];
totalInterval->nanoSecsElapsed = totalNanoSecsThisChunk;
[self maybeAddTimeIntervalAsSlowest:totalInterval];
[timeIntervals removeAllObjects];
totalNanoSecsThisChunk = 0;
midChunk = NO;
}
}
lastStamp = chunkStamp;
}
if (thisThreadHadChunks) {
NSLog(@"++ Chunk = %@, avg time = %lld nsec", chunkName,
totalNanoSecsThisThread / numRunsThisThread);
}
[chunkData removeAllObjects];
}
didLogAnything = YES;
}
if (didLogAnything) {
[self logSlowestChunks];
NSLog(@"==== End timestamp data ====");
}
}
- (void)addLogString:(NSString *)newString {
[pendingLines addObject:newString];
}
- (void)maybeAddTimeIntervalAsSlowest:(ChunkTimeInterval *)timeInterval {
if ([slowestChunks count] < kNumSlowestChunks ||
((ChunkTimeInterval *)[slowestChunks lastObject])->nanoSecsElapsed < timeInterval->nanoSecsElapsed) {
[slowestChunks addObject:timeInterval];
NSSortDescriptor *sortByTime = [[[NSSortDescriptor alloc] initWithKey:@"nanoSecsElapsed" ascending:NO] autorelease];
[slowestChunks sortUsingDescriptors:[NSArray arrayWithObject:sortByTime]];
if ([slowestChunks count] > kNumSlowestChunks) [slowestChunks removeLastObject];
}
}
- (void)logSlowestChunks {
if ([slowestChunks count] == 0) return;
NSLog(@"==== Slowest chunks so far ====");
for (ChunkTimeInterval *timeInterval in slowestChunks) {
NSLog(@"# Chunk = %@, time = %.4fs", timeInterval->intervalName, (float)timeInterval->nanoSecsElapsed/1e9);
}
}
- (void)consolidateTimeIntervals:(NSMutableArray *)timeIntervals {
NSSortDescriptor *sortByName = [[[NSSortDescriptor alloc] initWithKey:@"intervalName" ascending:YES] autorelease];
[timeIntervals sortUsingDescriptors:[NSArray arrayWithObject:sortByName]];
NSMutableArray *consolidatedIntervals = [NSMutableArray array];
NSString *lastName = nil;
ChunkTimeInterval *thisInterval = nil;
for (ChunkTimeInterval *timeInterval in timeIntervals) {
if ([lastName isEqualToString:timeInterval->intervalName]) {
thisInterval->nanoSecsElapsed += timeInterval->nanoSecsElapsed;
} else {
thisInterval = [[ChunkTimeInterval new] autorelease];
thisInterval->intervalName = [timeInterval->intervalName retain];
thisInterval->nanoSecsElapsed = timeInterval->nanoSecsElapsed;
[consolidatedIntervals addObject:thisInterval];
}
lastName = timeInterval->intervalName;
}
[timeIntervals removeAllObjects];
[timeIntervals addObjectsFromArray:consolidatedIntervals];
NSSortDescriptor *sortByTime = [[[NSSortDescriptor alloc] initWithKey:@"nanoSecsElapsed" ascending:NO] autorelease];
[timeIntervals sortUsingDescriptors:[NSArray arrayWithObject:sortByTime]];
}
@end
@implementation ChunkStamp
- (NSComparisonResult)compare:(id)other {
ChunkStamp *otherStamp = (ChunkStamp *)other;
if (thread != otherStamp->thread) {
return (thread < otherStamp->thread ? NSOrderedAscending : NSOrderedDescending);
}
if (strcmp(fnName, otherStamp->fnName) != 0) {
return (strcmp(fnName, otherStamp->fnName) < 0 ? NSOrderedAscending : NSOrderedDescending);
}
if (timestamp == otherStamp->timestamp) return NSOrderedSame;
return (timestamp < otherStamp->timestamp ? NSOrderedAscending : NSOrderedDescending);
}
@end

View File

@@ -27,6 +27,9 @@
0AD6F3DD168123C900E13802 /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 0AD6F3DA168123C800E13802 /* CHANGELOG */; };
0AD6F3DE168123C900E13802 /* GVUserDefaults.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 0AD6F3DB168123C800E13802 /* GVUserDefaults.podspec */; };
0AD6F3DF168123C900E13802 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0AD6F3DC168123C800E13802 /* README.md */; };
0AD6F3F21681BEDF00E13802 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AD6F3F01681BEDF00E13802 /* ViewController.m */; };
0AD6F3F31681BEDF00E13802 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0AD6F3F11681BEDF00E13802 /* ViewController.xib */; };
0AD6F3F71681C17300E13802 /* CodeTimestamps.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AD6F3F61681C17300E13802 /* CodeTimestamps.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -68,6 +71,11 @@
0AD6F3DA168123C800E13802 /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CHANGELOG; path = ../CHANGELOG; sourceTree = "<group>"; };
0AD6F3DB168123C800E13802 /* GVUserDefaults.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = GVUserDefaults.podspec; path = ../GVUserDefaults.podspec; sourceTree = "<group>"; };
0AD6F3DC168123C800E13802 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = "<group>"; };
0AD6F3EF1681BEDF00E13802 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
0AD6F3F01681BEDF00E13802 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
0AD6F3F11681BEDF00E13802 /* ViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ViewController.xib; sourceTree = "<group>"; };
0AD6F3F51681C17300E13802 /* CodeTimestamps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeTimestamps.h; sourceTree = "<group>"; };
0AD6F3F61681C17300E13802 /* CodeTimestamps.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodeTimestamps.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -131,6 +139,8 @@
0AD6F3651680DCD400E13802 /* Example */ = {
isa = PBXGroup;
children = (
0AD6F3F51681C17300E13802 /* CodeTimestamps.h */,
0AD6F3F61681C17300E13802 /* CodeTimestamps.m */,
0AD6F3C21680DF3800E13802 /* GVUserDefaults+Properties.h */,
0AD6F3C31680DF3800E13802 /* GVUserDefaults+Properties.m */,
0AD6F3A41680DDD000E13802 /* AppDelegate.h */,
@@ -141,6 +151,9 @@
0AD6F3A91680DDD000E13802 /* Example-Info.plist */,
0AD6F3AA1680DDD000E13802 /* Example-Prefix.pch */,
0AD6F3AE1680DDD000E13802 /* main.m */,
0AD6F3EF1681BEDF00E13802 /* ViewController.h */,
0AD6F3F01681BEDF00E13802 /* ViewController.m */,
0AD6F3F11681BEDF00E13802 /* ViewController.xib */,
0AD6F3A11680DDD000E13802 /* en.lproj */,
);
name = Example;
@@ -263,6 +276,7 @@
0AD6F3DD168123C900E13802 /* CHANGELOG in Resources */,
0AD6F3DE168123C900E13802 /* GVUserDefaults.podspec in Resources */,
0AD6F3DF168123C900E13802 /* README.md in Resources */,
0AD6F3F31681BEDF00E13802 /* ViewController.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -301,6 +315,8 @@
0AD6F3B51680DDD000E13802 /* main.m in Sources */,
0AD6F3B91680DE0600E13802 /* GVUserDefaults.m in Sources */,
0AD6F3C41680DF3900E13802 /* GVUserDefaults+Properties.m in Sources */,
0AD6F3F21681BEDF00E13802 /* ViewController.m in Sources */,
0AD6F3F71681C17300E13802 /* CodeTimestamps.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -365,7 +381,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
@@ -386,7 +402,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
@@ -399,6 +415,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Example-Prefix.pch";
INFOPLIST_FILE = "Example-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -410,6 +427,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Example-Prefix.pch";
INFOPLIST_FILE = "Example-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};

13
Example/ViewController.h Normal file
View File

@@ -0,0 +1,13 @@
//
// ViewController.h
// Example
//
// Created by Kevin Renskers on 19-12-12.
// Copyright (c) 2012 Gangverk. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

39
Example/ViewController.m Normal file
View File

@@ -0,0 +1,39 @@
//
// ViewController.m
// Example
//
// Created by Kevin Renskers on 19-12-12.
// Copyright (c) 2012 Gangverk. All rights reserved.
//
#import "ViewController.h"
#import "GVUserDefaults+Properties.h"
#import "CodeTimestamps.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test;
LogTimestamp;
test = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUSerDefault:userName"];
LogTimestamp;
test = [GVUserDefaults standardUserDefaults].userName;
LogTimestamp;
[[NSUserDefaults standardUserDefaults] setObject:@"Hello!" forKey:@"NSUSerDefault:userName"];
LogTimestamp;
[GVUserDefaults standardUserDefaults].userName = @"Hello!";
LogTimestamp;
}
@end

180
Example/ViewController.xib Normal file
View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1536</int>
<string key="IBDocument.SystemVersion">12C60</string>
<string key="IBDocument.InterfaceBuilderVersion">2844</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1930</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUILabel</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUILabel" id="430579578">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">293</int>
<string key="NSFrame">{{141, 20}, {39, 21}}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Hello</string>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>
<string key="IBUIColorCocoaTouchKeyPath">darkTextColor</string>
</object>
<nil key="IBUIHighlightedColor"/>
<int key="IBUIBaselineAdjustment">0</int>
<object class="IBUIFontDescription" key="IBUIFontDescription">
<int key="type">1</int>
<double key="pointSize">17</double>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">17</double>
<int key="NSfFlags">16</int>
</object>
<bool key="IBUIAdjustsFontSizeToFit">NO</bool>
</object>
</array>
<string key="NSFrame">{{0, 20}, {320, 548}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="430579578"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<object class="IBUIScreenMetrics" key="IBUISimulatedDestinationMetrics">
<string key="IBUISimulatedSizeMetricsClass">IBUIScreenMetrics</string>
<object class="NSMutableDictionary" key="IBUINormalizedOrientationToSizeMap">
<bool key="EncodedWithXMLCoder">YES</bool>
<array key="dict.sortedKeys">
<integer value="1"/>
<integer value="3"/>
</array>
<array key="dict.values">
<string>{320, 568}</string>
<string>{568, 320}</string>
</array>
</object>
<string key="IBUITargetRuntime">IBCocoaTouchFramework</string>
<string key="IBUIDisplayName">Retina 4 Full Screen</string>
<int key="IBUIType">2</int>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="191373211"/>
</object>
<int key="connectionID">3</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<array class="NSMutableArray" key="children">
<reference ref="430579578"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="430579578"/>
<reference key="parent" ref="191373211"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">ViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">6</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">ViewController</string>
<string key="superclassName">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/ViewController.h</string>
</object>
</object>
</array>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1930</string>
</data>
</archive>

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "GVUserDefaults"
s.version = "0.1.0"
s.version = "0.2.0"
s.summary = "NSUserDefaults access via properties."
s.homepage = "https://github.com/gangverk/GVUserDefaults"
s.license = 'MIT'

View File

@@ -56,7 +56,6 @@ void accessorSetter(id self, SEL _cmd, id newValue) {
// Set value of the key anID to newValue
[[NSUserDefaults standardUserDefaults] setObject:newValue forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
@end

View File

@@ -41,6 +41,11 @@ Registering defaults is done as usual, on NSUserDefaults directly (use the same
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
### Performance
The getter is about 3 times as slow as directly using NSUSerDefaults, but we're talking about fractions of a millisecond (0.22 ms vs 0.65 ms on an iPod Touch 4th gen). The setter is about 3 times as slow as well (0.20 ms vs 0.61 ms).
The numbers vary a bit from device to device and from run to run, but it always seems to be about 1.5 to 3 times as slow. For example on an iPhone 4 the setter takes 0.77 ms vs 0.5 ms when you do it natively.
## Install
Install via [CocoaPods](http://cocoapods.org) (`pod 'GVUserDefaults'`) or drag the code in the GVUserDefaults subfolder to your project.