What is the default capacity of NSMutableDictionary? - ios

Basically when you call
[[NSMutableDictionary alloc] init];
Apple will create a dictionary of some default size. What is that default size?

Interestingly, on iOS at least it appears that Apple does the same thing for init as if it were initWithCapacity:0. I ran the following code under Instruments:
int max=1000000;
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:max];
for(int i=0; i < max; i++) {
[array addObject:[[NSMutableDictionary alloc] init]];
}
if(true) return array; // Don't let the compiler remove the ref
Next I did something very similar but with 0 capacity explicitly specified:
int max=1000000;
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:max];
for(int i=0; i < max; i++) {
[array addObject:[[NSMutableDictionary alloc] initWithCapacity:0]];
}
if(true) return array; // Don't let the compiler remove the ref
Both of these ran with a max consumption of 55.3 MB on my iOS 9 device. Then I tried using initWithCapacity:1 when creating the dictionaries:
int max=1000000;
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:max];
for(int i=0; i < max; i++) {
[array addObject:[[NSMutableDictionary alloc] initWithCapacity:1]];
}
if(true) return array; // Don't let the compiler remove the ref
In that case my max consumption was 116.4 MB.
As other commenters have noted, this may vary from OS to OS and even from version to version. Don't rely on it, but that's one way to tell what NSMutableDictionary init is doing.

Related

#autoreleasepool block in loop dose not reduce memory peak

I was told that #autoreleasepool block in loop can reduce the peak of memory usage until I do a test. The test device is iPhone 6s with iOS 11.4.1.
my code:
#implementation BigMemObj{
NSMutableArray *_mutArr;
}
-(instancetype)init{
if(self = [super init]){
_mutArr = [[NSMutableArray alloc] initWithCapacity:1024*1024*30];
for(int i = 0; i < 1024*1024*30; i++){
[_mutArr addObject:#(i)];
}
}
return self;
}
- (void)viewDidLoad {
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
#autoreleasepool {
BigMemObj *mem = [[BigMemObj alloc] init];
}
}
}
- (void)viewDidLoad {
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
BigMemObj *mem = [[BigMemObj alloc] init];
}
}
I run both test 34 seconds, in test 1, the highest memory usage is 458M, but in the test 2 the highest memory usage is 362M. and both test have a triangle shape.
with #autoreleaspool block
without #autoreleaspool block
Did the autoreleasepool implementation changed? or the compiler dose some optimization?
Thank You!
It all looks normal actually. The growth you are seeing is this part:
_mutArr = [[NSMutableArray alloc] initWithCapacity:1024*1024*30];
for(int i = 0; i < 1024*1024*30; i++){
[_mutArr addObject:#(i)];
}
So here you are adding your numbers to an array _mutArr and you are adding 1024*1024*30 of them. When this loop finishes the _mutArr is valid and full, it retains all of those numbers. This would not even be changed by adding another autorelease pool within this loop because your array will not let those numbers be released.
Now after this constructor is being called you have
#autoreleasepool {
BigMemObj *mem = [[BigMemObj alloc] init];
}
so autorelease pool will be drained in this moment releasing all the numbers inside BigMemObj instance mem and your memory is back to normal.
You might expect that your application should keep growing in memory without the call to #autoreleasepool. But there is no change at all if you remove that call. The reason for that is that none of your code uses autorelease pool at all. What your code translates to (non-ARC) is:
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
#autoreleasepool {
BigMemObj *mem = [[BigMemObj alloc] init];
[mem release];
}
}
[arr release];
But you would need your autoreleasepool only if it was
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
#autoreleasepool {
BigMemObj *mem = [[[BigMemObj alloc] init] autorelease];
}
}
[arr release];
To have a situation where autorelease pool is needed:
NSMutableArray *allBigValues = [[NSMutableArray alloc] init];
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"profile.png"];
for(int i = 0; i<100000; i++){
#autoreleasepool {
[allBigValues addObject:[UIImage imageWithContentsOfFile:path]];
[allBigValues removeAllObjects];
}
}
If in this code you remove the autorelease pool it will grow in memory until the loop ends. The reason for this is because imageWithContentsOfFile is using autorelease pool and all the images produced by this method will only be released after the pool is drained. Since the main pool will not be released inside the loop we need to create another one.
Bottom line this code works nicely but as soon as you remove the #autoreleasepool part it will start growing in memory and probably crash you application.
Note 1: You need to add image profile.png into your app for this code to work (just drag it among source files, not the assets).
Note 2: We use "drain" where it comes to pools because this used to be the name of the method you needed to call when you wanted for the pool to remove it's objects. This is how it used to be:
for(int i = 0; i<100000; i++){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[allBigValues addObject:[UIImage imageWithContentsOfFile:path]];
[allBigValues removeAllObjects];
[pool drain];
}

