问题
I have an array containing various instances of MyClass
, which has four properties: int id
, int valueA
, int valueB
, and int valueC
. On occasion, I need to modify a property for a specific instance (for this example let's say it is the one with id
equal to 5
).
Currently I do it like this:
MyClass *myClass = [[MyClass alloc] init];
for (int i = 0; i < [myMutableArray count]; i++)
{
myClass = [myMutableArray objectAtIndex:i];
if(myClass.id == 5)
{
myClass.valueA = 100;
myClass.valueB = 200;
myClass.valueC = 300;
[myMutableArray replaceObjectAtIndex:i withObject: myClass];
}
}
Is there a better (I am hesitant to say more efficient) way of doing this?
回答1:
A straightforward, though wordy, way to get a particular member of the array by any criteria at all is to use indexOfObjectPassingTest:
combined with objectAtIndex:
MyClass * theInstanceIWant;
theInstanceIWant = [myArray objectAtIndex:[dates indexOfObjectPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop) {
return ([obj id] == 5);
}];
theInstanceIWant.valueA = 100;
theInstanceIWant.valueB = 200;
theInstanceIWant.valueC = 300;
There's no need to use replaceObjectAtIndex:withObject:
-- the name theInstanceIWant
refers to the actual object in the array, and you can change its properties while it's still in the array.
(This is made more pleasant with a category method on NSArray
.)
- (id)WSSObjectPassingTest:(BOOL (^)(id obj, NSInteger idx, BOOL *stop))test
{
NSUInteger testedIndex = [self indexOfObjectPassingTest:test];
return [self objectAtIndex:testedIndex];
}
回答2:
If you know that there is only 1 instance with an id
of 5, just add a break;
at the end of the if
statement after you have made your updates. This will terminate the loop iteration and is probably the most efficient.
You shouldn't worry too much about runtime performance without profiling.
To write less code, you can write the for
as:
for (MyClass *myClass in myMutableArray) {
You may want to change away from using an array for your storage if you need to access individual items often anyway.
Also, don't do:
MyClass *myClass = [[MyClass alloc] init];
Because you're needlessly creating an instance that you never use and just gets thrown away.
回答3:
Are you sure this is a performance bottleneck before you begin over-optimizing?
One alternative is to keep the instances in an NSMutableDictionary
where the key is an NSNumber
of the id
. Then when you need to update a specific one, you can just retrieve the instance directly with [myMutableDictionary objectForKey:@(someIntId)]
. That would turn your O(n) loop into a O(1) hash table lookup [assuming a good hash
implementation or it will be O(log n) in the worst case].
回答4:
There's two quick ways I see to optimize your code. First, you original assignment of your new object to replace the myClass instance in the array is unnecessary - since it's a pointer, you're actually directly manipulating the object in the array. Secondly, were you to retain your current for loop with an incrementer the original allocation of MyClass is unnecessary. You don't need to do alloc and init on it, because you only would need a pointer. However, you could use a for in loop rather than an incrementing for loop and just loop directly through the array.
Final code:
for (MyClass *myClass in myMutableArray)
{
if(myClass.id == 5)
{
myClass.valueA = 100;
myClass.valueB = 200;
myClass.valueC = 300;
}
}
回答5:
First thing first, you never want to name a varibale id
in Objective - C. id
means pointer to any type, and it is a reserved keyword.
A better / more efficient way of doing this is called fast enumeration. Fast enumeration is a special way to iterate through each item in a list/array/set. The code looks like this:
for (MyClass *iteratorObject in myMutableArray) {
if (iteratorObject.id == 5) {
//do what you want with that specific object
//insert your modified object back into mutable array
}
Although this would be the easiest way to do it, you are breaking one rule. You are never supposed to edit / add to / remove objects while performing fast enumeration on an array. My solution would be to simply call [myMutableArray copy]
which return an immutableArray and then iterate through that.
来源:https://stackoverflow.com/questions/17867248/finding-a-particular-instance-in-an-array-by-a-property