dispatch_time calculations are failing on Apple Silicon - objective-c

I have some Objective-C code that works on Intel Macs, but fails on Macs with the M1 chip.
dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0);
dispatch_time_t delta = (uint64_t)(1 * NSEC_PER_SEC);
dispatch_time_t when = dispatch_time(now, delta);
NSLog(#"now: %llu", now);
NSLog(#"delta: %llu", delta);
NSLog(#"when: %llu", when);
NSLog(#"real delta: %llu", when-now);
It'll print something like this:
now: 1272914827933
delta: 1000000000
when: 1272938827933
real delta: 24000000
1272914827933 + 1000000000 = 1273914827933, NOT 1272938827933. Why the hell is this failing, and only on some Macs?
Edit: this is also broken when running on an actual iPhone device. Just create a new Xcode project using Obj-C, paste the code into the AppDelegate, and run on your phone.

dispatch_time() second argument (delta) is in nanoseconds, however the returned dispatch_time_t depends on the CPU architecture. For Intel, the returned value is measured in nanoseconds. For Apple Silicon, the value is in ticks.
Since the two values are not measured the same way, you can not calculate numbers this way.
Here is a new test. Notice mac_absolute_time is always in advance of the dispatch_time now time.
// cc -framework Foundation -o dispatch_time dispatch_time.m
#include <time.h>
#include <mach/mach_time.h>
#import <Foundation/Foundation.h>
int main() {
dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0);
dispatch_time_t delta = (uint64_t) (1 * NSEC_PER_SEC);
dispatch_time_t when = dispatch_time(now, delta);
NSLog(#"mac_absolute_time: %llu", mach_absolute_time());
NSLog(#"now: %llu", now);
NSLog(#"delta: %llu", delta);
NSLog(#"when: %llu", when);
NSLog(#"invalid: when-now delta: %llu", when-now);
return 0;
}

Related

dispatch_time_t output value

Consider following code:
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
NSLog(#"Popped after %llu time!", popTime);
});
And it output not 2,5 seconds, but 4445236369163.
How could that be? Dispatch_time_t is just typedef for uint64_t, which simply an unsigned integer, without sign extension bit.
Even with that weird value, app works just fine, outputing log after 2,5 seconds.
If you look into documentation, dispatch_time_t is:
"A somewhat abstract representation of time."
You should not thought about it as a time expressed in second or nanosecond, so there is nothing wrong with your output.

Performance of measuring text width in AppKit

Is there a way in AppKit to measure the width of a large number of NSString objects(say a million) really fast? I have tried 3 different ways to do this:
[NSString sizeWithAttributes:]
[NSAttributedString size]
NSLayoutManager (get text width instead of height)
Here are some performance metrics
Count\Mechanism sizeWithAttributes NSAttributedString NSLayoutManager
1000 0.057 0.031 0.007
10000 0.329 0.325 0.064
100000 3.06 3.14 0.689
1000000 29.5 31.3 7.06
NSLayoutManager is clearly the way to go, but the problem being
High memory footprint(more than 1GB according to profiler) because of the creation of heavyweight NSTextStorage objects.
High creation time. All of the time taken is during creation of the above strings, which is a dealbreaker in itself.(subsequently measuring NSTextStorage objects which have glyphs created and laid out only takes about 0.0002 seconds).
7 seconds is still too slow for what I am trying to do. Is there a faster way? To measure a million strings in about a second?
In case you want to play around, Here is the github project.
Here are some ideas I haven't tried.
Use Core Text directly. The other APIs are built on top of it.
Parallelize. All modern Macs (and even all modern iOS devices) have multiple cores. Divide up the string array into several subarrays. For each subarray, submit a block to a global GCD queue. In the block, create the necessary Core Text or NSLayoutManager objects and measure the strings in the subarray. Both APIs can be used safely this way. (Core Text) (NSLayoutManager)
Regarding “High memory footprint”: Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint.
Regarding “All of the time taken is during creation of the above strings, which is a dealbreaker in itself”: Are you saying all the time is spent in these lines:
double random = (double)arc4random_uniform(1000) / 1000;
NSString *randomNumber = [NSString stringWithFormat:#"%f", random];
Formatting a floating-point number is expensive. Is this your real use case? If you just want to format a random rational of the form n/1000 for 0 ≤ n < 1000, there are faster ways. Also, in many fonts, all digits have the same width, so that it's easy to typeset columns of numbers. If you pick such a font, you can avoid measuring the strings in the first place.
UPDATE
Here's the fastest code I've come up with using Core Text. The dispatched version is almost twice as fast as the single-threaded version on my Core i7 MacBook Pro. My fork of your project is here.
static CGFloat maxWidthOfStringsUsingCTFramesetter(
NSArray *strings, NSRange range) {
NSString *bigString =
[[strings subarrayWithRange:range] componentsJoinedByString:#"\n"];
NSAttributedString *richText =
[[NSAttributedString alloc]
initWithString:bigString
attributes:#{ NSFontAttributeName: (__bridge NSFont *)font }];
CGPathRef path =
CGPathCreateWithRect(CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), NULL);
CGFloat width = 0.0;
CTFramesetterRef setter =
CTFramesetterCreateWithAttributedString(
(__bridge CFAttributedStringRef)richText);
CTFrameRef frame =
CTFramesetterCreateFrame(
setter, CFRangeMake(0, bigString.length), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
for (id item in lines) {
CTLineRef line = (__bridge CTLineRef)item;
width = MAX(width, CTLineGetTypographicBounds(line, NULL, NULL, NULL));
}
CFRelease(frame);
CFRelease(setter);
CFRelease(path);
return (CGFloat)width;
}
static void test_CTFramesetter() {
runTest(__func__, ^{
return maxWidthOfStringsUsingCTFramesetter(
testStrings, NSMakeRange(0, testStrings.count));
});
}
static void test_CTFramesetter_dispatched() {
runTest(__func__, ^{
dispatch_queue_t gatherQueue = dispatch_queue_create(
"test_CTFramesetter_dispatched result-gathering queue", nil);
dispatch_queue_t runQueue =
dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
dispatch_group_t group = dispatch_group_create();
__block CGFloat gatheredWidth = 0.0;
const size_t Parallelism = 16;
const size_t totalCount = testStrings.count;
// Force unsigned long to get 64-bit math to avoid overflow for
// large totalCounts.
for (unsigned long i = 0; i < Parallelism; ++i) {
NSUInteger start = (totalCount * i) / Parallelism;
NSUInteger end = (totalCount * (i + 1)) / Parallelism;
NSRange range = NSMakeRange(start, end - start);
dispatch_group_async(group, runQueue, ^{
double width =
maxWidthOfStringsUsingCTFramesetter(testStrings, range);
dispatch_sync(gatherQueue, ^{
gatheredWidth = MAX(gatheredWidth, width);
});
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return gatheredWidth;
});
}

How many times a second should CADisplayLink's displayLink be called?

I have a CADisplayLink running in line with Chipmunk Physics, and I'm getting very slow performance. I put an NSLog in the method that's called on the CADisplayLink update, and it's being called an average of 22 times per second. I was under the impression that that should be nearer 60. I have the frameInterval set to 1, so should it be 60fps, in a perfect world? The delta times are averaging around 0.0167 seconds (and 1 / 60 IS 0.0167, which is confusing me even further).
I just have four walls around the bounds of my screen and just eight circle-shaped bodies on-screen, updating to UIButton instances on each call, so I don't think I'm doing anything that should tax it to this extent on both my 4S and iPad3. I'm applying a random force to each button once every 2.5 seconds in a separate method. Running in the simulator is butter-smooth, so it's a device-only issue. Can anyone help me spot what's causing the slowdown here, and what I can do about it?
Here's the relevant code, first that which sets up the link:
[NSTimer scheduledTimerWithTimeInterval: 2.5f target: self selector: #selector(updateForces) userInfo: nil repeats: YES];
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: #selector(update)];
_displayLink.frameInterval = 1;
[_displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
Here's the method that should be called (I think!) 60 times per second, but is called only 22 or so:
if (!gameIsPaused) {
cpFloat dt = _displayLink.duration * _displayLink.frameInterval;
cpSpaceStep([[AGChipmunkSpace sharedInstance] space], dt);
for (LCBall *i in balls) {
cpVect pos1 = cpBodyGetPos(i.body);
CGAffineTransform trans1 = CGAffineTransformMakeTranslation(pos1.x, pos1.y);
CGAffineTransform rot1 = CGAffineTransformMakeRotation(cpBodyGetAngle(i.body));
i.button.transform = CGAffineTransformConcat(rot1, trans1);
}
}
And finally, here's the method that's called every 2.5 seconds, to apply the random forces (updateForces):
if (!gameIsPaused) {
for (LCBall *i in balls) {
int randomAngle = arc4random() % 360;
CGPoint point1 = [self getVectorFromAngle: randomAngle AndMagnitude: (arc4random() % 40) + ((arc4random() % 20) + 15)];
i.body -> f = cpv(point1.x, point1.y);
}
}
(Also, here's my method to get a vector from an angle, which I doubt is causing the issue):
angle = (angle / 180.0) * M_PI;
float x = magnitude * cos(angle);
float y = magnitude * sin(angle);
CGPoint point = CGPointMake(x, y);
return point;
Turns out I had a method on a different UIViewController in my storyboard that was firing every 0.1 seconds that hadn't turned off, and combined with the physics processing, was bogging things down.

Get the precise time of system bootup on iOS/OS X

Is there an API to obtain the NSDate or NSTimeInterval representing the time the system booted? Some APIs such as [NSProcessInfo systemUptime] and Core Motion return time since boot. I need to precisely correlate these uptime values with NSDates, to about a millisecond.
Time since boot ostensibly provides more precision, but it's easy to see that NSDate already provides precision on the order of 100 nanoseconds, and anything under a microsecond is just measuring interrupt latency and PCB clock jitter.
The obvious thing is to subtract the uptime from the current time [NSDate date]. But that assumes that time does not change between the two system calls, which is, well, hard to accomplish. Moreover if the thread is preempted between the calls, everything is thrown off. The workaround is to repeat the process several times and use the smallest result, but yuck.
NSDate must have a master offset it uses to generate objects with the current time from the system uptime, is there really no way to obtain it?
In OSX you could use sysctl(). This is how the OSX Unix utility uptime does it. Source code is available - search for boottime.
Fair warning though, in iOS i have no idea if this would work.
UPDATE: found some code :)
#include <sys/types.h>
#include <sys/sysctl.h>
#define MIB_SIZE 2
int mib[MIB_SIZE];
size_t size;
struct timeval boottime;
mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
size = sizeof(boottime);
if (sysctl(mib, MIB_SIZE, &boottime, &size, NULL, 0) != -1)
{
// successful call
NSDate* bootDate = [NSDate dateWithTimeIntervalSince1970:
boottime.tv_sec + boottime.tv_usec / 1.e6];
}
see if this works...
The accepted answer, using systcl, works, but the values returned by sysctl for KERN_BOOTTIME, at least in my testing (Darwin Kernel Version 11.4.2), are always in whole seconds (the microseconds field, tv_usec, is 0). This means the resulting time may be up to 1 second off, which is not very accurate.
Also, having compared that value, to one derived experimentally from the difference between the REALTIME_CLOCK and CALENDAR_CLOCK, they sometimes differ by a couple seconds, so its not clear whether the KERN_BOOTTIME value corresponds exactly to the time-basis for the uptime clocks.
There is another way. It could give result slightly different (less or more) than accepted answer
I have compared them. I get difference -7 second for OSX 10.9.3 and +2 second for iOS 7.1.1
As i understand this way gives same result if wall clock changed, but accepted answer gives different results if wall clock changed...
Here code:
static CFAbsoluteTime getKernelTaskStartTime(void) {
enum { MICROSECONDS_IN_SEC = 1000 * 1000 };
struct kinfo_proc info;
bzero(&info, sizeof(info));
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID = 0.
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, 0};
// Call sysctl.
size_t size = sizeof(info);
const int sysctlResult = sysctl(mib, COUNT_ARRAY_ELEMS(mib), &info, &size, NULL, 0);
assert(0 != sysctlResult);
const struct timeval * timeVal = &(info.kp_proc.p_starttime);
NSTimeInterval result = -kCFAbsoluteTimeIntervalSince1970;
result += timeVal->tv_sec;
result += timeVal->tv_usec / (double)MICROSECONDS_IN_SEC;
return result;
}
Refer to this category
NSDate+BootTime.h
#import <Foundation/Foundation.h>
#interface NSDate (BootTime)
+ (NSDate *)bootTime;
+ (NSTimeInterval)bootTimeTimeIntervalSinceReferenceDate;
#end
NSDate+BootTime.m
#import "NSDate+BootTime.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#implementation NSDate (BootTime)
+ (NSDate *)bootTime {
return [NSDate dateWithTimeIntervalSinceReferenceDate:[NSDate bootTimeTimeIntervalSinceReferenceDate]];
}
+ (NSTimeInterval)bootTimeTimeIntervalSinceReferenceDate {
return getKernelTaskStartTime();
}
////////////////////////////////////////////////////////////////////////
#pragma mark - Private
////////////////////////////////////////////////////////////////////////
#define COUNT_ARRAY_ELEMS(arr) sizeof(arr)/sizeof(arr[0])
static CFAbsoluteTime getKernelTaskStartTime(void) {
enum { MICROSECONDS_IN_SEC = 1000 * 1000 };
struct kinfo_proc info;
bzero(&info, sizeof(info));
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID = 0.
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, 0};
// Call sysctl.
size_t size = sizeof(info);
const int sysctlResult = sysctl(mib, COUNT_ARRAY_ELEMS(mib), &info, &size, NULL, 0);
if (sysctlResult != -1) {
const struct timeval * timeVal = &(info.kp_proc.p_starttime);
NSTimeInterval result = -kCFAbsoluteTimeIntervalSince1970;
result += timeVal->tv_sec;
result += timeVal->tv_usec / (double)MICROSECONDS_IN_SEC;
return result;
}
return 0;
}
#end
The routines inside mach/mach_time.h are guaranteed to be monotonically increasing, unlike NSDate.

Convert first number in an NSString into an Integer?

I have an NSString like so:
#"200hello"
or
#"0 something"
What I would like to be able to do is take the first occuring number in the NSString and convert it into an int.
So that #"200hello" would become int = 200.
and #"0 something" would become int = 0.
int value;
BOOL success = [[NSScanner scannerWithString:#"1000safkaj"] scanInteger:&value];
If the number is not always at the beginning:
NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
int value = [[#"adfsdg1000safkaj" stringByTrimmingCharactersInSet:nonDigits] intValue];
Steve Ciarcia once said a single measured result is worth more than a hundred engineers opinions. And so begins the first, and last, "How to get an int value from a NSString" cook-off!
The following are the contenders: (microseconds taken and number of bytes used per match using the incredibly high precision for(x=0; x<100000; x++) {} micro-benchmark that has been handed down through the generations. Time measured via getrusage(), bytes used via malloc_size(). The string to be matched was normalized to 'foo 2020hello' for all cases, except those that required the number to be at the start. All conversions were normalized to 'int'. The two numbers after the time are normalized results relative to the best and worst performers.)
EDIT: These were the original numbers posted, see below for updated numbers. Also, times are from a 2.66 Core2 macbook pro.
characterSet time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
original RKL time: 1.20686us 11.0 / 0.88 memory: 16 bytes (via Dave DeLong)
modified RKL time: 1.07631us 9.9 / 0.78 memory: 16 bytes (me, changed regex to \d+)
scannerScanInt time: 0.49951us 4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
intValue time: 0.16739us 1.5 / 0.12 memory: 0 bytes (via zpasternack)
rklIntValue time: 0.10925us 1.0 / 0.08 memory: 0 bytes (me, modified RKL example)
As I noted somewhere else in this message, I originally threw this in to a unit test harness I use for RegexKitLite. Well, being the unit test harness meant that I was testing with my private copy of RegexKitLite... which just so happened to have a bunch of debug stuff tacked on while tracking down a bug report from a user. The above timing results are approximately equivalent to calling [valueString flushCachedRegexData]; inside the for() {} timing loop (which was essentially what the inadvertent debugging stuff was doing). The following results are from compiling against the latest, unmodified, RegexKitLite available (3.1):
characterSet time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
original RKL time: 0.58446us 5.3 / 0.43 memory: 16 bytes (via Dave DeLong)
modified RKL time: 0.54628us 5.0 / 0.40 memory: 16 bytes (me, changed regex to \d+)
scannerScanInt time: 0.49951us 4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
intValue time: 0.16739us 1.5 / 0.12 memory: 0 bytes (via zpasternack)
rklIntValue time: 0.10925us 1.0 / 0.08 memory: 0 bytes (me, modified RKL example)
This is slightly better than a 50% improvement. If you're willing to live slightly dangerously, you can coax a bit more speed out with the -DRKL_FAST_MUTABLE_CHECK compile time option:
original RKL time: 0.51188us 4.7 / 0.37 memory: 16 bytes using intValue
modified RKL time: 0.47665us 4.4 / 0.35 memory: 16 bytes using intValue
original RKL time: 0.44337us 4.1 / 0.32 memory: 16 bytes using rklIntValue
modified RKL time: 0.42128us 3.9 / 0.31 memory: 16 bytes using rklIntValue
This is usually good for about another 10% boost, and it's fairly safe to use (for more info, see the RKL docs). And while I was at it... why not use the faster rklIntValue too? Is there some kind of prize for beating the native, built in Foundation methods using an external, third party, non-integrated general purpose regex pattern matching engine? Don't believe the hype that "regexes are slow".
END EDIT
The RegexKitLite example can be found at RegexKitLite Fast Hex Conversion. Basically swapped strtoimax for strtol, and added a line of code to skip over leading characters that weren't [+-0-9]. (full disclosure: I'm the author of RegexKitLite)
Both 'scannerScanInt' and 'intValue' suffer from the problem that the number to be extracted must be at the start of the string. I think both will skip any leading white-space.
I modified Dave DeLongs regex from '[^\d]*(\d+)' to just '\d+' because that's all that's really needed, and it manages to get rid of a capture group usage to boot.
So, based on the above data, I offer the following recommendations:
There's basically two different capability classes here: Those that can tolerate extra 'stuff' and still get you the number (characterSet, RegexKitLite matchers, and rklIntValue), and those that basically need the number to be the very first thing in the string, tolerating at most some white space padding at the start (scannerScanInt and intValue).
Do not use NSCharacterClass to do these kinds of things. For the given example, 16 bytes is used to instantiate the first NSCharacterClass, then 32 bytes for the inverted version, and finally 16 bytes for the string result. The fact that a general purpose regex engine outperforms it by a double digit percentage margin while using less memory pretty much seals the deal.
(keep in mind I wrote RegexKitLite, so take the following with whatever sized grain of salt you feel is appropriate).
RegexKitLite turns in good times and uses the smallest amount of memory possible considering the fact that it's returning a NSString object. Since it uses a LRU cache internally for all the ICU regex engine stuff, those costs get amortized over time and repeated uses. It also takes seconds to change the regex if the need comes up (hex values? hex floats? Currencies? Dates? No problem.)
For the simple matchers, it should be obvious that you definitely should NOT use NSScanner to do these kinds of things. Using NSScanner to do a 'scanInt:' is no different than just calling [aString intValue]. The produce the same results with the same caveats. The difference is NSScanner takes FIVE times longer to the same thing, while wasting 32 bytes of memory in the process.... while [aString intValue] (probably) doesn't require one byte of memory to perform its magic- it probably just calls strtoimax() (or an equivalent) and since it has direct access to the pointer holding the strings contents....
The final one is 'rklIntValue', which again is just a slightly tweaked version of what you can find at (the 'RegexKitLite Fast Hex Conversion' link above, stackoverflow won't let me post it twice). It uses CoreFoundation to try to get direct access to the strings buffer, and failing that, allocates some space off the stack and copies a chunk of the string to that buffer. This takes all of, oh, three instructions on the CPU, and is fundamentally impossible to 'leak' like a malloc() allocation. So it uses zero memory and goes very, very fast. As an extra bonus, you pass to strtoXXX() the number base of the string to convert. 10 for decimal, 16 for hex (automatically swallowing a leading 0x if present), or 0 for automagic detection. It's a trivial, single line of code to skip the pointer over any 'uninteresting' characters until you get to what you want (I choose -,+, and 0-9). Also trivial to swap in something like strtod() if you need to parse double values. strtod() converts just about any valid floating point text: NAN, INF, hex floats, you name it.
EDIT:
Per request of the OP, here's a trimmed and minified version of the code that I used to perform the tests. One thing of note: While putting this together, I noticed that Dave DeLongs original regex didn't quite work. The problem is in the negated character set- meta-character sequences inside sets (ie, [^\d]+) mean the literal character, not the special meaning they have outside the character set. Replaced with [^\p{DecimalNumber}]*, which has the intended effect.
I originally bolted this stuff to a RegexKitLite unit test harness, so I left some bits and pieces for GC in. I forgot all about this, but the short version of what happens when GC is turned on is that times of everything BUT RegexKitLite double (that is, takes twice as long). RKL only takes about 75% longer (and that took an enormous, non-trivial amount of effort to get when I was developing it). The rklIntValue time stays exactly the same.
Compile with shell% gcc -DNS_BLOCK_ASSERTIONS -mdynamic-no-pic -std=gnu99 -O -o stackOverflow stackOverflow.m RegexKitLite.m -framework Foundation -licucore -lauto
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <objc/objc-auto.h>
#include <malloc/malloc.h>
#import <Foundation/Foundation.h>
#import "RegexKitLite.h"
static double cpuTimeUsed(void);
static double cpuTimeUsed(void) {
struct rusage currentRusage;
getrusage(RUSAGE_SELF, &currentRusage);
double userCPUTime = ((((double)currentRusage.ru_utime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_utime.tv_usec)) / 1000000.0;
double systemCPUTime = ((((double)currentRusage.ru_stime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_stime.tv_usec)) / 1000000.0;
double CPUTime = userCPUTime + systemCPUTime;
return(CPUTime);
}
#interface NSString (IntConversion)
-(int)rklIntValue;
#end
#implementation NSString (IntConversion)
-(int)rklIntValue
{
CFStringRef cfSelf = (CFStringRef)self;
UInt8 buffer[64];
const char *cptr, *optr;
char c;
if((cptr = optr = CFStringGetCStringPtr(cfSelf, kCFStringEncodingMacRoman)) == NULL) {
CFRange range = CFRangeMake(0L, CFStringGetLength(cfSelf));
CFIndex usedBytes = 0L;
CFStringGetBytes(cfSelf, range, kCFStringEncodingUTF8, '?', false, buffer, 60L, &usedBytes);
buffer[usedBytes] = 0U;
cptr = optr = (const char *)buffer;
}
while(((cptr - optr) < 60) && (!((((c = *cptr) >= '0') && (c <= '9')) || (c == '-') || (c == '+'))) ) { cptr++; }
return((int)strtoimax(cptr, NULL, 0));
}
#end
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#ifdef __OBJC_GC__
objc_start_collector_thread();
objc_clear_stack(OBJC_CLEAR_RESIDENT_STACK);
objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
#endif
BOOL gcEnabled = ([objc_getClass("NSGarbageCollector") defaultCollector] != NULL) ? YES : NO;
NSLog(#"Garbage Collection is: %#", gcEnabled ? #"ON" : #"OFF");
NSLog(#"Architecture: %#", (sizeof(void *) == 4UL) ? #"32-bit" : #"64-bit");
double startTime = 0.0, csTime = 0.0, reTime = 0.0, re2Time = 0.0, ivTime = 0.0, scTime = 0.0, rklTime = 0.0;
NSString *valueString = #"foo 2020hello", *value2String = #"2020hello";
NSString *reRegex = #"[^\\p{DecimalNumber}]*(\\d+)", *re2Regex = #"\\d+";
int value = 0;
NSUInteger x = 0UL;
{
NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *nonDigits = [digits invertedSet];
NSScanner *scanner = [NSScanner scannerWithString:value2String];
NSString *csIntString = [valueString stringByTrimmingCharactersInSet:nonDigits];
NSString *reString = [valueString stringByMatching:reRegex capture:1L];
NSString *re2String = [valueString stringByMatching:re2Regex];
[scanner scanInt:&value];
NSLog(#"digits : %p, size: %lu", digits, malloc_size(digits));
NSLog(#"nonDigits : %p, size: %lu", nonDigits, malloc_size(nonDigits));
NSLog(#"scanner : %p, size: %lu, int: %d", scanner, malloc_size(scanner), value);
NSLog(#"csIntString : %p, size: %lu, '%#' int: %d", csIntString, malloc_size(csIntString), csIntString, [csIntString intValue]);
NSLog(#"reString : %p, size: %lu, '%#' int: %d", reString, malloc_size(reString), reString, [reString intValue]);
NSLog(#"re2String : %p, size: %lu, '%#' int: %d", re2String, malloc_size(re2String), re2String, [re2String intValue]);
NSLog(#"intValue : %d", [value2String intValue]);
NSLog(#"rklIntValue : %d", [valueString rklIntValue]);
}
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] intValue]; } csTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByMatching:reRegex capture:1L] intValue]; } reTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByMatching:re2Regex] intValue]; } re2Time = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [valueString rklIntValue]; } rklTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [value2String intValue]; } ivTime = (cpuTimeUsed() - startTime) / (double)x;
for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { [[NSScanner scannerWithString:value2String] scanInt:&value]; } scTime = (cpuTimeUsed() - startTime) / (double)x;
NSLog(#"csTime : %.5lfus", csTime * 1000000.0);
NSLog(#"reTime : %.5lfus", reTime * 1000000.0);
NSLog(#"re2Time: %.5lfus", re2Time * 1000000.0);
NSLog(#"scTime : %.5lfus", scTime * 1000000.0);
NSLog(#"ivTime : %.5lfus", ivTime * 1000000.0);
NSLog(#"rklTime: %.5lfus", rklTime * 1000000.0);
[NSString clearStringCache];
[pool release]; pool = NULL;
return(0);
}
If the int value is always at the beginning of the string, you can simply use intValue.
NSString *string = #"123hello";
int myInt = [string intValue];
I would probably use a regular expression (implemented with the stellar RegexKitLite). Then it'd be something like:
#import "RegexKitLite.h"
NSString * original = #"foo 220hello";
NSString * number = [original stringByMatching:#"[^\\d]*(\\d+)" capture:1];
return [number integerValue];
The regex #"[^\d]*(\d+)" means "any number of non-numeric characters followed by at least one numeric character".
I came up with my own answer, potentially faster and easier than the others provided.
My answer does assume you know the position the number begins and ends though...
NSString *myString = #"21sss";
int numberAtStart = [[myString substringToIndex:2] intValue];
You can get to to work the other way too:
NSString *myString = #"sss22";
int numberAtEnd = [[myString substringFromIndex:3] intValue];
int i;
NSString* string;
i = [string intValue];

Categories