NSMutablearray doesn't prints all array elements

I'm using the following piece of code to store elements in array dynamically and retrieve it later
for (int i = 0; i< [companyNames count]; i++){
testimonialsArray = [[NSMutableArray alloc]init];
testimonialsComplementedArray = [[NSMutableArray alloc]init];
[testimonialsArray addObject:companyNames[i] ];
[testimonialsComplementedArray addObject:texts[i]];
NSLog(#"Compliments %#",testimonialsComplementedArray);
}
But it prints only the last copy that had been added to the array. How to retrieve all elements?
Following are the company names,
"General Marketing Company",
"United Enterprises, Chennai",
"Hari Match Industries"
allocate the testimonialsArray = [[NSMutableArray alloc]init]; in above the for loop if you initilize in inside the loop every time the memory will be initilize, so final value only you get on output in finally , do like
testimonialsArray = [[NSMutableArray alloc]init];
testimonialsComplementedArray = [[NSMutableArray alloc]init]
for (int i = 0; i< [companyNames count]; i++){
[testimonialsArray addObject:companyNames[i] ];
[testimonialsComplementedArray addObject:texts[i]];
}
NSLog(#"Compliments %#",testimonialsComplementedArray);
testimonialsArray = [[NSMutableArray alloc]init]; // have to init out side the loop
testimonialsComplementedArray = [[NSMutableArray alloc]init]; // have to init out side the loop
for (int i = 0; i< [companyNames count]; i++){
[testimonialsArray addObject:companyNames[i] ];
[testimonialsComplementedArray addObject:texts[i]];
}
NSLog(#"Compliments %#",testimonialsComplementedArray);
You are allocating mutable array every time in for loop because of that its initializes array and add object in that everytime.Always its better to alloc any object in ViewDidLoad method.
Try like this.
testimonialsArray = [[NSMutableArray alloc]init];
testimonialsComplementedArray = [[NSMutableArray alloc]init];
for (int i = 0; i< [companyNames count]; i++){
[testimonialsArray addObject:companyNames[i] ];
[testimonialsComplementedArray addObject:texts[i]];
}
NSLog(#"Compliments %#",testimonialsComplementedArray);

How to save two Arrays in two dimensional Array?

I am new at iOS Dev. I want to save two different arrays (array1 & array2) in 2 dimensional array. I know how to save data directly in two dimensional array but can't by save two different arrays in one.
NSString* path = [[NSBundle mainBundle] pathForResource:#"Aasvogel" ofType:#"txt"];
NSString* content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
NSArray* foo = [content componentsSeparatedByString: #","];
NSMutableArray *array1 = #[], *array2 = #[];
for ( int i = 0; i < [foo count]; i++ )
{
NSString* day = foo[i];
if ( i % 2 == 0 ) { [array1 addObject:day];}
else { [array2 addObject:day];}
}
// and here i have populated two arrays (array1 and array2)
// Now i want to save these arraya in below two dimensional array (dataArray) atIndex:0 and at Index:1
NSMutableArray *dataArray = [[NSMutableArray alloc] initWithCapacity: 2];
[dataArray addObject:[NSMutableArray arrayWithObjects:#"e",
#"el",
#"ale",
#"vela",
#"gavel",nil] atIndex:0];
[dataArray addObject:[NSMutableArray arrayWithObjects:#"Represents 50 in Roman numeral",
#"Building Wing",
#"Pub Brew",
#"Thin Parchment or membranes",
#"chairperson's hammer",nil] atIndex:1];
I have recently implemented 2D array into my application. Please check below code which is available at 2DArray
int capacity;
#property (nonatomic, strong) NSMutableArray *outerArray;
#define kCRL2DArrayEmptyKey #"kCRL2DArrayEmptyKey"
- (id) initWithRows:(int)x columns:(int)y
{
if (self = [super init])
{
capacity = y;
self.outerArray = [NSMutableArray array];
for (int i = 0; i < x; i++) {
NSMutableArray *innerArray = [NSMutableArray array];
for (int j = 0; j < y; j++) {
[innerArray addObject:kCRL2DArrayEmptyKey];
}
[self.outerArray addObject:innerArray];
}
}
return self;
}
you can try this
NSArray * firstArray, *secondArray;
NSArray * mainArray= [[NSArray alloc]initWithObjects: firstArray, secondArray, nil];
I am not sure about 2-dimensional array in iOS but if I were you I would be saved the two arrays within a dictionary such as
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setvalue:yourArray forKey:#"FirstArray"];
[dict setvalue:yourSecondArray forKey:#"SecondArray"];
And Use it accordingly.
There’s no such thing as a two (or more) dimensional NSArray. If you genuinely need an n-dimensional array object in iOS or OS X, you can of course roll your own, or you could instead create an NSArray of NSArray instances (which are columns and which are rows is entirely up to you). In that case, you could e.g. add items by doing
[[outerArray objectAtIndex:0] addObject:#"Foo"];
[[outerArray objectAtIndex:1] addObject:#"Bar"];
That said, for the problem you are tackling, it looks to me as if an NSDictionary might be more appropriate, e.g. with keys #"e", #"el" and values #"Represents 50 in Roman numerals", #"Building Wing".
If your concern is that the keys of NSDictionary are not held in sorted order, you can always extract the keys as an array and sort them. Or, if the keys change regularly, you might want to use a more sophisticated approach (e.g. keeping a separate sorted array of keys and inserting them into the right place when adding to the NSDictionary).
Also, you know that in modern Objective-C you can write e.g.
#[ #"a", #"b", #"c" ]
or
#{ #"a": #1, #"b": 2 }
rather than the very verbose forms you're using above?
this is how u add anything in a 2d array i.e an Array of arrays in objective-c
NSMutableArray *array 1 = [[NSMutableArray alloc]init];
NSMutableArray *array 2;
for(int col = 0;col <5;col++){
array2 = [[NSMutableArray alloc]init];
for(int row = 0;row<5;row++){
[array2 addObject:myItems];
}
[array1 addObject:array2];
}
hope this helps
use for loop to generate 2d array from 2 different array,
follow this stracture
int i, j;
for(i = 0; i < nrows; i++)
{
for(j = 0; j < ncolumns; j++)
array[i][j] = 0;
}
}
May be it will help you

NSMutableArray with int values from 1 to 100

This should be dead easy, but somehow it doesn't want to work for me. Using iOS 7 and XCode 5.
All I'm trying to do is create an array with values from 1 to 100.
NSMutableArray *array;
for (int i = 0; i < 100; i++)
{
[array addObject:i];
}
This doesn't work. I get a "Implicit conversion of 'int' to 'id' is disallowed with ARC.
I get it, I can't add primitive types to an NSMutableArray.
[array addObject:#i];
This doesn't work either. I get a "unexpected '#' in program"
[array addObject:[NSNumber numberWithInt:i]];
[array addObject:[NSNumber numberWithInteger:i]];
(either case) This "works" (compiles) but it really doesn't "work". The problem with this is that the value from NSNumber is really not a 1-100. What I get for each row is "147212864", 147212832", "147212840"...not what I want.
Lastly:
for (NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++)
{
[array addObject:i];
}
This also doesn't compile. I get an error on the i++. "Arithmetic on pointer to interface 'NSNumber', which is not a constant size for this architecture and platform"
Any suggestions on how to do this extremely simple thing on obj-c?
Either one of these should work:
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
[array addObject:#(i)];
}
or
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
[array addObject:[NSNumber numberWithInt:i]];
}
Here are the reasons why your code snippets did not work:
[array addObject:i] - You cannot add primitives to Cocoa collections
[array addObject:#i] - You forgot to enclose the expression i in parentheses
NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++ - You cannot increment NSNumber without "unwrapping" its value first.
If memory serves, I think you're simply missing parenthesis around the NSNumber shorthand expression.
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < 100; i++)
{
[array addObject:#(i)];
}
Minimally, #i should be #(i) as described here. You are also forgetting to allocate and initialise your array
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 100; i++) {
[array addObject:#(i)];
}
And since you are getting: "147212864", 147212832", "147212840"...not what I want., I think you are probably printing out your information wrongly or because the array is unallocated, that's simply garbage. Can you show us how you are outputting?
NSMutableArray *array = [[NSMutableArray alloc] init];
NSNumber *myNum;
for (int i = 0; i < 100; i++) {
myNum = [[NSNumber alloc]initWithInt:i];
[array addObject:myNum];
}
NSLog(#"%#", array); // 1 - 99 as expected
Worked for me :)
Just saying: Turn on all reasonable warnings in your Xcode project. Then read what the warnings are saying and do something about them. When you write something like
for (NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++)
What does a for loop do? An object is in the end a pointer. So you initalise i to nil. Then you compare a pointer with a random pointer: [NSNumber numberWithInteger:100] returns a pointer to an object which could be anywhere in memory, and you compare pointers. Next the i++: No, you can't increment a pointer to an NSNumber. It doesn't make sense.

sqlite3 NSDictionary memory leak

I have my sqlite execute query code as below. Instruments catches a memory leak in the NSDictionary alloc and release lines (inside the while loop). Can someone point out what is wrong in those alloc/release ?
- (NSMutableArray *)executeQuery:(NSString *)sql arguments:(NSArray *)args {
sqlite3_stmt *sqlStmt;
if (![self prepareSql:sql inStatament:(&sqlStmt)])
return nil;
int i = 0;
int queryParamCount = sqlite3_bind_parameter_count(sqlStmt);
while (i++ < queryParamCount)
[self bindObject:[args objectAtIndex:(i - 1)] toColumn:i inStatament:sqlStmt];
NSMutableArray *arrayList = [[NSMutableArray alloc]init]; // instrument marks leak 0.1 % on this line
NSUInteger rcnt= arrayList.retainCount;
int columnCount = sqlite3_column_count(sqlStmt);
while ([self hasData:sqlStmt]) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; // instrument marks leak 13% on this line
for (i = 0; i < columnCount; ++i) {
id columnName = [self columnName:sqlStmt columnIndex:i];
id columnData = [self columnData:sqlStmt columnIndex:i];
[dictionary setObject:columnData forKey:columnName];
}
[arrayList addObject:dictionary];
[dictionary release];// instrument marks leak 86.9 % on this line
}
sqlite3_finalize(sqlStmt);
rcnt=arrayList.retainCount;
return arrayList ;
}
Any help/pointer is very much appreciated. Have been struggling with this for few days.
If you are not using ARC then I suggest you change your return line for:
return [arrayList autorelease];
Otherwise you are probably leaking the complete arrayList.

Resources