Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #81 from github/faster-reflection

Optimize selector building
  • Loading branch information...
commit 1f9bb9e28f95d25331141c8a0a4aeec7d5f00783 2 parents 66e186e + 341c2f2
@joshaber joshaber authored
View
14 Mantle.xcodeproj/project.pbxproj
@@ -40,6 +40,8 @@
D042FC8815F72BC7004E8054 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D042FC5515F72B23004E8054 /* SenTestingKit.framework */; };
D042FC8B15F72BC7004E8054 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D042FC4415F72B23004E8054 /* Foundation.framework */; };
D042FC8E15F72BC7004E8054 /* libMantle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D042FC7915F72BC7004E8054 /* libMantle.a */; };
+ D058FE2116EFB3D2009DFB47 /* MTLReflection.m in Sources */ = {isa = PBXBuildFile; fileRef = D058FE1E16EFB3D2009DFB47 /* MTLReflection.m */; };
+ D058FE2216EFB3D2009DFB47 /* MTLReflection.m in Sources */ = {isa = PBXBuildFile; fileRef = D058FE1E16EFB3D2009DFB47 /* MTLReflection.m */; };
D064BA341613BA75004CA27A /* MTLTestNotificationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = D064BA331613BA75004CA27A /* MTLTestNotificationObserver.m */; };
D064BA351613BA75004CA27A /* MTLTestNotificationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = D064BA331613BA75004CA27A /* MTLTestNotificationObserver.m */; };
D0760E0515FFBD440060F550 /* Mantle.h in Headers */ = {isa = PBXBuildFile; fileRef = D042FC4C15F72B23004E8054 /* Mantle.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -226,6 +228,8 @@
D042FCB215F72C16004E8054 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
D042FCCB15F72EE8004E8054 /* Expecta.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Expecta.xcodeproj; path = MantleTests/expecta/Expecta.xcodeproj; sourceTree = "<group>"; };
D042FCEE15F72EF1004E8054 /* Specta.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Specta.xcodeproj; path = MantleTests/specta/Specta.xcodeproj; sourceTree = "<group>"; };
+ D058FE1D16EFB3D2009DFB47 /* MTLReflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLReflection.h; sourceTree = "<group>"; };
+ D058FE1E16EFB3D2009DFB47 /* MTLReflection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLReflection.m; sourceTree = "<group>"; };
D064BA321613BA75004CA27A /* MTLTestNotificationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLTestNotificationObserver.h; sourceTree = "<group>"; };
D064BA331613BA75004CA27A /* MTLTestNotificationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLTestNotificationObserver.m; sourceTree = "<group>"; };
D0760E7615FFBF330060F550 /* MTLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLModel.h; sourceTree = "<group>"; };
@@ -350,7 +354,7 @@
isa = PBXGroup;
children = (
D042FC4C15F72B23004E8054 /* Mantle.h */,
- D0760E7015FFBF020060F550 /* Classes */,
+ D0760E7015FFBF020060F550 /* Modules */,
D090E62515F730B2005282F9 /* Extensions */,
D042FC4615F72B23004E8054 /* Supporting Files */,
);
@@ -454,17 +458,19 @@
path = "Mac OS X";
sourceTree = "<group>";
};
- D0760E7015FFBF020060F550 /* Classes */ = {
+ D0760E7015FFBF020060F550 /* Modules */ = {
isa = PBXGroup;
children = (
D0760E7615FFBF330060F550 /* MTLModel.h */,
D0760E7715FFBF330060F550 /* MTLModel.m */,
D01BD0AD16CB52E800EC95C7 /* MTLModel+NSCoding.h */,
D01BD0AE16CB52E800EC95C7 /* MTLModel+NSCoding.m */,
+ D058FE1D16EFB3D2009DFB47 /* MTLReflection.h */,
+ D058FE1E16EFB3D2009DFB47 /* MTLReflection.m */,
D01BD0AB16CB46B600EC95C7 /* Adapters */,
D01BD0AC16CB46BD00EC95C7 /* Value Transformers */,
);
- name = Classes;
+ name = Modules;
sourceTree = "<group>";
};
D0760EC615FFCA3E0060F550 /* Specs */ = {
@@ -817,6 +823,7 @@
1ED5B5D1163A4E3C0072668E /* NSObject+MTLComparisonAdditions.m in Sources */,
D01BD09F16CB432D00EC95C7 /* MTLJSONAdapter.m in Sources */,
D01BD0B116CB52E800EC95C7 /* MTLModel+NSCoding.m in Sources */,
+ D058FE2116EFB3D2009DFB47 /* MTLReflection.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -851,6 +858,7 @@
1ED5B5D2163A4E3C0072668E /* NSObject+MTLComparisonAdditions.m in Sources */,
D01BD0A016CB432D00EC95C7 /* MTLJSONAdapter.m in Sources */,
D01BD0B216CB52E800EC95C7 /* MTLModel+NSCoding.m in Sources */,
+ D058FE2216EFB3D2009DFB47 /* MTLReflection.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
3  Mantle/MTLJSONAdapter.m
@@ -8,6 +8,7 @@
#import "MTLJSONAdapter.h"
#import "MTLModel.h"
+#import "MTLReflection.h"
NSString * const MTLJSONAdapterErrorDomain = @"MTLJSONAdapterErrorDomain";
NSInteger const MTLJSONAdapterErrorNoClassFound = 2;
@@ -198,7 +199,7 @@ - (NSDictionary *)JSONDictionary {
- (NSValueTransformer *)JSONTransformerForKey:(NSString *)key {
NSParameterAssert(key != nil);
- SEL selector = NSSelectorFromString([key stringByAppendingString:@"JSONTransformer"]);
+ SEL selector = MTLSelectorWithKeyPattern(key, "JSONTransformer");
if ([self.modelClass respondsToSelector:selector]) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self.modelClass methodSignatureForSelector:selector]];
invocation.target = self.modelClass;
View
5 Mantle/MTLModel+NSCoding.m
@@ -9,6 +9,7 @@
#import "MTLModel+NSCoding.h"
#import "EXTRuntimeExtensions.h"
#import "EXTScope.h"
+#import "MTLReflection.h"
#import <objc/runtime.h>
// Used in archives to store the modelVersion of the archived instance.
@@ -126,9 +127,7 @@ - (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:
NSParameterAssert(key != nil);
NSParameterAssert(coder != nil);
- NSString *methodName = [NSString stringWithFormat:@"decode%@%@WithCoder:modelVersion:", [key substringToIndex:1].uppercaseString, [key substringFromIndex:1]];
- SEL selector = NSSelectorFromString(methodName);
-
+ SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:");
if ([self respondsToSelector:selector]) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
invocation.target = self;
View
4 Mantle/MTLModel.m
@@ -9,6 +9,7 @@
#import "MTLModel.h"
#import "EXTRuntimeExtensions.h"
#import "EXTScope.h"
+#import "MTLReflection.h"
#import <objc/runtime.h>
// This coupling is needed for backwards compatibility in MTLModel's deprecated
@@ -151,8 +152,7 @@ - (NSDictionary *)dictionaryValue {
- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model {
NSParameterAssert(key != nil);
- NSString *methodName = [NSString stringWithFormat:@"merge%@%@FromModel:", [key substringToIndex:1].uppercaseString, [key substringFromIndex:1]];
- SEL selector = NSSelectorFromString(methodName);
+ SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:");
if (![self respondsToSelector:selector]) {
if (model != nil) {
[self setValue:[model valueForKey:key] forKey:key];
View
31 Mantle/MTLReflection.h
@@ -0,0 +1,31 @@
+//
+// MTLReflection.h
+// Mantle
+//
+// Created by Justin Spahr-Summers on 2013-03-12.
+// Copyright (c) 2013 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// Creates a selector from a key and a constant string.
+//
+// key - The key to insert into the generated selector. This key should be in
+// its natural case.
+// suffix - A string to append to the key as part of the selector.
+//
+// Returns a selector, or NULL if the input strings cannot form a valid
+// selector.
+SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2)));
+
+// Creates a selector from a key and a constant prefix and suffix.
+//
+// prefix - A string to prepend to the key as part of the selector.
+// key - The key to insert into the generated selector. This key should be in
+// its natural case, and will have its first letter capitalized when
+// inserted.
+// suffix - A string to append to the key as part of the selector.
+//
+// Returns a selector, or NULL if the input strings cannot form a valid
+// selector.
+SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3)));
View
50 Mantle/MTLReflection.m
@@ -0,0 +1,50 @@
+//
+// MTLReflection.m
+// Mantle
+//
+// Created by Justin Spahr-Summers on 2013-03-12.
+// Copyright (c) 2013 GitHub. All rights reserved.
+//
+
+#import "MTLReflection.h"
+#import <objc/runtime.h>
+
+SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) {
+ NSUInteger keyLength = [key maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ NSUInteger suffixLength = strlen(suffix);
+
+ char selector[keyLength + suffixLength + 1];
+
+ BOOL success = [key getBytes:selector maxLength:keyLength usedLength:&keyLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, key.length) remainingRange:NULL];
+ if (!success) return NULL;
+
+ memcpy(selector + keyLength, suffix, suffixLength);
+ selector[keyLength + suffixLength] = '\0';
+
+ return sel_registerName(selector);
+}
+
+SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) {
+ NSUInteger prefixLength = strlen(prefix);
+ NSUInteger suffixLength = strlen(suffix);
+
+ NSString *initial = [key substringToIndex:1].uppercaseString;
+ NSUInteger initialLength = [initial maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+ NSString *rest = [key substringFromIndex:1];
+ NSUInteger restLength = [rest maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+ char selector[prefixLength + initialLength + restLength + suffixLength + 1];
+ memcpy(selector, prefix, prefixLength);
+
+ BOOL success = [initial getBytes:selector + prefixLength maxLength:initialLength usedLength:&initialLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, initial.length) remainingRange:NULL];
+ if (!success) return NULL;
+
+ success = [rest getBytes:selector + prefixLength + initialLength maxLength:restLength usedLength:&restLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, rest.length) remainingRange:NULL];
+ if (!success) return NULL;
+
+ memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength);
+ selector[prefixLength + initialLength + restLength + suffixLength] = '\0';
+
+ return sel_registerName(selector);
+}
Please sign in to comment.
Something went wrong with that request. Please try again.