NSArray filled from array attempt to insert nil object - ios

I trying fill array from existing filled array but sometimes get this error:
*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[4830]
Exception caused by this code line:
NSArray *result = [NSArray arrayWithArray:self.testerLog];
testerLog is NSMutableArray and I use it for collect logs from App.
tester logs filled next way:
[self.testerLog addObject:[NSString stringWithFormat:#"%#: %# \n", [NSDate date], logRecord]];
How it could happens? No exception when I add object to testerLog and fail when trying fill array from this filled array?
Edit:
About initializing testerLog. Here is code of testerLog method:
- (NSMutableArray *)testerLog {
if (!_testerLog) {
_testerLog = [NSMutableArray array];
}
return _testerLog;
}
So I think it should be not nil.
UPDATE:
I forget to say that method that add NSString to testerLog may called from several threads;

The getter you posted is not thread safe. To get an equivalent thread safe getter, use the following code instead.
-(NSMutableArray *)testerLog {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// If you're not using ARC you most definitely want to retain the array!
// _testerLog = [[NSMutableArray array] retain];
// If you're using ARC you should just assign
_testerLog = [NSMutableArray array];
});
return _testerLog;
}
The dispatch_once call makes sure that whatever code you put into it, it will only be executed once during you app's lifetime (in a thread-safe manner). The static onceToken is what identifies the particular block. In your particular case this is useful because it guarantees that the array is instantiated only once no matter how many threads execute this getter.
NON-ARC ONLY: The retain is because you want the array to survive beyond this method's execution (again, ONLY if you are not using ARC).
Also, if you're not expecting to see a nil value somewhere because it means there was some logic error: use assertions. The following is an example of how to use them:
assert(self.testerLog != nil);
NSArray *result = [NSArray arrayWithArray:self.testerLog];

Make sure you properly initialized your testerLog array. It's nil and that's causing your problem!
addObject may not be throwing an error because you're trying to add a valid NSString to your testerLog array. Try doing an NSLog on self.testerLog immediately after the line where you add the object, and see that it's printing the testerLog array correctly as you would expect.

Related

NSMutableArray replaceObjectAtIndex Crash

