How for in loop works internally - Objective C - Foundation - ios

I found this answer:
https://stackoverflow.com/a/5163334/1364174
Which presents how for in loop is implemented.
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
[obj doSomething];
}
}
The problem is that, I found it wrong.
First of all, when you have Automatic Reference Counting (ARC) turned on, you got an error
Sending '__strong id *' to parameter of type '__unsafe_unretained_id*' changes retain/release properties of pointer
But even when I turn ARC off I found out that I __object array seems to behave strangely :
This is actual Code (I assumed MAX_STACKBUFF_SIZE to be 40):
#autoreleasepool {
NSArray *myArray = #[#"a", #"b", #"c", #"d", #"e", #"f", #"g"];
int MAX_STACKBUFF_SIZE = 40;
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
__enumState.itemsPtr
NSLog(#" Object from __objects ! %#", obj); // on screenshot different message
}
}
}
return 0;
I got EXC_BAD_ACESS when I try to get the contents of the __object array.
I also found out that when you try to iterate through __enumState.itemsPtr it actually works.
Could you explain me what is going on here ? Why my __objects seems to be "shrunken down". And why it doesn't contains desired object? And why is that error when ARC is turned on.
Thank you very very much in advance for your time and effort! (I provided screenshot for better understanding what causes an error)

First of all, strong pointers cannot be used in C-structures, as explained in the "Transitioning to ARC Release Notes", therefore the objects array has be be declared
as
__unsafe_unretained id __objects[MAX_STACKBUFF_SIZE];
if you compile with ARC.
Now it is not obvious (to me) from the NSFastEnumeration documentation, but it is
explained in Cocoa With Love:Implementing countByEnumeratingWithState:objects:count:
that the implementation need not fill the supplied objects array, but can just set
__enumState.itemsPtr to an existing array (e.g. some internal storage). In that case, the contents of the
__objects array is undefined, which causes the crash.
Replacing
id obj = __objects[i];
by
id obj = __enumState.itemsPtr[i];
gives the expected result, which is what you observed.
Another reference can be found in the "FastEnumerationSample" sample code:
You have two choices when implementing this method:
1) Use the stack
based array provided by stackbuf. If you do this, then you must
respect the value of 'len'.
2) Return your own array of objects. If
you do this, return the full length of the array returned until you
run out of objects, then return 0. For example, a linked-array
implementation may return each array in order until you iterate
through all arrays.
In either case, state->itemsPtr MUST be a valid
array (non-nil). ...

Related

Xcode 13 - Count not working on NSMutableArray anymore

