NSMutableArray to NSDictionary and then add to an NSArray - xcode

My app stores images in an NSMutableArray. I then call those objects and then send them through email in the mailSender.parts section of the code below. The problem is it only adds the first objectatindex when I need to add all objects. I am confused on how to make each image in the self.arrSlidshowImg NSMutableArray add to the vcfPart2 NSDictionary and then add it as an array object so the mailSender.parts will send all images. Any thoughts? I should also note that I did an NSLog to see the results adding this code NSLog(#"VCF: %#", vcfPart2);. The log file showed each value in vcfPart2. So the code is calling each response.
NSDictionary *vcfPart2;
for (int i = 0; i < self.arrSlidshowImg.count; i++) {
NSData *vcfData = [self.arrSlidshowImg objectAtIndex:i];
vcfPart2 = [JFMailSender partWithType:PartTypeFilePart
Message:[vcfData base64EncodedStringWithOptions:0]
ContentType:#"image/jpeg"
ContentTransferEncoding:#"base64"
FileName:[NSString stringWithFormat:#"Individual_%d", i]];
}
mailSender.parts = [NSArray arrayWithObjects:plainPart2,vcfPart2,nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[mailSender sendMail];
});
});

The following comes from a simple reading of your code, and among other things I have not looked up JFMailSender:
You declare a variable vcfPart2 to hold a reference to a dictionary
You enter a loop
In the loop you assign a value to vcfPart2, this value is presumably a reference to a dictionary as you report no warnings
Step 3 is executed self.arrSlidshowImg.count times, ice per iteration of the loop
You exit the loop, at this point vcfPart2 will hold the last reference assigned to it, the previous self.arrSlidshowImg.count - 1 having been overwritten
Your concern appears to be that when you then use vcfPart2 it only references one dictionary - but that is all it can ever do, that is it's type.
Maybe you intended to create a dictionary in your loop and then add that dictionary to a mutable array so that after the loop the array contains all the dictionaries?
HTH

Related

Handler blocks and NSArrays - how to add to an array in a block?

I want to add an NSObject to an NSMutableArray inside of a a block. In the code below, the NSLog line works fine and returns the number I expect. However, when I try to add that result to an NSArray, the array is always empty when I go to access it later.
CMStepQueryHandler stepQueryHandler = ^(NSInteger numberOfSteps,
NSError *error) {
NSLog(#"CMStepQueryHandler: Steps on day: %i", (int)numberOfSteps);
[stepsPerDay addObject:[NSNumber numberWithInt:numberOfSteps]];
};
How can I add an object to an NSMutableArray (in this case stepsPerDay) from inside of a block so that I can access it later?
The code looks fine.
I suspect you forgot to initialize stepsPerDay. It should be
NSMutableArray *stepsPerDay = [NSMutableArray array];
As a non-related advice, you may also consider a more modern syntax
[stepsPerDay addObject:#(numberOfSteps)];
The problem was that the block would always execute the addObject after I had tried to work with the contents of the array, I guess due to the fact that the block was executed asynchronously. The solution was to have the block call a function which worked with the array and that way I could guarantee the object would deb added to the array when the function was executed.

Objective-C EXC_BAD_ACCESS

Ok so I've recently decided to try to teach myself Objective-C (I'm interested in iPhone development), however I've never used C or any of its derivatives before, and as such am running into some problems.
I decided to start out by writing a very basic card application that creates a deck of cards, shuffles the deck, and then displays the cards on the screen using UIButtons, however I'm having a problem with my shuffling algorithm. Every time it gets called I get an EXC_BAD_ACCESS error, which I know means there's something desperately wrong with my code, but I just can't figure out what it is.
- (void) randomize {
NSMutableArray *tmpDeck = [[NSMutableArray alloc] init];
for(Card *tmp in _cards) {
BOOL didInsert = NO;
while(!didInsert) {
NSUInteger random = arc4random_uniform(54);
if([[tmpDeck objectAtIndex:random] isEqual:nil]) {
[tmpDeck insertObject:tmp atIndex:random];
didInsert = YES;
}
}
}
_cards = tmpDeck;
_hasBeenRandomized = YES;
}
_cards is a pointer to an NSMutableArray containing the unshuffled deck of card objects, and _hasBeenRandomized is a boolean (obviously) that keeps track of whether or not the deck has been randomized.
I've tried to use the debugger to work out what exactly is going on here, but I can't even step into the method without my program crashing. This leads me to believe that the problem has to come from the very first line, but it's just a straightforward creation of an NSMutableArray, so I don't know how it could be that. This method is being called from within viewDidLoad. This is the entirety of the viewDidLoad method currently.
- (void)viewDidLoad
{
[super viewDidLoad];
_deck = [[Deck alloc] init];
[_deck randomize];
}
Any and all help will be appreciated. Sorry if the answer is dead obvious.
This is because you are trying to insert into an index that doesn't exist yet. You need to initialize the array with as many places in the array as you need for your cards. Either that or use a NSMutableDictionary and just insert the object with the index being the key.
To add another note, calling initWithCapacity on the array wouldn't solve this for you either since this just gives a "hint" at the size. You need the count property of the array to actually be at least as large as the index you are trying to insert. If you wanted to do an array, then you would first need to populate something in each index first. You could define this in the new array literal format or use a for loop that loops the number of times you need (your max index) and insert a dummy object in it's place.
for (int i=0; i< _cards.count; ++i)
{
[tmpDeck insertObject:#"dummy" atIndex:i];
}
Then instead of checking for 'nil' before you replace, you check if it is equal to the dummy object you inserted. This would give you an array that you can insert into any of these indexes. I personally would still probably store them in an NSMutableDictionary. But if you need it in an array for some other purpose then this is a way to do it.
You also will need to be sure to replace the object instead of inserting, otherwise you will just keep adding indexes.
[tmpDeck replaceObjectAtIndex:random withObject:tmp];
If you still get the same error, set a breakpoint in your debugger and check what the random number is and what the count of your array is. If your random number is ever greater than your array count, then you will get this error.

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.

Proper way to return an array

I never seem to get this right. I've got a method that returns a mutable array. What is the proper way to return the array and avoid potential memory leaks?
If I plan to store the results locally inside another view controller, does that affect the way the array should be returned?
Lastly, what if it's just an non-mutable array? Does that require a different technique?
thanks,
Howie
If your method does not have alloc or copy in the name then the proper thing is to return a autoreleased version of the array. Also, you should return a copy of the array to prevent modifications to your local copy
- (NSMutabalArray*] mutableArray {
return [[myArray mutableCopy] autorelease];
}
- (NSArray*] array {
return [[myArray copy] autorelease];
}
Return an auto-released object. If you've created your array with any alloc/init/copy methods - you should send autorelease message to array before returning it (something like return [myArray autorelease];). Otherwise arrays created with factory methods (arrayFrom... arrayWithContentsOf...) return autoreleased object so you don't need to worry about memory leaks there.
You should read about memory management and retain count on apple dev site. There might be some other initialization methods that retain returned object which would 'cause a memory leak.
For a NSMutableArray I would use:
-(NSMutableArray*)getMyArray
{
NSMutableArray *retval = [[NSMutableArray alloc] init];
// do your stuff w/ array
return [retval autorelease];
}
The caller of this code may want to retain the returned array, since it is autoreleased.

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.

Resources