i use xcode 9
NSMutableArray *Upcase_Keys = #[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"];
NSString *str = [Upcase_Keys objectAtIndex:0];
str = #"test";
[Upcase_Keys replaceObjectAtIndex:0 withObject:str];
Gets specific data for NSMutableArray
It transforms the value and overwrites the existing index.
However, this code causes a crash.
What did I do wrong?
NSMutableArray replaceObjectAtIndex Crash
So there should be an error in console. Console will help you 99% of the cases when there is a crash, read it!
It should be something like -[NSArrayI replaceObjectAtIndex:withObject:] unrecognized selector sent to instance. That's the important part of your error. NSArrayI meaning NSImmutableArray (NSArray in other words), which is not a NSMutableArray which points out that the issue is about the creation of Upcase_Keys.
Why then? Because #[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"]; that's a short hand syntax for NSArray, not NSMutableArray. So even if it's declared as a NSMutableArray, the object is in fact a NSArray.
In fact, if you'd listen to XCode, it should give you this warning:
Incompatible pointer types initializing 'NSMutableArray *' with an
expression of type 'NSArray *'
Which concords with everything said before.` Sometimes XCode may be wrong, but try to listen to it.
There are a few possibilities call:
NSMutableArray *Upcase_Keys = [NSMutableArray arrayWithArray:#[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"]];
NSMutableArray *Upcase_Keys = [[NSMutableArray alloc] initWithArray:#[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"]];
NSMutableArray *Upcase_Keys = [#[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"] mutableCopy];
And finally, a recommendation use camelcase:
Avoid naming your var with an uppercase. Use a lower case for the first letter. I'd say that after the _ is less problematic, but we tend in iOS to write instead.
Upcase_Keys => upcase_Keys => upcaseKeys
You have defined as immutable object, The syntax will assign NSArray reference not NSMutableArray. You can't update the NSArray.
Try like this,
NSMutableArray *Upcase_Keys = [NSMutableArray arrayWithArray:#[#"1",#"2",#"3",#"4,"#"5",#"6",#"7",#"8",#"9",#"0"]];

mutating method sent to immutable object' in nsmutablearray

I want to remove objects from NSmutableArray can one tell me the Best way to remove from NSMutableArray
.h
#property(nonatomic,retain)NSMutableArray *arr_property;
.m
_arr_property=[[NSMutableArray alloc]init];
MTPop *lplv = [[MTPop alloc] initWithTitle:SelectProperty(APP_SHARE.language)
options:[_arr_property valueForKeyPath:#"property_list.property_type_name"]
handler:^(NSInteger anIndex) {
txt_Property.text=[[_arr_property valueForKeyPath:#"property_list.property_type_name"] objectAtIndex:anIndex];
NSLog(#"index number %ld",(long)anIndex);
remove object--->>>
NSLog(#"index number %#",[_arr_property valueForKey:#"property_list"]);
[[_arr_property valueForKeyPath:#"property_list.property_type_name"] removeObjectAtIndex:anIndex]; ////hear the app is crashing
app is crashing error iam getting is
2015-06-09 13:21:31.104 Estater[2170:62264] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
Think about your code:
_arr_property=[[NSMutableArray alloc]init];
You now have an empty NSMutableArray. It has no elements.
[... removeObjectAtIndex:0];
What did we just say? The array has no elements. It has no element 0 - to have an element 0 it would need to have one element at least, but it doesn't. There is nothing to remove.
[_arr_property valueForKeyPath:#"property_list.property_type_name"]
That part is the weirdest, but let's carry on. When called on an array, valueForKeyPath: results in an NSArray - not an NSMutableArray. So this gives you an empty NSArray. But you cannot say removeObjectAtIndex: to an NSArray, even if it empty - it is not mutable. That's the crash you are experiencing.
The real error is that you are calling removeObject on an element of your NSMutableArray:
[-->[_arr_property valueForKeyPath:#"property_list.property_type_name"]<-- removeObjectAtIndex:0];
The array looks empty, but if filled with something, to remove the first element you should do instead:
[_arr_property removeObjectAtIndex:0];
Firstly, you cannot work with NSMutableArray for key value coding it does not support. You must better use NSMutableDictionary for it.
As dictionaries store objects based on a key, whereas arrays store objects based on an index.
You can use NSMutableDictionary like this:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:something forKey:#"Some Key"];
// ... and later ...
id something = [dict objectForKey:#"Some Key"];
Secondly, valueForKeyPath: returns a value not array and valueForKey: returns array of value for the key and also that array is not mutable.
Edit:
Thirdly, after researching more on valueForKeyPath:, found its use in collection operation and syntax for using is. So, do it by changing
[_arr_property valueForKeyPath:#"property_list.property_type_name"]
To
[_arr_property valueForKeyPath:#"#property_list.property_type_name"]

Enumerating array of objects

I have array of objects and some objects in it have the same value(for example user's guid).
I want find all object with same guide and remove all of then rather then first.
What is the best way to do it?
You can use the NSMUtableArray's removeObject method. Notice that your object should implement the isEqual method appropriately.
[NSMutableArray removeObject]
as per the description:
This method uses indexOfObject: to locate matches and then removes
them by using removeObjectAtIndex:. Thus, matches are determined on
the basis of an object’s response to the isEqual: message. If the
array does not contain anObject, the method has no effect (although it
does incur the overhead of searching the contents).
So, first of all you array need to be mutable NSMutableArray, then the process is:
consider the actual object;
check if is present another object equal to this in the other objects;
if yes, delete the equal objects include the actual.
-
NSMutableArray *arr = [NSMutableArray arrayWithArray:#[#1, #2, #3, #2, #5, #3]];
for(int i=0; i<[arr count]; i++) {
id obj = arr[i];
if([arr indexOfObject:obj inRange:NSMakeRange(i+1, [arr count]-i-1)] != NSNotFound) {
[arr removeObject:obj inRange:NSMakeRange(i, [arr count]-i)];
i--;
}
}

How does this NSMutableArray alloc&init work?

I see a new kind of alloc&init NSMutableArray way in one project. It's like this A
NSMutableArray *array = [#[] mutableCopy]; and this works well, and i want to try whether its possible to use BNSMutableArray *array = [NSMutableArray mutableCopy]; it build succeeded, but got this error when used: +[NSMutableArray addObject:]: unrecognized selector sent to class 0x38bedc2c
Now i want to know how does A work? and why B is wrong? A is better than normal alloc&init?
Any help will be appreciated.
mutableCopy is an instance method declared in NSObject class. It is called on any instance to create a mutable copy of it.
In first case #[] will create an autoreleased NSArray instance on which calling mutableCopy will create NSMutableArray instance.
In second case calling mutableCopy on the class is incorrect because it is not meant to be called that way. This will get compiled but will cause exception at runtime.
Hope that helps!
In the first case, you're first initializing an empty NSArray instance; think of #[] as equivalent to [[NSArray alloc] init]. Therefore you're sending mutableCopy to a correct instance, so it works fine.
In the second case, you're sending the message to a class (as opposed to an instance of it), which doesn't make much sense, because the addObject message can only be sent to an instance, not the class itself.
#[] means an NSArray with no object. It returns an NSArray, and then its mutableCopy is copied to array.
+[NSMutableArray addObject:] is invalid as addObject is an instance method and you are trying to use it as class method.
Even NSMutableArray *array = [NSMutableArray mutableCopy]; is incorrect!!! As nothing is created, it is not been allocated and inited. If you log the array, it will only print the string NSMutableArray. Also you can't use array to addObject and other operations.
You should use NSMutableArray *array = [NSMutableArray array];
The first one is lazy typing.
You should avoid it.
It creates an empty NSArray from the array literal syntax and the creates a mutable copy.
That's saving a little typing by creating an unnecessary array.
You should just use
[NSMutableArray new]
Or
[[NSMutableArray alloc] init]
Or if possible because you know the initial capacity in advance
[[NSMutableArray alloc] initWithCapacity:someNSUIntegerValue]
Anything else above is laziness.
Only use mutableCopy when you are actually copying some content.

NSMutableArray addObject getting set to nil after release of object

I have declared an NSMutableArray *arrAllRecordsM and am trying to add NSMutableDictionary *dicRecordM to it using addObject. The dicRecordM gets added to arrAllRecordsM but on doing [dicRecordM removeAllObjects] it sets to nil in arrAllRecordsM. Below is the code, please help me fix it.
self.arrAllRecordsM = [NSMutableArray array];
self.dicRecordM = [NSMutableDictionary dictionary];
// Some Method
[self.dicRecordM setObject:#"Test" forKey:#"ADDRESS"];
[self.arrAllRecordsM addObject:self.dicRecordM];
// Value: Test
NSLog(#"Value: %#", [[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]);
[self.dicRecordM removeAllObjects];
// Value: null
NSLog(#"Value: %#", [[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]);
Adding an object to an NSMutableArray just stores a pointer (or "strong reference")
to the object into the array. Therefore
[self.arrAllRecordsM objectAtIndex:0]
and
self.dicRecordM
are two pointers to the same object. If your remove all key/value pairs from self.dicRecordM then [self.arrAllRecordsM objectAtIndex:0] still points to the same
(now empty) dictionary. That is the reason why
[[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]
returns nil.
If you want an independent copy of the dictionary in the array, use
[self.arrAllRecordsM addObject:[self.dicRecordM copy]];
copy can be used on many classes, such as NSDictionary, NSArray and NSString
and their mutable variants, to get a "functionally independent object". Formally, it is available for all classes conforming to the NSCopying protocol.
This is expected behavior.
You removed all the objects from the dictionary by calling removeAllObjects, then tried to retrieve an object from it and rightfully getting nil, since it doesn't exist in the dictionay anymore (you removed it).
What's maybe unclear to you is that NSArray doesn't copy the element you add to it, instead it just holds a strong reference.
So both dicRecordM and arrAllRecordsM are holding a reference to the same object, hence any modification to it (in this case removeAllObjects) will affect the same dictionary.
Incidentally, you shouldn't use valueForKey: for accessing the dictionary's entries. Use objectForKey: or the shorter subscripted syntax. For instance
self.arrAllRecordsM[0][#"ADDRESS"]
You can read this answer Difference between objectForKey and valueForKey? as a reference, but the main problem is that valueForKey: can behave very differently from objectForKey: in case the key contains special KVC characters, such as # or ..

Resources