├── .gitignore
├── README.md
├── cocoa-string-size-performance.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── Abhi.xcuserdatad
│ └── xcschemes
│ ├── cocoa-string-size-performance.xcscheme
│ └── xcschememanagement.plist
└── cocoa-string-size-performance
├── cocoa-string-size-performance-Prefix.pch
├── cocoa_string_size_performance.1
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 |
28 | *.xcuserstate
29 |
30 | cocoa-string-size-performance.xcodeproj/project.xcworkspace/xcuserdata/Abhi.xcuserdatad/UserInterfaceState.xcuserstate
31 |
32 | *.xcuserstate
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cocoa-string-size-performance
2 | This is a console application in Cocoa to show the various ways to measure the width of text and its performance. There are 3 ways(that I figured out) to do this:
3 |
4 |
5 |
[[NSString sizeWithAttributes:]](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSString_AppKitAdditions/index.html#//apple_ref/occ/instm/NSString/sizeWithAttributes:)
6 | [[NSAttributedString size]](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/NSAttributedString_UIKit_Additions/index.html#//apple_ref/occ/instm/NSAttributedString/size)
7 | [NSLayoutManager](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html#//apple_ref/doc/uid/20001809-CJBGBIBB) (get text width instead of height)
8 |
9 |
10 | To play with the project, download the file, open up main.m.
11 |
12 | - Change ITEMS_COUNT to the number of strings you want measured.
13 |
- Change ITEMS_ARRAY_CHUNK_THRESHOLD to chunk up a big array into small arrays.
14 |
15 | Here are some performance metrics on my Macbook Pro running Yosemite 2.3GHz Intel i7 and 8GB memory.
16 |
Count\Mechanism sizeWithAttributes NSAttributedString NSLayoutManager
17 | 1000 0.057 0.031 0.007
18 | 10000 0.329 0.325 0.064
19 | 100000 3.06 3.14 0.689
20 | 1000000 29.5 31.3 7.06
21 |
22 | NOTE: The NSLayoutManager mechanism creates an NSTextStorage object for every string. These objects are very heavyweight and memory intensive. By just creating 1 and re-using them however causes layout to happen for every string and the measurement time goes up to 40 seconds for a million strings.
23 |
24 | UPDATE
25 | Using CoreText is the way to go. For the above table here is the performance metric with and without multithreading.
26 | Count\Mechanism Core Text NO Multithreading Core Text Multithreading
27 | 1000 0.018 0.02
28 | 10000 0.023 0.023
29 | 100000 0.161 0.061
30 | 1000000 1.46 0.447
31 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 384E69E11B18BA9900B74FED /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384E69E01B18BA9900B74FED /* Foundation.framework */; };
11 | 384E69E41B18BA9900B74FED /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 384E69E31B18BA9900B74FED /* main.m */; };
12 | 384E69E81B18BA9900B74FED /* cocoa_string_size_performance.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 384E69E71B18BA9900B74FED /* cocoa_string_size_performance.1 */; };
13 | 384E69EF1B18BCD000B74FED /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384E69EE1B18BCD000B74FED /* AppKit.framework */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXCopyFilesBuildPhase section */
17 | 384E69DB1B18BA9900B74FED /* CopyFiles */ = {
18 | isa = PBXCopyFilesBuildPhase;
19 | buildActionMask = 2147483647;
20 | dstPath = /usr/share/man/man1/;
21 | dstSubfolderSpec = 0;
22 | files = (
23 | 384E69E81B18BA9900B74FED /* cocoa_string_size_performance.1 in CopyFiles */,
24 | );
25 | runOnlyForDeploymentPostprocessing = 1;
26 | };
27 | /* End PBXCopyFilesBuildPhase section */
28 |
29 | /* Begin PBXFileReference section */
30 | 384E69DD1B18BA9900B74FED /* cocoa-string-size-performance */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "cocoa-string-size-performance"; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 384E69E01B18BA9900B74FED /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
32 | 384E69E31B18BA9900B74FED /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
33 | 384E69E61B18BA9900B74FED /* cocoa-string-size-performance-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "cocoa-string-size-performance-Prefix.pch"; sourceTree = ""; };
34 | 384E69E71B18BA9900B74FED /* cocoa_string_size_performance.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cocoa_string_size_performance.1; sourceTree = ""; };
35 | 384E69EE1B18BCD000B74FED /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | 384E69DA1B18BA9900B74FED /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | 384E69EF1B18BCD000B74FED /* AppKit.framework in Frameworks */,
44 | 384E69E11B18BA9900B74FED /* Foundation.framework in Frameworks */,
45 | );
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXFrameworksBuildPhase section */
49 |
50 | /* Begin PBXGroup section */
51 | 384E69D41B18BA9900B74FED = {
52 | isa = PBXGroup;
53 | children = (
54 | 384E69E21B18BA9900B74FED /* cocoa-string-size-performance */,
55 | 384E69DF1B18BA9900B74FED /* Frameworks */,
56 | 384E69DE1B18BA9900B74FED /* Products */,
57 | );
58 | sourceTree = "";
59 | };
60 | 384E69DE1B18BA9900B74FED /* Products */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 384E69DD1B18BA9900B74FED /* cocoa-string-size-performance */,
64 | );
65 | name = Products;
66 | sourceTree = "";
67 | };
68 | 384E69DF1B18BA9900B74FED /* Frameworks */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 384E69EE1B18BCD000B74FED /* AppKit.framework */,
72 | 384E69E01B18BA9900B74FED /* Foundation.framework */,
73 | );
74 | name = Frameworks;
75 | sourceTree = "";
76 | };
77 | 384E69E21B18BA9900B74FED /* cocoa-string-size-performance */ = {
78 | isa = PBXGroup;
79 | children = (
80 | 384E69E31B18BA9900B74FED /* main.m */,
81 | 384E69E71B18BA9900B74FED /* cocoa_string_size_performance.1 */,
82 | 384E69E51B18BA9900B74FED /* Supporting Files */,
83 | );
84 | path = "cocoa-string-size-performance";
85 | sourceTree = "";
86 | };
87 | 384E69E51B18BA9900B74FED /* Supporting Files */ = {
88 | isa = PBXGroup;
89 | children = (
90 | 384E69E61B18BA9900B74FED /* cocoa-string-size-performance-Prefix.pch */,
91 | );
92 | name = "Supporting Files";
93 | sourceTree = "";
94 | };
95 | /* End PBXGroup section */
96 |
97 | /* Begin PBXNativeTarget section */
98 | 384E69DC1B18BA9900B74FED /* cocoa-string-size-performance */ = {
99 | isa = PBXNativeTarget;
100 | buildConfigurationList = 384E69EB1B18BA9900B74FED /* Build configuration list for PBXNativeTarget "cocoa-string-size-performance" */;
101 | buildPhases = (
102 | 384E69D91B18BA9900B74FED /* Sources */,
103 | 384E69DA1B18BA9900B74FED /* Frameworks */,
104 | 384E69DB1B18BA9900B74FED /* CopyFiles */,
105 | );
106 | buildRules = (
107 | );
108 | dependencies = (
109 | );
110 | name = "cocoa-string-size-performance";
111 | productName = "cocoa-string-size-performance";
112 | productReference = 384E69DD1B18BA9900B74FED /* cocoa-string-size-performance */;
113 | productType = "com.apple.product-type.tool";
114 | };
115 | /* End PBXNativeTarget section */
116 |
117 | /* Begin PBXProject section */
118 | 384E69D51B18BA9900B74FED /* Project object */ = {
119 | isa = PBXProject;
120 | attributes = {
121 | LastUpgradeCheck = 0510;
122 | ORGANIZATIONNAME = "___Abhishek Moothedath___";
123 | };
124 | buildConfigurationList = 384E69D81B18BA9900B74FED /* Build configuration list for PBXProject "cocoa-string-size-performance" */;
125 | compatibilityVersion = "Xcode 3.2";
126 | developmentRegion = English;
127 | hasScannedForEncodings = 0;
128 | knownRegions = (
129 | en,
130 | );
131 | mainGroup = 384E69D41B18BA9900B74FED;
132 | productRefGroup = 384E69DE1B18BA9900B74FED /* Products */;
133 | projectDirPath = "";
134 | projectRoot = "";
135 | targets = (
136 | 384E69DC1B18BA9900B74FED /* cocoa-string-size-performance */,
137 | );
138 | };
139 | /* End PBXProject section */
140 |
141 | /* Begin PBXSourcesBuildPhase section */
142 | 384E69D91B18BA9900B74FED /* Sources */ = {
143 | isa = PBXSourcesBuildPhase;
144 | buildActionMask = 2147483647;
145 | files = (
146 | 384E69E41B18BA9900B74FED /* main.m in Sources */,
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | /* End PBXSourcesBuildPhase section */
151 |
152 | /* Begin XCBuildConfiguration section */
153 | 384E69E91B18BA9900B74FED /* Debug */ = {
154 | isa = XCBuildConfiguration;
155 | buildSettings = {
156 | ALWAYS_SEARCH_USER_PATHS = NO;
157 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
158 | CLANG_CXX_LIBRARY = "libc++";
159 | CLANG_ENABLE_MODULES = YES;
160 | CLANG_ENABLE_OBJC_ARC = YES;
161 | CLANG_WARN_BOOL_CONVERSION = YES;
162 | CLANG_WARN_CONSTANT_CONVERSION = YES;
163 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
164 | CLANG_WARN_EMPTY_BODY = YES;
165 | CLANG_WARN_ENUM_CONVERSION = YES;
166 | CLANG_WARN_INT_CONVERSION = YES;
167 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
168 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
169 | COPY_PHASE_STRIP = NO;
170 | GCC_C_LANGUAGE_STANDARD = gnu99;
171 | GCC_DYNAMIC_NO_PIC = NO;
172 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
173 | GCC_OPTIMIZATION_LEVEL = 0;
174 | GCC_PREPROCESSOR_DEFINITIONS = (
175 | "DEBUG=1",
176 | "$(inherited)",
177 | );
178 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
179 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
180 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
181 | GCC_WARN_UNDECLARED_SELECTOR = YES;
182 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
183 | GCC_WARN_UNUSED_FUNCTION = YES;
184 | GCC_WARN_UNUSED_VARIABLE = YES;
185 | MACOSX_DEPLOYMENT_TARGET = 10.10;
186 | ONLY_ACTIVE_ARCH = YES;
187 | SDKROOT = macosx;
188 | };
189 | name = Debug;
190 | };
191 | 384E69EA1B18BA9900B74FED /* Release */ = {
192 | isa = XCBuildConfiguration;
193 | buildSettings = {
194 | ALWAYS_SEARCH_USER_PATHS = NO;
195 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
196 | CLANG_CXX_LIBRARY = "libc++";
197 | CLANG_ENABLE_MODULES = YES;
198 | CLANG_ENABLE_OBJC_ARC = YES;
199 | CLANG_WARN_BOOL_CONVERSION = YES;
200 | CLANG_WARN_CONSTANT_CONVERSION = YES;
201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
202 | CLANG_WARN_EMPTY_BODY = YES;
203 | CLANG_WARN_ENUM_CONVERSION = YES;
204 | CLANG_WARN_INT_CONVERSION = YES;
205 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
207 | COPY_PHASE_STRIP = YES;
208 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
209 | ENABLE_NS_ASSERTIONS = NO;
210 | GCC_C_LANGUAGE_STANDARD = gnu99;
211 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
212 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
213 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
214 | GCC_WARN_UNDECLARED_SELECTOR = YES;
215 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
216 | GCC_WARN_UNUSED_FUNCTION = YES;
217 | GCC_WARN_UNUSED_VARIABLE = YES;
218 | MACOSX_DEPLOYMENT_TARGET = 10.10;
219 | SDKROOT = macosx;
220 | };
221 | name = Release;
222 | };
223 | 384E69EC1B18BA9900B74FED /* Debug */ = {
224 | isa = XCBuildConfiguration;
225 | buildSettings = {
226 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
227 | GCC_PREFIX_HEADER = "cocoa-string-size-performance/cocoa-string-size-performance-Prefix.pch";
228 | PRODUCT_NAME = "$(TARGET_NAME)";
229 | };
230 | name = Debug;
231 | };
232 | 384E69ED1B18BA9900B74FED /* Release */ = {
233 | isa = XCBuildConfiguration;
234 | buildSettings = {
235 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
236 | GCC_PREFIX_HEADER = "cocoa-string-size-performance/cocoa-string-size-performance-Prefix.pch";
237 | PRODUCT_NAME = "$(TARGET_NAME)";
238 | };
239 | name = Release;
240 | };
241 | /* End XCBuildConfiguration section */
242 |
243 | /* Begin XCConfigurationList section */
244 | 384E69D81B18BA9900B74FED /* Build configuration list for PBXProject "cocoa-string-size-performance" */ = {
245 | isa = XCConfigurationList;
246 | buildConfigurations = (
247 | 384E69E91B18BA9900B74FED /* Debug */,
248 | 384E69EA1B18BA9900B74FED /* Release */,
249 | );
250 | defaultConfigurationIsVisible = 0;
251 | defaultConfigurationName = Release;
252 | };
253 | 384E69EB1B18BA9900B74FED /* Build configuration list for PBXNativeTarget "cocoa-string-size-performance" */ = {
254 | isa = XCConfigurationList;
255 | buildConfigurations = (
256 | 384E69EC1B18BA9900B74FED /* Debug */,
257 | 384E69ED1B18BA9900B74FED /* Release */,
258 | );
259 | defaultConfigurationIsVisible = 0;
260 | };
261 | /* End XCConfigurationList section */
262 | };
263 | rootObject = 384E69D51B18BA9900B74FED /* Project object */;
264 | }
265 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance.xcodeproj/xcuserdata/Abhi.xcuserdatad/xcschemes/cocoa-string-size-performance.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance.xcodeproj/xcuserdata/Abhi.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | cocoa-string-size-performance.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 384E69DC1B18BA9900B74FED
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance/cocoa-string-size-performance-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #ifdef __OBJC__
8 | #import
9 | #endif
10 |
--------------------------------------------------------------------------------
/cocoa-string-size-performance/cocoa_string_size_performance.1:
--------------------------------------------------------------------------------
1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
2 | .\"See Also:
3 | .\"man mdoc.samples for a complete listing of options
4 | .\"man mdoc for the short list of editing options
5 | .\"/usr/share/misc/mdoc.template
6 | .Dd 5/29/15 \" DATE
7 | .Dt cocoa-string-size-performance 1 \" Program name and manual section number
8 | .Os Darwin
9 | .Sh NAME \" Section Header - required - don't modify
10 | .Nm cocoa-string-size-performance,
11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key
12 | .\" words here as the database is built based on the words here and in the .ND line.
13 | .Nm Other_name_for_same_program(),
14 | .Nm Yet another name for the same program.
15 | .\" Use .Nm macro to designate other names for the documented program.
16 | .Nd This line parsed for whatis database.
17 | .Sh SYNOPSIS \" Section Header - required - don't modify
18 | .Nm
19 | .Op Fl abcd \" [-abcd]
20 | .Op Fl a Ar path \" [-a path]
21 | .Op Ar file \" [file]
22 | .Op Ar \" [file ...]
23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline
24 | arg2 ... \" Arguments
25 | .Sh DESCRIPTION \" Section Header - required - don't modify
26 | Use the .Nm macro to refer to your program throughout the man page like such:
27 | .Nm
28 | Underlining is accomplished with the .Ar macro like this:
29 | .Ar underlined text .
30 | .Pp \" Inserts a space
31 | A list of items with descriptions:
32 | .Bl -tag -width -indent \" Begins a tagged list
33 | .It item a \" Each item preceded by .It macro
34 | Description of item a
35 | .It item b
36 | Description of item b
37 | .El \" Ends the list
38 | .Pp
39 | A list of flags and their descriptions:
40 | .Bl -tag -width -indent \" Differs from above in tag removed
41 | .It Fl a \"-a flag as a list item
42 | Description of -a flag
43 | .It Fl b
44 | Description of -b flag
45 | .El \" Ends the list
46 | .Pp
47 | .\" .Sh ENVIRONMENT \" May not be needed
48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
49 | .\" .It Ev ENV_VAR_1
50 | .\" Description of ENV_VAR_1
51 | .\" .It Ev ENV_VAR_2
52 | .\" Description of ENV_VAR_2
53 | .\" .El
54 | .Sh FILES \" File used or created by the topic of the man page
55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
56 | .It Pa /usr/share/file_name
57 | FILE_1 description
58 | .It Pa /Users/joeuser/Library/really_long_file_name
59 | FILE_2 description
60 | .El \" Ends the list
61 | .\" .Sh DIAGNOSTICS \" May not be needed
62 | .\" .Bl -diag
63 | .\" .It Diagnostic Tag
64 | .\" Diagnostic informtion here.
65 | .\" .It Diagnostic Tag
66 | .\" Diagnostic informtion here.
67 | .\" .El
68 | .Sh SEE ALSO
69 | .\" List links in ascending order by section, alphabetically within a section.
70 | .\" Please do not reference files that do not exist without filing a bug report
71 | .Xr a 1 ,
72 | .Xr b 1 ,
73 | .Xr c 1 ,
74 | .Xr a 2 ,
75 | .Xr b 2 ,
76 | .Xr a 3 ,
77 | .Xr b 3
78 | .\" .Sh BUGS \" Document known, unremedied bugs
79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner
--------------------------------------------------------------------------------
/cocoa-string-size-performance/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // cocoa-string-size-performance
4 | //
5 | // Created by Abhi on 5/29/15.
6 | // Copyright (c) 2015 ___Abhishek Moothedath___. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #define ITEMS_COUNT 10000
12 | #define ITEMS_ARRAY_CHUNK_THRESHOLD 1000
13 |
14 |
15 | NS_INLINE NSArray* splitIntoChunks(NSArray *items, NSUInteger chunkSize)
16 | {
17 | NSMutableArray *chunks = [NSMutableArray new];
18 |
19 | for(NSUInteger i = 0; i < items.count; i++)
20 | {
21 | NSUInteger chunkIndex = i / chunkSize;
22 | if(chunks.count <= chunkIndex)
23 | {
24 | NSMutableArray *chunk = [NSMutableArray new];
25 | [chunks addObject:chunk];
26 | }
27 | NSMutableArray *chunk = [chunks objectAtIndex:chunkIndex];
28 | [chunk addObject:[items objectAtIndex:i]];
29 | }
30 |
31 | return chunks;
32 | }
33 |
34 | NS_INLINE void performAnalysisUsingSizeWithAttributes(NSArray* items)
35 | {
36 | double width = 0, timeTaken = 0;
37 | NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:0];
38 | for(NSString *item in items)
39 | {
40 | width = MAX(width, [item sizeWithAttributes:nil].width);
41 | }
42 | timeTaken = -[startDate timeIntervalSinceNow];
43 | NSLog(@"-----------------------Using [NSString sizeWithAttributes:]------------------\n");
44 | NSLog(@"Time to measure the width(%f) of %li strings is %f\n\n\n",width, items.count, timeTaken);
45 | }
46 |
47 | NS_INLINE void performAnalysisUsingNSAttributedString(NSArray* items)
48 | {
49 | double width = 0, timeTaken = 0;
50 | NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:0];
51 | for(NSString *item in items)
52 | {
53 | NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:item];
54 | width = MAX(width, attributedString.size.width);
55 | }
56 | timeTaken = -[startDate timeIntervalSinceNow];
57 | NSLog(@"-----------------------Using [NSAttributedString size]------------------\n");
58 | NSLog(@"Time taken to measure the width(%f) of %li strings is %f\n\n\n",width, items.count, timeTaken);
59 | }
60 |
61 | static void performAnalysisUsingNSLayoutManager(NSArray* items)
62 | {
63 | double width = 0, timeTaken = 0;
64 | NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:0];
65 |
66 | NSLayoutManager *layoutManager = [NSLayoutManager new];
67 | NSTextContainer *textContainer = [NSTextContainer new];
68 | [textContainer setLineFragmentPadding:0];
69 | [layoutManager addTextContainer:textContainer];
70 |
71 | NSMutableArray *textStorageObjects = [NSMutableArray new];
72 | for(NSString *item in items)
73 | {
74 | @autoreleasepool {
75 | NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:item];
76 | [textStorage addLayoutManager:layoutManager];
77 | [textStorageObjects addObject:textStorage];
78 | }
79 | }
80 | timeTaken = -[startDate timeIntervalSinceNow];
81 | NSLog(@"-----------------------Using NSLayoutManager------------------\n");
82 | NSLog(@"Time taken to create %li NSTextStorage objects is %f",items.count, timeTaken);
83 |
84 | startDate = [NSDate dateWithTimeIntervalSinceNow:0];
85 | [layoutManager glyphRangeForTextContainer:textContainer];
86 | width = [layoutManager usedRectForTextContainer:textContainer].size.width;
87 | timeTaken = -[startDate timeIntervalSinceNow];
88 | NSLog(@"Time taken to measure the width(%f) of %li strings is %f\n\n\n",width, items.count, timeTaken);
89 | }
90 |
91 | static void performUsingCoreText(NSArray *items)
92 | {
93 | NSArray *chunks = splitIntoChunks(items, ITEMS_ARRAY_CHUNK_THRESHOLD);
94 |
95 | __block double width = 0, timeTaken;
96 | NSFont *font = [NSFont fontWithName:@"Helvetica" size:12];
97 | NSDictionary *attributes = @{NSFontAttributeName: font};
98 | CGPathRef path = CGPathCreateWithRect(CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), NULL);
99 |
100 | dispatch_group_t group = dispatch_group_create();
101 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
102 |
103 | NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:0];
104 | for(NSArray *chunk in chunks)
105 | {
106 | dispatch_group_async(group, queue, ^{
107 | NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc]initWithString:[chunk componentsJoinedByString:@"\n"]
108 | attributes:attributes];
109 | [mutableString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
110 | CFAttributedStringRef stringRef = (__bridge CFAttributedStringRef)mutableString;
111 | CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString(stringRef);
112 | CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, mutableString.length), path, NULL);
113 |
114 | NSArray *lines = (__bridge NSArray*)CTFrameGetLines(frameRef);
115 |
116 | for(id item in lines)
117 | {
118 | CTLineRef line = (__bridge CTLineRef)item;
119 | double lineWidth = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
120 | width = MAX(width, lineWidth);
121 | }
122 | });
123 | }
124 | dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
125 |
126 | timeTaken = -[startDate timeIntervalSinceNow];
127 | NSLog(@"-----------------------Using Core Text------------------\n");
128 | NSLog(@"Time taken to measure the width(%f) of %li strings is %f",width, items.count, timeTaken);
129 | }
130 |
131 | static NSString* randomStringWithLength(int len)
132 | {
133 | NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
134 | NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
135 |
136 | for (int i=0; i