Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Optimize selector building #81

Merged
merged 3 commits into from

2 participants

@jspahrsummers

+stringWithFormat: is expensive when it's hit repeatedly. This builds up C strings instead, preferring stack allocations whenever possible.

@joshaber joshaber was assigned
@joshaber
Owner

:cool:

@joshaber joshaber merged commit 1f9bb9e into master

1 check passed

Details default Build #316992 succeeded in 23s
@joshaber joshaber deleted the faster-reflection branch
@jspahrsummers

@steipete This may help with some of the bottlenecks you've seen in the past.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
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);
+}
Something went wrong with that request. Please try again.