NSArray multiply each argument - xcode

Is the there a way to multiply each NSNumber contained in the array by 10?
Here is what I have so far:
NSMutableArray *vertex = [NSMutableArray arrayWithCapacity:3];
[vertex addObject:[NSNumber numberWithFloat:1.0]];
[vertex addObject:[NSNumber numberWithFloat:2.0]];
[vertex addObject:[NSNumber numberWithFloat:3.0]];
[vertex makeObjectsPerformSelector:#selector(doSomethingToObject:)];
I am not sure what selector to use to do this, please help!

Not easily, no. You would have to loop through the entire thing and replace all those instances of NSNumber with new instances of NSNumber (NSNumber itself is immutable). So, for example:
for( int i = 0; i < [vertex count]; i++ )
[vertex replaceObjectAtIndex:i withObject:[NSNumber numberWithFloat:[[vertex objectAtIndex:i] floatValue] * 10.0f]];
Obviously this is rather hard to read. You are probably better off just using a regular, primitive array of floats if you are going to be manipulating them often (e.g., applying transformations to them).

Short answer - no.
NSNumber of merely a container for a primitive value. It does not do any mathematical work. The makeObjectsPerformSelector method can be used to tell each object in the array to do something. But the class of each of those objects has to have the method the selector is for. NSNumber also does not provide any method for changing the stored value. So even if you added a category to NSNumber to do the math, you would still have to replace the old value in the array with the newly computed one.
I think a better solution would be to add a category method to NSMutableArray to do the work. It would look through the contents, calculate each new value and then replace each array member with the new one.

Related

NSTreeController - Retrieving selected node

I added Book object in bookController (NSCreeController). Now i want to get stored Book object when i select the row.
- (IBAction)addClicked:(id)sender {
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
// NSTimeInterval is defined as double
NSUInteger indexArr[] = {0,0};
Book *obj = [[Book alloc] init];
NSString *dateString = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterLongStyle];
obj.title = [NSString stringWithFormat:#"New %#",dateString];
obj.filename = [NSString stringWithFormat:#"%d",arc4random()%100000];
[self.booksController insertObject:obj atArrangedObjectIndexPath:[NSIndexPath indexPathWithIndexes:indexArr length:2]];
}
I concede there perhaps could be a better solution--
I am unfamiliar with how NSTreeController works, but I looked a the class reference and noticed that it has a content property, similar to an NSArrayController (Which I am familiar with grabbing specific objects from).
I believe that if the content property is actually of type of some kind of tree data structure, my answer here probably won't work. The class reference says this about content:
The value of this property can be an array of objects, or a
single root object. The default value is nil. This property is
observable using key-value observing.
So this is what I historically have done with the expected results:
NSString *predicateString = [NSString stringWithFormat:NEVER_TRANSLATE(#"(filename == %#) AND (title == %#)"), #"FILENAME_ARGUMENT_HERE", #"TITLE_ARGUMENT_HERE"];
NSArray *matchingObjects = [[self content] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:predicateString]];
Then simply calling -objectAtIndex: will grab you your object. Note that the NSArray will be empty if the object doesn't exist, and if you have duplicate objects, there will be multiple objects in the array.
I also searched for an answer to your question, and found this SO thread:
Given model object, how to find index path in NSTreeController?
It looks pretty promising if my solution doesn't work, the author just steps through the tree and does an isEqual comparison.
If you could (if it's not too much trouble), leave a comment here to let me know what works for you, I'm actually curious :)

xcode array access with IBOutletCollections. Mind-boggled.

I have 2 arrays. One is a collection of buttons. The other one should be an array of dictionaries. I am logging the length/size/count of object in the arrays in the console using [arrayListName count]:
2012-07-19 19:56:59.001 ABC[3224:707] lastest_badge_outlet_collection count: 4
2012-07-19 19:56:59.007 ABC[3224:707] badges_array count: 1
ok so when running this loop I want to populate the images with the key value 'name' from each of the dictionaries in existence in the array (that is badges_array). At the moment I have dictionary stored in that array (which is fine). However when I run this through the loop it always populates the third image along
for(int i = 0; i < [lastest_badge_outlet_collection count]; i++){
UIButton *button = [lastest_badge_outlet_collection objectAtIndex:i];
if(i < [badges_array count]){
NSDictionary *badge_d = [badges_array objectAtIndex:i];
NSString *badge_d_image_string = [badge_d objectForKey:#"image"];
UIImage *badge_d_image = [UIImage imageNamed: badge_d_image_string];
[[button imageView] setImage:badge_d_image];
[[button imageView] setContentMode: UIViewContentModeScaleAspectFit];
[button setAlpha:1.0f];
} else {
[button setAlpha:0.5f];
}
}
Theoretically it should be populating the first image. Why is it doing this? How does it decide which order the items are in the outlet collection for example...
Mind boggled.
I tried rewiring them to the collection 1 by 1 by wiring them up in order with no success....
Here is a screenshot.
Thanks!
I don't think there is a way to predict the order of IBOutletCollection.
I guess what you need to do is sort the collection before use it.
You can set tag property for each button and sort them by those number.
Check this answer for more detail.

How to use NSIndexSet

In Objective-C, my program opens a window and displays a table. I want to have a specified row of the table highlighted.
How do I do this?
I seem to need the code
[myTableView selectRowIndexes:(NSIndexSet *) byExtendingSelection:(BOOL)];
I looked at the developer documentation, and figured out that the BOOL should be NO.
By looking at the NSIndexSet docs, I can't figure out what the right syntax should be.
it would be the proper way:
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];
or you can use the NSMutableIndexSet for the random indexes:
NSMutableIndexSet *mutableIndexSet = [[NSMutableIndexSet alloc] init];
[mutableIndexSet addIndex:0];
[mutableIndexSet addIndex:2];
[mutableIndexSet addIndex:9];
etc.
Printing out an NSIndexSet in the debugger will show you that they are internally NSRanges. To create one, you can either specify the range or a single explicit index (from which it will create the range); something like
NSIndexSet *indexes = [[NSIndexSet alloc] initWithIndex:rowToHighlight];
[myTableView selectRowIndexes:indexes byExtendingSelection:NO];
[indexes release];
Note that the index(es) must all be unsigned integers (NSUIntegers, specifically).
I'd use a factory method to avoid having to manage memory:
[myTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:indexes]
byExtendingSelection:NO];
I seem to need the code
[myTableView selectRowIndexes:(NSIndexSet *) byExtendingSelection:(BOOL)];
No; those are casts without anything to cast, which is invalid.
Remove the casts and put values there instead.
I looked at the developer documentation, and figured out that the BOOL should be NO.
Yes, because you don't want to extend the selection, you want to replace it.
By looking at the NSIndexSet docs, I can't figure out what the right syntax should be.
The same as for passing any other variable or message expression.
You need to create an index set and then either stash it in a variable and pass that or pass the result of the creation message directly.

How do copy and mutableCopy apply to NSArray and NSMutableArray?

What is the difference between copy and mutableCopy when used on either an NSArray or an NSMutableArray?
This is my understanding; is it correct?
// ** NSArray **
NSArray *myArray_imu = [NSArray arrayWithObjects:#"abc", #"def", nil];
// No copy, increments retain count, result is immutable
NSArray *myArray_imuCopy = [myArray_imu copy];
// Copys object, result is mutable
NSArray *myArray_imuMuta = [myArray_imu mutableCopy];
// Both must be released later
// ** NSMutableArray **
NSMutableArray *myArray_mut = [NSMutableArray arrayWithObjects:#"A", #"B", nil];
// Copys object, result is immutable
NSMutableArray *myArray_mutCopy = [myArray_mut copy];
// Copys object, result is mutable
NSMutableArray *myArray_mutMuta = [myArray_mut mutableCopy];
// Both must be released later
copy and mutableCopy are defined in different protocols (NSCopying and NSMutableCopying, respectively), and NSArray conforms to both. mutableCopy is defined for NSArray (not just NSMutableArray) and allows you to make a mutable copy of an originally immutable array:
// create an immutable array
NSArray *arr = [NSArray arrayWithObjects: #"one", #"two", #"three", nil ];
// create a mutable copy, and mutate it
NSMutableArray *mut = [arr mutableCopy];
[mut removeObject: #"one"];
Summary:
you can depend on the result of mutableCopy to be mutable, regardless of the original type. In the case of arrays, the result should be an NSMutableArray.
you cannot depend on the result of copy to be mutable! copying an NSMutableArray may return an NSMutableArray, since that's the original class, but copying any arbitrary NSArray instance would not.
Edit: re-read your original code in light of Mark Bessey's answer. When you create a copy of your array, of course you can still modify the original regardless of what you do with the copy. copy vs mutableCopy affects whether the new array is mutable.
Edit 2: Fixed my (false) assumption that NSMutableArray -copy would return an NSMutableArray.
I think you must have misinterpreted how copy and mutableCopy work. In your first example, myArray_COPY is an immutable copy of myArray. Having made the copy, you can manipulate the contents of the original myArray, and not affect the contents of myArray_COPY.
In the second example, you create a mutable copy of myArray, which means that you can modify either copy of the array, without affecting the other.
If I change the first example to try to insert/remove objects from myArray_COPY, it fails, just as you'd expect.
Perhaps thinking about a typical use-case would help. It's often the case that you might write a method that takes an NSArray * parameter, and basically stores it for later use. You could do this this way:
- (void) doStuffLaterWith: (NSArray *) objects {
myObjects=[objects retain];
}
...but then you have the problem that the method can be called with an NSMutableArray as the argument. The code that created the array may manipulate it between when the doStuffLaterWith: method is called, and when you later need to use the value. In a multi-threaded app, the contents of the array could even be changed while you're iterating over it, which can cause some interesting bugs.
If you instead do this:
- (void) doStuffLaterWith: (NSArray *) objects {
myObjects=[objects copy];
}
..then the copy creates a snapshot of the contents of the array at the time the method is called.
The "copy" method returns the object created by implementing NSCopying protocols copyWithZone:
If you send NSString a copy message:
NSString* myString;
NSString* newString = [myString copy];
The return value will be an NSString (not mutable)
The mutableCopy method returns the object created by implementing NSMutableCopying protocol's mutableCopyWithZone:
By sending:
NSString* myString;
NSMutableString* newString = [myString mutableCopy];
The return value WILL be mutable.
In all cases, the object must implement the protocol, signifying it will create the new copy object and return it to you.
In the case of NSArray there is an extra level of complexity regarding shallow and deep copying.
A shallow copy of an NSArray will only copy the references to the objects of the original array and place them into the new array.
The result being that:
NSArray* myArray;
NSMutableArray* anotherArray = [myArray mutableCopy];
[[anotherArray objectAtIndex:0] doSomething];
Will also affect the object at index 0 in the original array.
A deep copy will actually copy the individual objects contained in the array. This done by sending each individual object the "copyWithZone:" message.
NSArray* myArray;
NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:myArray
copyItems:YES];
Edited to remove my wrong assumption about mutable object copying
NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:oldArray
copyItems:YES];
will create anotherArray which is a copy of oldArray to 2 levels deep. If an object of oldArray is an Array. Which is generally the case in most applications.
Well if we need a True Deep Copy we could use,
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: oldArray]];
This would ensure that all levels are actually copied retaining the mutability of the original object at each level.
Robert Clarence D'Almeida,
Bangalore, India.
You're calling addObject and removeObjectAtIndex on the original array, rather than the new copy of it you've made. Calling copy vs mutableCopy only effects the mutability of the new copy of the object, not the original object.
To state it simply,
copy returns an immutable (can't be modified) copy of the array,
mutableCopy returns a mutable (can be modified) copy of the array.
Copy (in both cases) means that you get a new array "populated" with object references to the original array (i.e. the same (original) objects are referenced in the copies.
If you add new objects to the mutableCopy, then they are unique to the mutableCopy. If you remove objects from the mutableCopy, they are removed from the original array.
Think of the copy in both cases, as a snapshot in time of the original array at the time the copy was created.
Assume
NSArray *A = xxx; // A with three NSDictionary objects
NSMutableArray *B = [A mutableCopy];
B's content is NSDictionary object not NSMutableDictionary, is it right?
-(id)copy always returns a immutable one & -(id)mutableCopy always returns a mutable object,that's it.
You have to know the return type of these copying stuff and while declaring the new object which one will be assigned the return value must be of immutable or mutable one, otherwise compiler will show you error.
The object which has been copied can not be modified using the new one,they are totally two different objects now.

Get NSIndexSet from NSArray

NSArray has useful methods to find objects for specified indexes
// To find objects by indexes
- (id)objectAtIndex:(NSUInteger)index
- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes
// To find index by object
- (NSUInteger)indexOfObject:(id)anObject
However, I want to get NSIndexSet (multiple indexes) for given objects. Something like:
- (NSIndexSet *)indexesOfObjects:(NSArray *)objects
This method does not exist for NSArray. Am I missing something? Does someone know another standard method? Otherwise I have to write this as a category method.
Newer NSArray versions (OSX 10.6 and iOS 4) provides the indexesOfObjectsPassingTest: method.
NSIndexSet *indexesOfObjects = [[array1 indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [array2 containsObject:obj];
}];
It might be useful to implement it using a set to specify the objects to find, such as:
- (NSIndexSet *) indicesOfObjectsInSet: (NSSet *) set
{
if ( [set count] == 0 )
return ( [NSIndexSet indexSet] );
NSMutableIndexSet * indices = [NSMutableIndexSet indexSet];
NSUInteger index = 0;
for ( id obj in self )
{
if ( [set containsObject: obj] )
[indices addIndex: index];
index++;
}
return ( [[indices copy] autorelease] );
}
This requires visiting every object in the array, but at least only does so once and makes use of fast enumeration while doing so. Using an NSSet and testing each object in the array against that set is also much faster than testing for inclusion in an array.
There's a potential optimization here, but it would break in the case where a single object is stored in the receiving array multiple times:
if ( [set containsObject: obj] )
{
[indices addIndex: index];
if ( [indices count] == [set count] )
break;
}
That way if you're scanning a 20'000-item array for two objects and they're both inside the first ten, you'll be able to avoid scanning the other 19'990 objects in the array. As I said though, that doesn't help if the array contains duplicates, because it'll stop as soon as it's found 2 indices (even if they both point to the same object).
Having said that, I agree with Mike's comment above. Chances are you're setting yourself up for some pain come optimization-time. It may be worth thinking about different data types; for instance, while NSArray seems the most logical choice for a simple flat container, if you don't actually need the ordering information it's better to use an NSSet instead; this has the added advantage that it won't store the same object (calculated using -isEqual:) twice. If you do want to keep track of duplicates, but don't need ordering, you can use NSCountedSet, which behaves as NSSet except it keeps track of how many times each objects has been added/removed without actually storing duplicates.
You have to implement your own category, as far as I can see.

Resources