Why NSMutableDictionary's setValue:forKey doesn't work - objective-c

I want to write a timer class that records average elapsed time of a function. So I want to save how many times the function is ran. But the item in dictionary isn't changed.
here is my code:
#implementation Timer {
NSDate *startTime;
// NSString *item;
NSMutableDictionary *itemCounts;
NSMutableDictionary *itemTimes;
}
- (void)tic {
startTime = [NSDate date];
}
- (void)tocWithMessage:(NSString*)message {
if (itemCounts[message] == nil) {
// itemCounts[message] = #1;
[itemCounts setValue:#1 forKey:message];
NSLog(#"%d", [itemCounts[message] integerValue]);
} else {
itemCounts[message] = #([itemCounts[message] integerValue] + 1);
}
double totalTime = [itemTimes[message] doubleValue];
double thisTime = -[startTime timeIntervalSinceNow] * 1000;
totalTime += thisTime;
itemTimes[message] = #(totalTime);
int count = [itemCounts[message] integerValue];
double averageTime = totalTime / count;
NSLog(#"%#: No.%d, average time: %lfms", message, count, averageTime);
// std::cout << message << ": " << 1. * (clock() - timerTimestamp) / CLOCKS_PER_SEC * 1000 << "ms" << std::endl;
}
#end
But the itemCounts[message] is always nil. Please help me :)
Thanks in advance!

You forgot to initialize the dictionaries. In Swift you would have seen a crash if you do like this :)

You have only declared the dictionaries.
Before using them you have to initialize them somewhere:
itemCounts = [[NSMutableDictionary alloc] init];
itemTimes = [[NSMutableDictionary alloc] init];

Related

Is there a substantial performance difference between using NSPredicate predicateWithBlock and using a block in a for loop?

Is there a substantial difference in performance (in either direction) between filtering like this:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id,NSDictionary*)];
NSArray *filtered = [sourceArray filteredArrayUsingPredicate:predicate];
versus filtering like this:
BOOL (^filterPredicate)(id,NSDictionary*) predicate = ^BOOL(id sourceObject, NSDictionary *bindings) {};
foreach (id sourceObject in sourceArray) {
if (filterPredicate(sourceObject)) {
filtered addObject:sourceObject;
}
}
My gut feeling is that if there is any difference, the second way would be faster as the first has some extra baggage from the NSPredicate class.
If there is a difference, what is it, how big is it, and what is the source of it?
Per #wain's recommendation I tested to find out.
I need to expand the test to get a more accurate reading, but here's my methodology and result:
Create array of SomeObject
Set SomeObject properties for each object
Filter on NSString rangeOfString
Compare execution times
SomeObject.h
#interface SomeObject : NSObject
#property (nonatomic, strong) NSNumber *numericProperty;
#property (nonatomic, strong) NSString *stringProperty;
#property (nonatomic, strong) NSArray *arrayProperty;
#property (nonatomic, strong) NSDate *dateProperty;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *objectsToSort = [self generateArrayOfRandomSomeObject:1000];
// Sorting with predicate
BOOL (^predicateBlock)(id, NSDictionary *) = ^BOOL(id sourceObject, NSDictionary *bindings) {
SomeObject *destinationTypeObject = (SomeObject *)sourceObject;
if (sourceObject) {
NSRange r = [destinationTypeObject.stringProperty rangeOfString:#"ab" options:NSCaseInsensitiveSearch];
if (r.location != NSNotFound) {
return true;
} else {
return false;
}
}
return NO;
};
NSPredicate *predicate = [NSPredicate predicateWithBlock:predicateBlock];
NSDate *predicateMethodStart = [NSDate date];
NSArray *filteredWithPredicate = [objectsToSort filteredArrayUsingPredicate:predicate];
NSDate *predicateMethodFinish = [NSDate date];
NSTimeInterval predicateTime = [predicateMethodFinish timeIntervalSinceDate:predicateMethodStart];
NSLog(#"predicateTime = %f", predicateTime);
NSData *blockStart = [NSDate date];
NSMutableArray *filteredWithBlock = [NSMutableArray array];
for (id sourceObject in objectsToSort) {
if (predicateBlock(sourceObject, nil)) {
[filteredWithBlock addObject:sourceObject];
}
}
NSDate *blockFinish = [NSDate date];
NSTimeInterval blockTime = [blockFinish timeIntervalSinceDate:blockStart];
NSLog(#"blockTime = %f", blockTime);
}
- (NSArray *)generateArrayOfRandomSomeObject:(NSUInteger)numberOfObjects
{
NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:numberOfObjects];
for (int count = 0; count < numberOfObjects; count++) {
SomeObject *object = [[SomeObject alloc] init];
int length = arc4random_uniform(10) + 4;
unichar buf[length];
for (int idx = 0; idx < length; idx++) {
buf[idx] = (unichar)('a' + arc4random_uniform(26));
}
object.stringProperty = [NSString stringWithCharacters:buf length:length];
u_int32_t max = ((u_int32_t)((u_int32_t)numberOfObjects * 10));
u_int32_t randomNumber = arc4random_uniform(max);
object.numericProperty = [NSNumber numberWithUnsignedInteger:randomNumber];
[objects addObject:object];
}
return objects;
}
#end
And the winner is... NSPredicate by a long shot. Why? I have no idea.
2015-05-13 15:17:20.708 TestingPerformance[60942:5787881] predicateTime = 0.000768
2015-05-13 15:17:20.710 TestingPerformance[60942:5787881] blockTime = 0.000923
Edit 0: Mistake in original code and test with initializing appropriately-sized array
Somehow despite my typo of NSData *blockStart in the original code, it still worked! I fixed it.
And I initialize the mutable array with a capacity of 1000.
New results:
2015-05-13 15:26:51.950 TestingPerformance[61157:5792057] predicateTime = 0.000771
2015-05-13 15:26:51.952 TestingPerformance[61157:5792057] blockTime = 0.000864
So the predicateTime is consistent and the blockTime improves with this small adjustment.
Edit 1: Using traditional for loop without iterator
for (int i = 0; i < objectsToSort.count; i++) {
if (predicateBlock(objectsToSort[i], nil)) {
[filteredWithBlock addObject:objectsToSort[i]];
}
}
2015-05-13 15:30:25.464 TestingPerformance[61282:5793520] predicateTime = 0.000775
2015-05-13 15:30:25.467 TestingPerformance[61282:5793520] blockTime = 0.001079
Edit 2: Increase N, added random array access
- (void)viewDidLoad {
[super viewDidLoad];
NSUInteger N = 100000;
NSArray *objectsToSort = [self generateArrayOfRandomSomeObject:N];
// Sorting with predicate
BOOL (^predicateBlock)(id, NSDictionary *) = ^BOOL(id sourceObject, NSDictionary *bindings) {
SomeObject *destinationTypeObject = (SomeObject *)sourceObject;
if (sourceObject) {
NSRange r = [destinationTypeObject.stringProperty rangeOfString:#"ab" options:NSCaseInsensitiveSearch];
if (r.location != NSNotFound) {
return true;
} else {
return false;
}
}
return NO;
};
NSPredicate *predicate = [NSPredicate predicateWithBlock:predicateBlock];
NSDate *predicateMethodStart = [NSDate date];
NSArray *filteredWithPredicate = [objectsToSort filteredArrayUsingPredicate:predicate];
NSDate *predicateMethodFinish = [NSDate date];
[self doStuffWithArray:filteredWithPredicate];
NSDate *predicateStuffFinish = [NSDate date];
NSDate *blockStart = [NSDate date];
NSMutableArray *filteredWithBlock = [NSMutableArray arrayWithCapacity:N];
for (id sourceObject in objectsToSort) {
if (predicateBlock(sourceObject, nil)) {
[filteredWithBlock addObject:sourceObject];
}
}
NSDate *blockFinish = [NSDate date];
[self doStuffWithArray:filteredWithBlock];
NSDate *blockDoStuffFinish = [NSDate date];
NSLog(#"predicateTime = %f", [predicateMethodFinish timeIntervalSinceDate:predicateMethodStart]);
NSLog(#"predicate time to do stuff = %f", [predicateStuffFinish timeIntervalSinceDate:predicateMethodFinish]);
NSLog(#"blockTime = %f", [blockFinish timeIntervalSinceDate:blockStart]);
NSLog(#"block time to do stuff = %f", [blockDoStuffFinish timeIntervalSinceDate:blockFinish]);
}
Here is do stuff
- (void)doStuffWithArray:(NSArray *)arrayOfSomeObjects {
NSLog(#"Filtered array has %lu objects.", (unsigned long)arrayOfSomeObjects.count);
NSUInteger numberOfSpotsToCheck = ((NSUInteger)(arrayOfSomeObjects.count / 4));
for (int i = 0; i < numberOfSpotsToCheck; i++) {
u_int32_t randomIndex = arc4random_uniform((u_int32_t)arrayOfSomeObjects.count);
SomeObject *o = (SomeObject *)[arrayOfSomeObjects objectAtIndex:randomIndex];
NSLog(#"Object at index: %d \n description is %# \n stringProprerty is %# \n numericProperty is %i",
i,
o,
o.stringProperty,
[o.numericProperty unsignedIntValue]);
}
}
And still block time is a bit slower in both access and filtering.
2015-05-13 16:10:26.750 TestingPerformance[62201:5810268] predicateTime = 0.086866
2015-05-13 16:10:26.750 TestingPerformance[62201:5810268] predicate time to do stuff = 0.531776
2015-05-13 16:10:26.751 TestingPerformance[62201:5810268] blockTime = 0.107424
2015-05-13 16:10:26.751 TestingPerformance[62201:5810268] block time to do stuff = 0.684613

copy and sort an NSArray in one step

I am trying to sort an NSArray.
Unfortunately the array appears to stay empty.
I start with an NSMutableArray with normal distributed data called "gaussianPopulation" which is then to be copied and sorted into the NSArray "sortedData".
I checked a similar post here: Sorting NSArray and returning NSArray?
But I am still missing something...
Population.h:
#define ARC4RANDOM_MAX 0x100000000
#import <Foundation/Foundation.h>
#interface Population : NSObject
#property NSMutableArray *totalPopulation;
#property NSMutableArray *gaussianPopulation;
-(void)createPopulation; // test purpose to create uniform distribution. not used anymore.
-(void)createGaussianPopulation;
-(double)calculateAndersonDarling;
#end
and Population.m:
#import "Population.h"
#include <math.h>
#implementation Population
-(void)createPopulation
{
_totalPopulation = [[NSMutableArray alloc] init];
srandom((int)time(NULL));
NSNumber *randomNumber;
for (int i = 0 ; i < 10000 ; i++)
{
randomNumber = [[NSNumber alloc] initWithInt:random()];
[_totalPopulation addObject:randomNumber];
NSLog(#"%#", randomNumber);
}
}
-(void)createGaussianPopulation
{
for (int i = 0; i < 5000 ; i++)
{
double x1, x2, w, y1, y2;
do
{
x1 = 2.0 * ((double)arc4random() / ARC4RANDOM_MAX) - 1.0;
x2 = 2.0 * ((double)arc4random() / ARC4RANDOM_MAX) - 1.0;
w = x1 * x1 + x2 * x2;
} while (w >= 1.0);
w = sqrt((-2.0 * log(w))/w);
y1 = x1 * w;
y2 = x2 * w;
NSNumber *value1 = [NSNumber numberWithDouble:y1];
NSNumber *value2 = [NSNumber numberWithDouble:y2];
[self.gaussianPopulation addObject:value1];
[self.gaussianPopulation addObject:value2];
//NSLog(#"%# %#", value1, value2);
NSString *pathToDesktop = [NSString stringWithFormat:#"/Users/%#/Desktop", NSUserName()];
//make a file name to write the data to using the documents directory:
NSString *fileName = [NSString stringWithFormat:#"%#/testfile.tst", pathToDesktop];
//create content
NSString *content = [NSString stringWithFormat:#"%#\n%# \n", [value1 stringValue], [value2 stringValue]];
NSFileHandle *myHandle = [NSFileHandle fileHandleForWritingAtPath:fileName];
[myHandle seekToEndOfFile];
[myHandle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
}
}
-(double)calculateAndersonDarling
{
NSLog((#"in method calculateAndersonDarling"));
NSLog(#"%i", [self.gaussianPopulation count]);
for (id eachObject in self.gaussianPopulation)
{
NSLog(#"%#", eachObject);
}
NSArray *sortedData = [NSArray arrayWithArray:[self.gaussianPopulation sortedArrayUsingSelector:#selector(compare:)]];
//NSArray *sortedData = [self.gaussianPopulation sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%i", [sortedData count]);
for (id eachObject in sortedData)
{
NSLog(#"%#", eachObject);
}
return 0.0; //return value to be done later
}
#end
As you can see (where the commented line is) I have tried different approaches.
But it seems like the sortedData array remains empty.
The size via NSLog is reported as zero and there is no output for the contents.
Any help would be appreciated.
just in case:
#import <Foundation/Foundation.h>
#import "Population.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
Population *myPopulation;
myPopulation = [[Population alloc] init];
[myPopulation createGaussianPopulation];
[myPopulation calculateAndersonDarling];
}
return 0;
}
NSLog output:
2014-09-12 16:46:44.647 Statistik[4158:303] Hello, World!
2014-09-12 16:46:45.154 Statistik[4158:303] in method calculateAndersonDarling
2014-09-12 16:46:45.154 Statistik[4158:303] 0
2014-09-12 16:46:45.155 Statistik[4158:303] 0
Program ended with exit code: 0
clearly the Array gaussianPopulation is already empty in the method calculateAndersonDarling, or is my calculation of the array size wrong?
But why should it have lost its contents???
You forgot to allocate the gaussianPopulation array before adding elements to it. In Objective-C you can call methods on nil objects, it won't have any effect (no crash, no warning). That's why it's sometimes difficult to see these bugs. Just initialize the array at the beginning of the method:
- (void)createGaussianPopulation
{
self.gaussianPopulation = [NSMutableArray array];
...

Printing a string object from an NSMutableArray

I stored some strings in objects and added the objects to an NSMutableArray. Now I want to print the strings in each element of the array. Clearly, I'm doing something wrong. I'm going to back and review these basics, but I was hoping someone could explain how I can print the string instead of the what looks to be the element address.
/** interface **/
#property (nonatomic, copy) NSString*myNumber;
-(void)setNumber: (NSString*) randomNumber;
/** implementation **/
#synthesize myNumber;
-(void) setNumber:(NSString *)randomNumber
{
myNumber = randomNumber;
}
/**main**/
Fraction * aFrac = [[Fraction alloc] init];
[aFrac setNumber:#"5/6"];
Fraction * bFrac = [[Fraction alloc] init];
[bFrac setNumber:#"2/3"];
NSMutableArray * myArray = [[NSMutableArray alloc] init];
[myArray addObject:aFrac];
[myArray addObject:bFrac];
int i;
for(i = 0; i<2; ++i)
{
id myArrayElement = [myArray objectAtIndex:i];
NSLog(#"%#", myArrayElement);
}
for(i = 0; i<2; ++i)
{
NSLog(#"%#", myArray[i]);
}
Both for loops print the same thing.
When you pass a custom object to NSLog you have to override the -(NSString)description method in that object.
So in your Fraction class if you simply override this function like so
- (NSString*)description
{
return self.myNumber;
}
that should log out what you want.
I would probably think about renaming that property from number as you are storing a string.
Hope that helps
I'm guessing the Fraction type you created has a NSString property or method named number (to match the -setNumber: method), in which case you would use the following code to print it:
NSLog("%#", [myArrayElement number]);
Or, for the second loop:
NSLog("%#", [myArray[i] number]);
In your code both for loop meaning has same only, try below
for(i = 0; i<2; ++i)
{
id myArrayElement = [myArray objectAtIndex:i];
NSLog(#"%#", myArrayElement.number);
}
for(i = 0; i<2; ++i)
{
NSLog(#"%#", myArray[i].number);
}
Now here two array value you are extracting
[myArray objectAtIndex:i] which is equivalent to myArray[i]

After 15000 iterations, getting "error: can't allocate region"-- no memory warning

Points: Using ARC;
full error is:
malloc: * mmap(size=2097152) failed (error code=12)
error: can't allocate region
** set a breakpoint in malloc_error_break to debug
four times.
The only code of any substance to the question is:
-(void)iterate:(NSString *)string{
frontString = NULL;
backString = NULL;
arrayOfNumbers = NULL;
backwardArrayOfNumbers = NULL;
nextString = NULL;
nextArrayOfNumbers = NULL;
nextArrayOfNumbers = [NSMutableArray new];
nextString = [NSMutableString new];
backwardArrayOfNumbers = [NSMutableArray new];
arrayOfNumbers = [NSMutableArray new];
frontString = [[NSMutableString alloc] initWithString:string];
backString = [NSMutableString new];
if (string.length > 1) {
iteration++;
for (unsigned long i = 0; i < string.length; ++i) {
NSString *sub = [string substringWithRange:(NSRange){i, 1}];
[arrayOfNumbers addObject:sub];
NSString *back = [string substringWithRange:(NSRange){string.length-(i+1), 1}];
[backwardArrayOfNumbers addObject:back];
[backString appendString:back];
sub = NULL;
back = NULL;
}
if ([frontString isEqualToString:backString]) {
[palindromicNumberTextView setText:string];
[iterationLabel setText:[NSString stringWithFormat:#"%ld", iteration]];
} else {
int carrier = 0;
for (long long j = arrayOfNumbers.count-1; j > -1; --j) {
int a = [[arrayOfNumbers objectAtIndex:j] intValue];
int b = [[backwardArrayOfNumbers objectAtIndex:j] intValue];
//NSLog(#"a = %i, b = %i", a, b);
int c = a+b+carrier;
if (c > 9) {
c = c-10;
carrier = 1;
} else {
carrier = 0;
}
[nextArrayOfNumbers addObject:[NSString stringWithFormat:#"%i", c]];
if (carrier == 1 && (nextArrayOfNumbers.count == arrayOfNumbers.count)) {
[nextArrayOfNumbers addObject:[NSString stringWithFormat:#"%i", carrier]];
}
//NSLog(#"nextArrayOfNumbers = %#", nextArrayOfNumbers);
}
for (int i = 0; i < nextArrayOfNumbers.count; ++i) {
NSString *back = [nextArrayOfNumbers objectAtIndex: nextArrayOfNumbers.count-(i+1)];
//NSLog(#"back = %#", back);
[nextString appendString:back];
back = NULL;
}
if (iteration%1000 == 0) {
NSLog(#"iteration %ld; count:%u", iteration, nextArrayOfNumbers.count);
}
//NSLog(#"iteration %ld", iteration);
[self iterate:nextString];
}
}
}
15,000+ iterations later, crash + error, with no memory warning:
- (void)didReceiveMemoryWarning
{
NSLog(#"Error near iteration %ld", iteration);
[super didReceiveMemoryWarning];
}
Any ideas what the problem might be? Thanks in advance!
EDIT: Converting between CString and NSString is a pain with ARC. So I went with #autoreleasepool{} inside the loops (and nil rather than NULL) and that has dramatically reduced memory usage. I'm up over 50k iterations.
So the program itself has some issues if it's using that much memory -- you can reduce memory usage of this algo significantly.
Anyways, you won't get a memory warning if this takes place on the main thread. The memory warning is made on the main thread, and you give it no chance to handle the warning because you exhaust memory before the main thread's run loop has another chance to do its usual work (such as handle memory warnings or handle other events).
Perhaps you would see something different if this happened on a secondary thread, but that would not solve the root of the problem.
You are solving a problem recursively which fills up memory.
Your computer has to keep track of where to go when it finishes each of 15,000 cycles. That can become a lot of space.
If it is possible to make this "tail recursive" you could save space. For instance:
"bad" recursion which saves in memory 7 + 6 + 5 + 4 + 3 + 2
int factorial (int x)
{
if (x > 1)
{
return (x + recursion (x - 1));
}
else return x;
}
//
good recursion start with y = 1
only saves in memory two integers, updating them every cycle.
int factorial (int x, int y)
{
if (x > 1)
{
return factorial (x - 1, (x * y));
}
else return x;
}

Objective C Loop

I am trying to loop this code until the yser types in DONE. I used the while loop but only parts of the embedded while loop is executed. Why is the first prompt(Enter the name) not executing in the following program? Thanks
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int number;
int i = 1;
double payRate, hours, totalPay;
NSString *name;
NSString *amount;
char inputBuffer[200];
NSNumberFormatter *price = [[NSNumberFormatter alloc] init];
[price setNumberStyle:NSNumberFormatterCurrencyStyle];
[price setCurrencySymbol:#"$"];
while(i > 0){
NSLog (#"Enter the name:");
scanf("%[^\n]", inputBuffer);
name = [[NSString alloc] initWithUTF8String:inputBuffer];
if([name isEqualToString:#"DONE"])
break;
else{
NSLog (#"Enter the total number of hours: ");
scanf ("%lf", &hours);
NSLog (#"Enter the pay rate: ");
scanf ("%lf", &payRate);
if(hours <= 40)
totalPay = hours * payRate;
else
totalPay = 400 + (payRate * (hours - 40) * 1.5);
NSString *myString = [NSString stringWithFormat:#"%f",totalPay];
NSNumber *myNumber = [NSNumber numberWithDouble:[myString doubleValue]];
amount = [price stringFromNumber:myNumber];
NSLog(#"Name: %#", name);
NSLog(#"Hours:%.2lf", hours);
NSLog(#"Pay Rate:%.2lf",payRate);
NSLog(#"Total Pay:%#", amount);
NSLog(#"\n");
}
}
NSLog (#"DONE!");
}
Your problem can be contributed to a number of buffering issues, such as scanf()ing numbers, but not the following line feeds. On top of this, I sometimes find that NSLog() messes of stdin/stdout buffering if you are also using printf() or scanf(). Usually I find it best to avoid the scanf() function whenever possible, especially when using a higher-level language like Objective-C.
Instead of using scanf() to read user input, I wrote a small function to read a line from the console, and return it as an NSString. This function looks as follows:
NSString * readLine (FILE * input) {
NSMutableString * string = [[NSMutableString alloc] init];
int aChar = 0;
while ((aChar = fgetc(input)) != EOF) {
if (aChar != '\r') {
if (aChar == '\n') {
break;
} else if (aChar > 0) {
[string appendFormat:#"%C", aChar];
}
}
}
return [string autorelease];
}
Using this, you could rewrite your main() function using this new method. In the following code I have also taken out all NSLog() statements that prompt the user for information, replacing them with more appropriate printf() calls.
int main (int argc, char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
double payRate, hours, totalPay;
NSString * name;
NSString * amount;
NSNumberFormatter * price = [[NSNumberFormatter alloc] init];
[price setNumberStyle:NSNumberFormatterCurrencyStyle];
[price setCurrencySymbol:#"$"];
while (YES) {
printf("Please, enter your name: ");
name = readLine(stdin);
if ([name isEqualToString:#"DONE"])
break;
else {
printf("Enter the total number of hours: ");
hours = [readLine(stdin) intValue];
printf("Enter the pay rate: ");
payRate = [readLine(stdin) intValue];
if (hours <= 40)
totalPay = hours * payRate;
else
totalPay = 400 + (payRate * (hours - 40) * 1.5);
NSNumber * myNumber = [NSNumber numberWithDouble:totalPay];
amount = [price stringFromNumber:myNumber];
NSLog(#"Name: %#", name);
NSLog(#"Hours: %.2lf", hours);
NSLog(#"Pay Rate: %.2lf",payRate);
NSLog(#"Total Pay: %#", amount);
}
}
[price release];
NSLog(#"DONE!");
[pool drain];
}
In: scanf("%[^\n]", inputBuffer); that does not read anything from the user, you need to read from the user into inputBuffer.
Also NSLog is not a good way to send text to the user, probably use "C" functions since you are writing a "C" program.