Cocoa Touch: Accessor method + memory needs clarification - iphone

Please consider a problem constructing an array and returning it to a parent object. Please have a look at my code and comments below.
My question is: Why does method THREE work while methods ONE and TWO do not?
// ****************************
// Master.m
// ****************************
// Simply declare array and call accessor
NSMutableArray * allElementTypes;
allElementTypes = [ElementObject badElements];
// ****************************
// ElementObject.m
// ****************************
// Doesn't work (EXE_BAD_ACCESS)
+ (NSMutableArray*) badElements {
NSMutableArray * array = [[NSMutableArray alloc] initWithObjects:#"crab", #"poison", #"snake"];
return array;
}
// Doesn't work (EXE_BAD_ACCESS)
+ (NSMutableArray*) badElements {
return [NSMutableArray arrayWithObjects:#"crab", #"poison", #"snake"];
}
// WORKS
+ (NSMutableArray*) badElements {
NSMutableArray * array = [[NSMutableArray alloc] init];
[array addObject:#"crab"];
[array addObject:#"poison"];
[array addObject:#"snake"];
return array; // TODO: release this memory
}

-arrayWithObjects: or -initWithObjects takes a nil terminated, variable length, list of arguments.
You wrote:
[NSMutableArray arrayWithObjects:#"crab", #"poison", #"snake"];
where you should have written:
[NSMutableArray arrayWithObjects:#"crab", #"poison", #"snake", nil];

Related

NSMutableArray replace object

I try to find object in my array and if success I need to replace object from my array to new object
for (id existingSig in allSignature)
if ([[existingSig objectForKey:#"SignatureName"] isEqualToString:[item objectForKey:#"name"]])
{
[allSignature removeObject:existingSig];
[allSignature addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"SignatureIsRich", [item objectForKey:#"name"], #"SignatureName", generatedID, #"SignatureUniqueId", nil]];
}
I have error 'NSCFArray: 0x100551f10> was mutated while being enumerated'
As others have said, you cannot mutate a MutableArray while it is being Enumerated. You could handle it by having two arrays of what to remove and what to add after the loop.
NSMutableArray *remove = [NSMutableArray array];
NSMutableArray *add = [NSMutableArray array];
for (id existingSig in allSignature){
if ([[existingSig objectForKey:#"SignatureName"] isEqualToString:[item objectForKey:#"name"]])
{
// Add to the array of objects to be removed
[remove addObject:existingSig];
// Add to the array of objects to be added
[add addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"SignatureIsRich", [item objectForKey:#"name"], #"SignatureName", generatedID, #"SignatureUniqueId", nil]];
}
}
[allSignature removeObjectsInArray:remove]; // Remove objects
[allSignature addObjectsFromArray:add]; // Add new objects
The easiest way is to make a copy and iterate over that, then modify the original.
I have a map array method I have in a NSArray Category for this very purpose
- (NSArray *) cw_mapArray:(id (^)(id obj))block
{
NSMutableArray * cwArray = [[NSMutableArray alloc] init];
[self cw_each:^(id obj, NSUInteger index, BOOL *stop) {
id rObj = block(obj);
if (rObj) {
[cwArray addObject:rObj];
}
}];
return cwArray;
}
this way I can get a new array and then just change the array with the new array. You can change cw_each to enumerateObjectsUsingBlock:. Basically this is simple, if you want to map the object to the new array you just return the object as is in the block, otherwise modify it and return that or if you don't want to map the object to the new array then return nil. Its not very much code and works wonderfully.

NSMutable Array: Getting "out of scope" Status After Mutable Copying

I have a SOAP service and I generated classes and functions on SudzC.com.
So I'm using the soap functions they generated, it returns an NSMutableArray with objects that are inherited by my custom class(which is generated by them, too).
So far everything's good. My values are getting into the array and I could see any property of any object with one condition: Only inside of the function that's handling the service.
Just to make it clear, here is the code:
- (void)viewDidLoad
{
SDZGeneratedWebService* service = [SDZGeneratedWebService service];
service.logging = YES;
[service callMyData:self action:#selector(callMyDataHandler:) dataId: 1];
[super viewDidLoad];
}
- (void) callMyDataHandler: (id) value {
// Handle errors
if([value isKindOfClass:[NSError class]]) {
NSLog(#"%#", value);
return;
}
// Handle faults
if([value isKindOfClass:[SoapFault class]]) {
NSLog(#"%#", value);
return;
}
// Do something with the NSMutableArray* result
NSMutableArray *result = (NSMutableArray *)value;
MyCustomClass *myObject = [result objectAtIndex:0];
NSLog(#"%#", myObject.myProperty); //Works Great
}
Like I said, so far everything's perfect. But I need to use the data outside of this function.
So in my .h file, I created an array like NSMutableArray *myDataArray;
When I intend to copy the result array to myDataArray, it copies the objects(I can see that the myDataArray.count value is equal to result array's) but all the objects are "out of scope". So I cannot use them.
I also tried to copy all objects by indexes in a for loop, nope, the objects are getting their values, but when I "addObject" to myDataArray, same, out of scope.
What is wrong here? Can't I generate an array of a custom class this way?
Edit: The code I'm generating myDataArray:
myDataArray = [[NSMutableArray alloc] init];
[myDataArray removeAllObjects];
for (int i=0; i<((NSMutableArray *)result).count; i++) {
MyCustomClass *myObject = [result objectAtIndex:i];
[myDataArray addObject:myObject];
[myObject release];
}
[self.tableView reloadData];
} //(End of callMyDataHandler function)
I before tried this way, too:
[myDataArray removeAllObjects];
duyurular = [result mutableCopy];
} //(End of callMyDataHandler function)
You can copy objects from one array to another using this method:
NSArray *source;
NSArray *dst = [[NSArray alloc] initWithArray:source];
In your code you should remove line: [myObject release]; and I would better call [((NSMutableArray *)result) count] rather then using dot notation.

iOS - Passing a NSMutableArray via method return

How would one pass a NSMutableArray via method return.
I have it passing the array "spaces" so an array of 10 objects passes the 10 blocks but none of the information contained in those objects.
Thanks in advance
Edit: Basically I created another class that contains path information because my controller was getting a bit cluttered. So this new class I want call the "create" method which returns an NSMutableArray. The array is created fine in the path class but when the return statement fires it only passes the spaces and not the values or even a pointer.
currently it's
return path;
I've tried
return &path;
and that fails epically.
Edit2: Here is the issue I'm having unfortunately.
Still crashing
calling
newNode = [newNode copy];
causes a crash
- (NSMutableArray *) mutableFloobizwits {
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < TheAnswerToTheUltimateQuestion; ++i) {
void(^MyBlock)(void) = ^{
NSLog(#"captured i: %ld", i);
};
MyBlock = [MyBlock copy]; //move the block from off the stack and onto the heap
[array addObject:[Floobizwit floobizwithWithBlock:MyBlock]];
[MyBlock release]; //the Floobizwit should've -retained the block, so we release it
}
return array;
}
I would set up your other class that returns the array of path objects as follows:
#implementation PathFactory
- (NSMutableArray*) create
{
// In your PathFactory object you create an array and make it autorelease so
// it becomes the callers responsibility to free the memory
NSMutableArray * pathArray = [[[NSMutableArray alloc] init] autorelease];
// Create a bunch of PathObject objects and add them to the mutable array
// also set these to autorelease because the NSMutableArray will retain objects
// added to the collection (ie It is the NSMutableArray's responsibility to ensure
// the objects remain allocated).
for (int i = 0; i < numberOfPaths; i++)
[pathArray addObject:[[[PathObject alloc] init] autorelease]];
// Just return the pointer to the NSMutableArray. The caller will need to
// call the retain message on the pointer it gets back (see next)
return pathArray;
}
#end
So in your caller code:
// create a tempory PathFactory (autorelease will make sure it is cleaned up when we
// are finished here)
PathFactory * newPathFactory = [[[PathFactory alloc] init] autorelease];
// grab the new array of Path objects and retain the memory. _newPathArray
// is a member of this class that you will need to release later.
_newPathArray = [[newPathFactory create] retain];

how can I save NSMutableArray into NSMutableArray so that previous data in NSMutableArray remain same?

I want to know that how can I add NSMutableArray in to an NSMutableArray so that previous data should not lost, and new data will be added on next indexes.
If you don't understand it then you can ask again to me,
I will appraise the right answer.
my code is as below
-(void)setArray1:(NSMutableArray *)arrayValueFromNew
{
self.myArray=arrayValueFromNew;
myArray2 = [[NSMutableArray alloc] initWithArray:arrayValueFromNew];
for(int i=0;i<[myArray2 count];i++)
{
[myArray addObject:[myArray2 objectAtIndex:i]];
}
}
In your code, myArray and myArray2, both have same objects as you are assigning the arrayValueFromNew array to both. So it kind of doesn't make sense.
But to answer your question 'how to add one array to another?' do :
[mutableArray1
addObjectsFromArray:array2];
EDIT:
this is how your method should look
-(void)setArray1:(NSMutableArray *)arrayValueFromNew
{
if(!self.myArray)
{
self.myArray = arrayValueFromNew;
}
else
{
[self.myArray addObjectsFromArray:arrayValueFromNew];
}
}
Your 'myArray must be initialized. You can initialize it in viewDidLoad or init:
self.myArray = [[NSMutableArray alloc]
initWithCapacity:1];
NSMutableArray *array1 = [NSMutableArray array], *array2 = [NSMutableArray array];
// add some objects to the arrays
[array1 addObjectsFromArray:array2];
//array1 now contains all the objects originally in array1 and array2
This will work,
NSMutableArray *mutarr=[[NSMutableArray alloc]initWithArray: array1]
It looks like you just want a new copy of the old array. There is a handy function for that
NSMutableArray *newArray = [oldArray mutableCopy];
Remember that you've used copy in getting this array so you are responsible for managing the memory of newArray
EDIT
What is your code doing?
-(void)setArray1:(NSMutableArray *)arrayValueFromNew //1
{
self.myArray=arrayValueFromNew; //2
myArray2 = [[NSMutableArray alloc] initWithArray:arrayValueFromNew]; //3
for(int i=0;i<[myArray2 count];i++)
{
[myArray addObject:[myArray2 objectAtIndex:i]]; //4
}
}
This looks like a setter for a property array1
You are setting the property 'array' to arrayValueFromNew. Since I don't know whether this property has been declared with retain or copy I don't know whether array is a pointer to arrayValueFromNew or a pointer to a copy of arrayValueFromNew
You set myArray2 to be a new array that contains the objects of arrayValueFromNew
For each object in myArray2 (which are the objects from arrayValueFromNew. see point 3) you add this object to myArray. Assuming myArray is an NSMutableArray it started with the objects from arrayValueFromNew which you have now added again. It contains each item in arrayValueFromNew twice.

Array is empty after function

I am newbie with Cocoa Touch, I have a problem that I try to figure out. I will appreciate if anyone could help.
I would like to create a tableDataList to display on the table. AsyncViewController will call TableHandler fillList method to initialize table data. But after fillList call, the tableDataList return empty.
Can anyone explain me this problem?
In "AsyncViewController.m":
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[self.myHandler fillList];
[super viewDidLoad];
}
In "TableHandler.m":
#implementation TableHandler
#define ItemNumber 20
#synthesize tableDataList;
- (void) fillList {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:(NSUInteger) 20];
for (NSUInteger i = 0; i < ItemNumber; i++) {
[array addObject:[NSString stringWithFormat:#"Row %d", i]];
}
tableDataList = [NSArray arrayWithArray:array];
}
Thanks
tableDataList needs to retain the new array, or it will be autoreleased soon after your call.
If tableDataList is a #property with retain, just change the line above to:
self.tableDataList = [NSArray arrayWithArray:array];
and the setter will handle it for you.
The equivalent of a #property (retain) NSArray *tableDataList; is in code,
- (void)setTableDataList:(NSArray *)anArray
{
if (tableDataList != nil) {
[tableDataList autorelease];
}
tableDataList = [anArray retain];
}
The above code will automatically release and retain objects when you replace the variable, using self.tableDataList = SOMETHING. However, if you just use tableDataList = SOMETHING you are not using the above setter, you're setting the variable directly.
Are you sure it's empty and not nil? You may need to alloc the tableDataList before you use it

Categories