It seems that i didn't cover my basics enough, but I hope that You guys mabybe will be able to help here.
I need to use cateringView.status outside this loop and even in another class. This is simple BOOL value, parsed from XML with PUGIXML
- (void)dataPrepared
{
Food* food = (Food*)[[DataManager sharedInstance] dataForItem:kDataManagerItemCatering];
if (food)
{
for (CateringView* cateringView in cateringViews)
[cateringView removeFromSuperview];
[cateringViews removeAllObjects];
for (FoodItem* item in food.catering)
{
CateringView* cateringView = [CateringView new];
[cateringView.imageView loadURL:[NSURL URLWithString:item.image] session:[DataManager sharedInstance].session completion:nil];
cateringView.status = item.status;
[self addSubview: cateringView];
[cateringViews addObject: cateringView];
}
[self layoutSubviews];
}
[super dataPrepared];
}
Could You explain me how can I do that?
My header file:
(...)
#interface CateringView : UIView
#property (strong) NSNumber* status;
#end
#interface CateringPreviewCell : PreviewCell
{
NSMutableArray* cateringViews;
(...)
}
#end
Is this somebody else's code you are trying to understand? It is unclear what you are asking, but you appear to be confusing the lifetime of local variables and objects. Maybe the following will help:
The second for loop starts:
for (FoodItem* item in food.catering)
{
CateringView* cateringView = [CateringView new];
This last statements does two things:
The right hand side (RHS) creates a new object of type CateringView. The result of the RHS is a reference to the created object. The lifetime of the created object extends as long as there is a reference to it[A].
The left hand side (LHS) creates a new local variable called cateringView. The reference returned by the RHS is stored in this variable. The lifetime of the created variable is a single iteration of the for loop.
At the end of the loop the code is:
[self addSubview: cateringView];
[cateringViews addObject: cateringView];
}
These two statements take the reference, to the created CateringView object, which is stored in the local variable cateringView and add it to this object's (which is an instance of the CateringPreviewCell class) subviews and cateringViews instance variable.
After these two statements have executed you have stored the reference to the created CateringView object three times: in the local variable cateringView, in the owning object's subviews, and in the owning object's cateringViews instance variable.
Also after these statements the loop iteration ends, so the lifetime of the local variable cateringView ends and you can no longer use that variable. However the reference to the object that was stored in that local variable still exists in two locations and that object is still alive.
You are stating you need to access cateringView.status outside of the loop. That does not make sense, the variable does not exist. However the object the variable referenced when it did exist is still alive, so the status value you seek is still around - you are just looking in the wrong place.
After the loop, and after the call to dataPrepared has returned, all the CateringView objects created can be accessed either:
as subviews of the object instance of CateringPreviewCell that dataPrepared was called on, or
as members of the instance variable cateringViews of that object instance.
The first of these is accessible "outside the class", the second can be provided you have instance methods defined on CateringPreviewCell which provided access to the instance variable.
HTH
[A]: This is not exactly true, but sufficient for the purpose here. Later you may learn about things such as weak references which do not govern how long an object lives.
You wouldn't use cateringView, because you actually have many of them. They're all stored in cateringViews so that's what you'd actually use. You'd either iterate all the views in that array or you'd choose one at a specific index to interact with.
Without knowing whether your intention is to try to add .status to
existing objects inside the cateringViews array
or
new objects that you add to cateringViews array
since your question has missing information and isn't very clear, here is the solution for #2
for (CateringView* cateringView in self.cateringViews) { //needed curly braces and self. to access property var
[cateringView removeFromSuperview];
}
[self.cateringViews removeAllObjects]; //again needed self.
for (int i = 0; i < food.catering.count; i++) { //make sure food.catering is array and not nil
CateringView* cateringView = [[CateringView alloc] init];
[cateringView.imageView loadURL:[NSURL URLWithString:item.image]
session:[DataManager sharedInstance].session
completion:nil];
FoodItem *item = food.catering[i]
cateringView.status = item.status;
[self addSubview: cateringView];
[self.cateringViews addObject: cateringView];
}
If I'm wrong and you're trying to accomplish #1, (consider improving your question wording if this is the case) then you'll need to loop through self.cateringViews as well.
Related
I've seen several other questions of the same form, but I either a) can't understand the provided answers, or b) don't see how those situations are similar to mine.
I'm writing a Category on UIView to recursively evaluate all the subviews of a UIView and return an Array of subviews passing a test. I've noted where my compiler warning occurs:
-(NSArray*)subviewsPassingTest:(BOOL(^)(UIView *view, BOOL *stop))test {
__block BOOL *stop = NO;
NSArray*(^__block evaluateAndRecurse)(UIView*);
evaluateAndRecurse = ^NSArray*(UIView *view) {
NSMutableArray *myPassedChildren = [[NSMutableArray alloc] init];
for (UIView *subview in [view subviews]) {
BOOL passes = test(subview, stop);
if (passes) [myPassedChildren addObject:subview];
if (stop) return myPassedChildren;
[myPassedChildren addObjectsFromArray:evaluateAndRecurse(subview)];
// ^^^^ Compiler warning here ^^^^^
// "Capturing 'evaluateAndRecurse' strongly in this block
// is likely to lead to a retrain cycle"
}
return myPassedChildren;
};
return evaluateAndRecurse(self);
}
Also, I get a bad_access failure when I don't include the __block modifier in my block's declaration (^__block evaluateAndRecurse). If someone could explain why that is, that would be very helpful too. Thanks!
The problem here is that your block evaluteAndRecurse() captures itself, which means that, if it's ever to be copied (I don't believe it will in your case, but in slightly less-trivial cases it may), then it will retain itself and therefore live forever, as there is nothing to break the retain cycle.
Edit: Ramy Al Zuhouri made a good point, using __unsafe_unretained on the only reference to the block is dangerous. As long as the block remains on the stack, this will work, but if the block needs to be copied (e.g. it needs to escape to a parent scope), then the __unsafe_unretained will cause it to be deallocated. The following paragraph has been updated with the recommended approach:
What you probably want to do here is use a separate variable marked with __unsafe_unretained that also contains the block, and capture that separate variable. This will prevent it from retaining itself. You could use __weak, but since you know that the block must be alive if it's being called, there's no need to bother with the (very slight) overhead of a weak reference. This will make your code look like
NSArray*(^__block __unsafe_unretained capturedEvaluteAndRecurse)(UIView*);
NSArray*(^evaluateAndRecurse)(UIView*) = ^NSArray*(UIView *view) {
...
[myPassedChildren addObjectsFromArray:capturedEvaluateAndRecurse(subview)];
};
capturedEvaluateAndRecurse = evaluteAndRecurse;
Alternatively, you could capture a pointer to the block, which will have the same effect but allow you to grab the pointer before the block instantiation instead of after. This is a personal preference. It also allows you to omit the __block:
NSArray*(^evaluateAndRecurse)(UIView*);
NSArray*(^*evaluteAndRecursePtr)(UIView*) = &evaluateAndRecurse;
evaluateAndRecurse = ^NSArray*(UIView*) {
...
[myPassedChildren addObjectsFromArray:(*evaluateAndRecursePtr)(subview)];
};
As for needing the __block, that's a separate issue. If you don't have __block, then the block instance will actually capture the previous value of the variable. Remember, when a block is created, any captured variables that aren't marked with __block are actually stored as a const copy of their state at the point where the block is instantiated. And since the block is created before it's assigned to the variable, that means it's capturing the state of the capturedEvaluteAndRecurse variable before the assignment, which is going to be nil (under ARC; otherwise, it would be garbage memory).
In essence, you can think of a given block instance as actually being an instance of a hidden class that has an ivar for each captured variable. So with your code, the compiler would basically treat it as something like:
// Note: this isn't an accurate portrayal of what actually happens
PrivateBlockSubclass *block = ^NSArray*(UIView *view){ ... };
block->stop = stop;
block->evaluteAndRecurse = evaluateAndRecurse;
evaluteAndRecurse = block;
Hopefully this makes it clear why it captures the previous value of evaluateAndRecurse instead of the current value.
I've done something similar, but in a different way to cut down on time allocating new arrays, and haven't had any problems. You could try adapting your method to look something like this:
- (void)addSubviewsOfKindOfClass:(id)classObject toArray:(NSMutableArray *)array {
if ([self isKindOfClass:classObject]) {
[array addObject:self];
}
NSArray *subviews = [self subviews];
for (NSView *view in subviews) {
[view addSubviewsOfKindOfClass:classObject toArray:array];
}
}
Ok, so I am working with two sets of data that are extremely similar, and at the same time, these data sets are both global NSMutableArrays within the object.
data_set_one = [[NSMutableArray alloc] init];
data_set_two = [[NSMutableArray alloc] init];
Two new NSMutableArrays are loaded, which need to be added to the old, existing data. These Arrays are also global.
xml_dataset_one = [[NSMutableArray alloc] init];
xml_dataset_two = [[NSMutableArray alloc] init];
To reduce code duplication (and because these data sets are so similar) I wrote a void method within the class to handle the data combination process for both Arrays:
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str
Now, I have a decent understanding of object oriented programming, so I was thinking that if I were to invoke the method with the global Arrays in the data like so...
[self constructData:data_set_one fromDownloadArray:xml_dataset_one withMatchSelector:#"id"];
Then the global NSMutableArrays (data_set_one) would reflect the changes that happen to "array" within the method. Sadly, this is not the case, data_set_one doesn't reflect the changes (ex: new objects within the Array) outside of the method.
Here is a code snippet of the problem
// data_set_one is empty
// xml_dataset_one has a few objects
[constructData:(NSMutableArray *)data_set_one fromDownloadArray:(NSMutableArray *)xml_dataset_one withMatchSelector:(NSString *)#"id"];
// data_set_one should now be xml_dataset_one, but when echoed to screen, it appears to remain empty
And here is the gist of the code for the method, any help is appreciated.
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str {
if ([data count] == 0) {
data = down; // set data equal to downloaded data
} else if ([down count] == 0) {
// download yields no results, do nothing
} else {
// combine the two arrays here
}
}
This project is not ARC enabled.
Thanks for the help guys!
Rob
If I understood your problem, you are trying to pass your object as call-by-reference and hoping to work as in C++/C. But Obj-C, although similar but has some different way. You have to dereference the object by using ** (double pointer) as mostly seen in NSError case, which is very rare.
Second ways is: wrap method as a block. Then put the variable in the same lexical scope as the block, and denote it by __block storage type.
Third way can be accessing your object directly by making it an ivar/property or even an singleton.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this :
classA
-(NSMutableArray*)set:(NSMutableArray*)data
{
manch= [[data mutableCopy]autorelease] ;
int count=2*[data count]; //to not enter infinity loop
for(int k=0;k< count;k=k+2)
{
if(k==count-1)
[manch addObject:[NSNumber numberWithInt:![[manch objectAtIndex:k] integerValue] ] ];
}
data=[manch mutuableCopy];
return data;
}
My goal is to create a class that gets an NSMutuableArray do some calculations, than return it, and NOT TO BE DEPEND on this class anymore .
EDIT :
As people here ask.
in another classB(the user of the method above), i have in the interface :
NSMutuableArray *data ;
and on the .m file init method i have
data=[[NSMutuableArray alloc]init];
to use the function from my question, i do :
mIns=[[classA alloc]init];
data= [mIns set:[self decimalToBinary:autoWord]];
than i loose data later.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this:
This is an incorrect statement, you can return an autoreleased object, that's a sane thing to do. It's worth noting that you should design your method names correctly to inform the user what sort of object is returned. Any method whose name begins with alloc, new, copy, or mutableCopy will return a retained object. (Source)
In your case, your method name is set:, which informs the user of this method that it will return a non retained object (almost always an autoreleased object). This is because it isn't prefixed with any of those words mentioned above.
In that case, the issue you have is with the user of the method; they are not retaining a reference to the object being returned. As such, the user of the method should use it as so:
#interface ClassName () {
NSMutableArray* m_ivarArray;
}
#property (nonatomic, retain) NSMutableArray* propertyArray;
#end
NSMutableArray* data = ...;
// If using a property with retain, setting via "self." will retain it for you
self.propertyArray = [self set:data];
// If using an ivar (which doesn't do the retain for you)
m_ivarArray = [[self set:data] retain];
You can avoid these issues by using Automatic Reference Counting (ARC, More Information), which will handle this sort of memory management for you. It is still important that you use the correct naming conventions, as ARC will judge how to manage your memory based on this (in certain situations)
Update: After seeing your update, I can see the problem.
data=[[NSMutuableArray alloc]init];
This is creating a new instance of NSMutableArray, one which is correctly retained (due to what I mentioned before).
data= [mIns set:[self decimalToBinary:autoWord]];
This is replacing the object held in data with a new NSMutableArray, one that is autoreleased. The previous instance you created has been lost, and you've replaced it with another one. This new instance has not been retained, and as such, will be released unexpectedly.
To fix, you need to use this instead:
NSMutableArray* data = [[mIns set:[self decimalToBinary:autoWord]] retain];
You don't need to alloc/init a variable if it will be populated by some other object later on. I strongly suggest brushing up on how this all works, this might be a good start.
i have two Objective-C classes, say ParentLayer and ChildLayer. in my child instance, i want to access a C-Array in my parent instance. so i have something like this in my cocos2d code:
#define kNumOfElements 10
#implementation ParentLayer{
int array[kNumOfElements];
}
-(id)init{
//...
for(int i=0;i<kNumOfElements;i++){
array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
-(int *)getArray{
return array;
}
#end
//meanwhile in my child layer...
//...
-(void)initializeValues{
int *arr = [(ParentLayer *)[self parent] getArray];
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
//...
what's the proper way to do this?
maybe i dont understand the right memory management behind C Arrays.
i was under the impression that C Arrays didn't need to be allocated,
and that they could be passed by value, on the stack?
also, shouldn't my parent instance still be around? i thought if i
put a C Array as an ivar of my parent, it shouldn't get destroyed
any help is appreciated. thanks!
what's the proper way to do this?
Ideally, you should never pass a C-style array pointer outside of the object that owns it. You open yourself up to all sorts of problems if a piece of code tries to use the array after the object is deallocated, or writes past the end, or something else. It is easier to guarantee that none of this happens if you can make sure the reference never leaves the object's source file.
maybe i dont understand the right memory management behind C Arrays. i was under the impression that C Arrays didn't need to be allocated, and that they could be passed by value, on the stack?
It is not that simple.
A C-style array is just a memory address. That's it. It doesn't carry around the other useful information that an object might, such as number of elements, retain count.
If you declare an array like this:
int array[100];
Then the memory is allocated in either the stack or the heap, depending on where you put the declaration. If it's a local variable inside a function or method, it's on the stack. If it's in global scope or a member variable of an object, it's on the heap.
Furthermore, if it's an instance variable, you're actually setting aside 100 ints worth of memory inside the block of memory allocated to hold the object. It isn't a separate thing.
Since array is just a memory address, you are basically passing it around by reference. Technically, you are passing the address by value, but any changes you make to the memory will be seen by anyone looking at the same address, so it acts like pass by reference.
also, shouldn't my parent instance still be around? i thought if i put a C Array as an ivar of my parent, it shouldn't get destroyed
The way you have coded it, that array will be valid as long as the parent object is around. Once the parent gets deallocated, that memory could be reclaimed. Since the array variable is just a memory address, however, you have no way of knowing whether the data it points to is valid or not. This is the danger of using C-style arrays rather than objects.
Since the last line is giving you NULL (0) address, my guess is that [self parent] is nil. That would put a 0 in arr; when you try to dereference NULL, you will get an exception.
In Objective C, you can use property for this.
#define kNumOfElements 10
#interface ParentLayer: NSObject
{
int *array;
}
#property(nonatomic, assign) int *array;
#end
#implementation ParentLayer
-(id)init{
//...
self.array =(int*)malloc(sizeof(int) * kNumOfElements);
for(int i=0;i<kNumOfElements;i++){
self.array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
//-(int *)getArray{
// return array;
//}
-(void)dealloc
{
if(self.array)
{
free(self.array); self.array = NULL;
}
[super dealloc];
}
#end
-(void)initializeValues{
ParentLayer *player = (ParentLayer *)[self parent] ;
int *arr = player.array;
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
can't seem to add a reply to benzado's post. but depending on how to declare your object, it might be automatically deallocated. to ensure that it is retained, use a retain keyword.
[obj retain];
especially using the cocos2d framework, they have quite a number of auto release objects. typically initWith shouldn't be auto release.
In objective c, suppose I have an object Obj stored in a NSMutableArray, and the array's pointer to it is the only strong pointer to Obj in the entire program. Now suppose I call a method on Obj and I run this method in another thread. In this method, if Obj sets the pointer for itself equal to nil will it essentially delete itself? (Because there will be no more strong pointers left) I suspect the answer is no, but why? If this does work, is it bad coding practice (I assume its not good coding, but is it actually bad?)
It is highly unlikely that an object would be in a position to cause its own release/deallocation if your code is designed properly. So yes, the situation you describe is indicative of bad coding practice, and can in fact cause the program to crash. Here is an example:
#interface Widget : NSObject
#property (retain) NSMutableArray *array;
#end
#implementation Widget
#synthesize array;
- (id)init
{
self = [super init];
if(self) {
array = [[NSMutableArray alloc] init];
[array addObject:self];
}
return self;
}
- (void)dealloc
{
NSLog(#"Deallocating!");
[array release];
[super dealloc];
}
- (void)removeSelf
{
NSLog(#"%d", [array count]);
[array removeObject:self];
NSLog(#"%d", [array count]);
}
#end
and then this code is in another class:
Widget *myWidget = [[Widget alloc] init];
[myWidget release]; // WHOOPS!
[myWidget removeSelf];
The second call to NSLog in removeSelf will cause an EXC_BAD_ACCESS due to the fact that array has been deallocated at that point and can't have methods called on it.
There are at least a couple mistakes here. The one that ultimately causes the crash is the fact that whatever class is creating and using the myWidget object releases it before it is finished using it (to call removeSelf). Without this mistake, the code would run fine. However, MyWidget shouldn't have an instance variable that creates a strong reference to itself in the first place, as this creates a retain cycle. If someone tried to release myWidget without first calling removeSelf, nothing would be deallocated and you'd probably have a memory leak.
If your back-pointer is weak (which it should be since a class should never try to own it's owner, you will end up with a retain-cycle) and you remove the strong pointer from the array the object will be removed from the heap. No strong pointers = removed from memory.
You can always test this.
If you need a class to bring to a situation where its deleted, the best practice is to first retain/autorelease it and then make the situation happen. In this case the class won't be deleted in a middle of its method, but only afterwards.
I think we can say it might be bad coding practice, depending on how you do it. There are ways you could arrange to do it safely, or probably safely.
So let's assume we have a global:
NSMutableArray *GlobalStore;
One approach is to remove yourself as your final action:
- (void) someMethod
{
...
[GlobalStore removeObject:self];
}
As this is the final action there should be no future uses of self and all should be well, probably...
Other options include scheduling the removal with a time delay of 0 - which means it will fire next time around the run loop (only works of course if you have a run loop, which in a thread you may not). This should always be safe.
You can also have an object keep a reference to itself, which produces a cycle and so will keep it alive. When its ready to die it can nil out its own reference, if there are no other references and that is a final action (or a scheduled action by another object) then the object is dead.