I have something like this in my code which worked fine for many years
NSMutableArray* DeviceList = [NSMutableArray alloc] init]; // Multi-dimensional (n x m) mutable Array filled throughout the code before arriving here:
if ([[DeviceList objectAtIndex:i] count] == 9))
{
[DeviceList removeObjectAtIndex:i];
}
I haven't touched the project for a while and now upgraded to Xcode 13 and get the following error:
Multiple methods named 'count' found with mismatched result, parameter type or attributes
The error list on the left pane shows 12 options where count has been declared.
So what has changed that the simple
someNSUInteger = [[someArray] count];
creates an error and what would be a good way to fix it in 100+ instances without casting every instance with (NSMutableArray*)?
Thanks a lot!
objectAtIndex returns "id" (objects of any type). Before it used to be permissive, and you were allowed to call any method on such (such as "count").
A better way to write this code would be to declare an intermediate variable:
NSArray *deviceRow = [DeviceList objectAtIndex:i];
if ([deviceRow count] == 9) {
or if you know the type if the items inside the array, let's say you have strings:
NSArray<NSString *> *deviceRow = [DeviceList objectAtIndex:i];
if ([deviceRow count] == 9) {
and even better:
NSArray<NSString *> *deviceRow = DeviceList[i];
if (deviceRow.count == 9) {
Another problem is that there's too many instances of that, and you'd rather not touch it everywhere.
One way to fix most of the 100 occurences is to define a wrapper class for DeviceList instead of using a "naked" NSMutableArray:
#interface MyDeviceList
- (nonnull instancetype)init;
- (nullable NSArray *)objectAtIndex:(NSUInteger)index;
... any other NSMutableArray methods used in the code ...
#end
and replace the array instantiation with:
MyDeviceList *DeviceList = [MyDeviceList new];
Hopefully you have much less instantiations than unsafe usages.
You could also have some helper methods in MyDeviceList like "countAtIndex" if it happens that you need it a lot.

Getting an error while adding a integer to array object at index (objective c)

while adding a integer to object at index of an array, I am getting an error of "arithmetic on pointer to interface id which is not a constant size for this architecture and platform", didn't know how to resolve it.
Please help.
my code is -
if (arrayTotalAmount.count>0) {
int sum = 0;
for (int i = 0; i<=arrayTotalAmount.count; i++) {
sum = (sum+[arrayTotalAmount objectAtIndex:i]);
}
In 4th line I am getting that error.
Thanks
Objective C array only accepts NSObject type. That means it is impossible to insert a primitive value into an NSArray. You are getting an error because objectAtIndex method returns a pointer which points to that NSObject, the arithmetic operations are still valid on pointers but the thing is that the size of a pointer as integer (32bit, 64bit) may differ on device. So one of the solution is typecasting the pointer sum+(int)[arrayTotalAmount objectAtIndex:i] which makes no sense in your case.
The solution you are looking for is probably sum+[[arrayTotalAmount objectAtIndex:i] intValue] or similar. Assuming that array contains NSNumber objects. If the object inside an array is not an NSNumber then your app will fail in runtime showing an error indicating that an object X does not have a method called intValue in which case you will need to figure how to convert object X to your int.
You just need to convert your array object to integer and then add it will work for you.
if (arrayTotalAmount.count>0) {
int sum = 0;
for (int i = 0; i<=arrayTotalAmount.count; i++) {
sum = (sum+[[arrayTotalAmount objectAtIndex:i] intValue]);
}

iOS: Remove NSArray in NSMutableArray in For Loop

This is a pretty simple concept, but I'm not getting the results I'm wanting. I have an NSMutableArray that is populated with NSArrays, I want to loop through that NSMutableArray and remove certain NSArrays based on a key-value pair. My results have many of the NSArrays that I should be removing and I think it has something to do with the count of the NSMutableArray and the int I declare in the For Loop.
Here is my code: (restArray is the NSMutableArray)
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if ([[array valueForKey:#"restaurant_status"] isEqualToString:#"0"]) {
[restArray removeObjectAtIndex:i];
}
}
Does someone know what I am doing wrong here?
It is not recommended to modify an array on what are you currently iterating.
Lets create a tmp array, and reverse your logic.
NSMutableArray * tmpArray = [NSMutableArray array];
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if (![[array valueForKey:#"restaurant_status"] isEqualToString:#"0"] {
[tmpArray addObject:array];
}
}
So at the end of the iteration, you should end up with tmpArray having the arrays you needed.
Use NSPredicate:
NSArray *testArray = #[#{#"restaurant_status" : #"1"}, #{#"restaurant_status" : #"0"}];
NSArray *result = [testArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(restaurant_status == %#)", #"1"]];
When you remove an element all the elements past it shift down by one, e.g. If you remove the element at index 3 then the element previously at index 4 moves to index 3.
Every iteration you increase the index by one.
Combine the above two and you see that when you remove an element your code skips examining the following element.
The simple solution is to reverse the order of the iteration:
for (int i = restArray.count - 1; i >= 0; i--)
and then your algorithm will work.
Addendum
You can safely ignore this addendum if your arrays contain < 2^32 elements and you use Clang or GCC (and most other C compilers).
It has been raised in the comments that this answer has a problem if the array has 0 elements in it. Well yes & no...
First note that the code in the question is technically incorrect: count returns an NSUInteger which on a 64-bit machine is a 64-bit unsigned integer, the loop variable i is declared as an int which is 32-bit signed. If the array has more than 2^31-1 elements in it then the loop is incorrect.
Most people don't worry too much about this for some reason ;-) But let's fix it:
for (NSInteger i = restArray.count - 1; i >= 0; i--)
Back to the problem with an empty array: in this case count returns unsigned 0, C standard arithmetic conversions convert the literal 1 to unsigned, the subtraction is done using modular arithmetic, and the result is unsigned 2^64-1.
Now that unsigned value is stored into the signed i. In C converting from signed to unsigned of the same type is defined to be a simple bit-copy. However converting from unsigned to signed is only defined if the value is within range, and implementation defined otherwise.
Now 2^64-1 is greater than the maximum signed integer, 2^32-1, so the result is implementation defined. In practice most compilers, including Clang and GCC, choose to use bit-copy, and the result is signed -1. With this the above code works fine, both the NSInteger and the int (if you've less than 2^32-1 elements in your array) versions.
What the comments raise is how to avoid this implementation-defined behaviour. If this concerns you the following will handle the empty array case correctly with ease:
for (NSUInteger i = restArray.count; i > 0; )
{
i--; // decrement the index
// loop body as before
}
If the array is empty the loop test, i > 0, will fail immediately. If the array is non-empty i, being initialised to the count, will start as one greater than the maximum index and the decrement in the loop will adjust it - effectively in the loop test i contains the number of elements left to process and in the loop body after the decrement contains the index of the next element to process.
Isn't C fun (and mathematically incorrect by definition)!

How to avoid NSMutable array add object values changes before adding object to NSMutable array?

I am new to Objective C. I had used following code.
for (int i = 0; i < [ValueisFound count]; i++)
{
NSString* ObjectName = [NSString stringWithUTF8String:name];
ObjectName = [[NSClassFromString([NSString stringWithUTF8String:samType]) alloc]init];
NSMutableDictionary* jsonDictionary=[TemplateClass SeparateArray:jsonValue_1 key:[arrayofKeys objectAtIndex:j] index:i];
ObjectName = [TemplateClass JsonPharser:str1 jsonObject:jsonDictionary];
NSMutableArray *samArray=[[NSMutableArray alloc]initWithObjects:[TemplateClass JsonPharser:str1 jsonObject:jsonDictionary], nil];
[manoj_Array addObject:samArray];
[samArray release];
[ObjectName release];
}
While executing for loop:
at i=0, Object has value(number=10), now manoj_Array also has (number=10).
at i=1, Object has value(number=12), now manoj_Array has (number=12,number=12).
But i want the result as manoj_Array has (number=10,number=12). I don't know how that array values are changing to last value.
My mind
samArray and manoj_Array is shared the memory Reference Thats why last value is insert to the manoj_Array
i = 0 value is Store by the address of 1000
i = 1 value is Store by the address of 1002
but manoj_Array share the memory so change the Last value is Added to manoj_Array
You can implement KVO in order to know when a value has changed and handle its behaviour.

NSMutableArray or NSMutableDictionary : which is best for this scenario?

I need to scroll through several thousands of words to categorize them... to determine which words have the same pattern. (this part works)
For example, a four letter word that has two m's in 2nd & 4th position represent a pattern ("-m-m"). Once I have gone through all the words, I will know how many words there are for any given pattern. I am scrolling through now, but the problem I have is 'remembering' how many words I have in any given pattern.
I was thinking of using NSMutableDictionary and have the key be the pattern ('-m-m-') and the object represent the count of that pattern. This means every time I come across a pattern, I look up that pattern in the dictionary, get the key, increment the key, and put it back in the dictionary.
I need help with both the decision and syntax for performing this task.
Thank You
The answer to your question was this part of your (given) question "I will know how many words there are for any given pattern.". I would use an array of dictionary. You use the dictionary to store key value pair: a known pattern and the count. And you use the array to store those KVP records. So the next time you detect a pattern, search for the array for that record (dictionary), if found, increment the count. If not, create new record and set the count to 1.
Added sample code:
#define kPattern #"Pattern"
#define kPatternCount #"PatternCount"
-(NSMutableDictionary *)createANewDictionaryRecord:(NSString *) newPattern
{
int count = 1;
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
newPattern, kPattern,
[NSString stringWithFormat:#"%i",count], kPatternCount,
nil];
return myDictionary;
}
-(void)addANewPatternToArray:(NSMutableDictionary *)newDictionary
{
// NSMutableArray *myArrayOfDictionary = [[NSMutableArray alloc]init]; // you need to define it somewhere else and use property etc.
[self.myArrayOfDictionary addObject:newDictionary]; //or [self.myArrayOfDictionary addObject:newDictionary]; if you follow the recommendation above.
}
-(BOOL)existingPatternLookup:(NSString *)pattern
{
for (NSMutableDictionary *obj in self.myArrayOfDictionary)
{
if ([[obj objectForKey:kPattern] isEqual:pattern])
{
int count = [[obj objectForKey:kPatternCount] intValue] + 1;
[obj setValue:[NSString stringWithFormat:#"%i",count] forKey:kPatternCount];
return YES;
}
}
[self.myArrayOfDictionary addObject:[self createANewDictionaryRecord:pattern]];
return NO;
}
-(void)testData
{
NSMutableDictionary *newDict = [self createANewDictionaryRecord:#"mmm"];
[self addANewPatternToArray:newDict];
}
-(void) printArray
{
for (NSMutableDictionary * obj in self.myArrayOfDictionary)
{
NSLog(#"mydictionary: %#", obj);
}
}
- (IBAction)buttonPressed:(id)sender
{
if ([self existingPatternLookup:#"abc"])
{
[self printArray];
} else
{
[self printArray];
}
}
Not being an objective C expert but solving this problem in java before, I would say a dictionary(I used a map when doing it in java) is the best way. Check if the key(pattern) already exist if so increment that count else put a new one in the dictionary.
EDIT
If you want to not just get the count of a pattern, but in fact tell which words fall under that pattern, I would use a dictionary of strings to mutable arrays. In the arrays you store the words and the key to the array is the pattern(as a string), similar code as above but instead of just incrementing the count, you have to add the new word to the array.
The only difference in NSDictionary and NSMutableDictionary is that one can have objects added to it. I think your implementation is good, but English is a complex language. It would be more efficient to parse out the string with regex than to set a key for it.
Why don't you use NSCountedSet instead:
NSCountedSet Class Reference
..which is available in iOS 2.0 and later?
Each distinct object inserted into an NSCountedSet object has a counter associated with it. NSCountedSetkeeps track of the number of times objects are inserted [...] Thus, there is only one instance of an object in an NSSet object even if the object has been added to the set multiple times. The count method defined by the superclass NSSet has special significance; it returns the number of distinct objects, not the total number of times objects are represented in the set.
Then use:
- (NSUInteger)countForObject:(id)anObject
Use a dictionary of NSMutableArrays, and check for the existence of each search key as you recommended. If the key doesn't exist, add an NSMutableSet or NSMutableArray (depending on your needs) for the searched key type "-m-m" for example, and then add to the set or array for that key.

Resources