Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.
Second thing to know is that the keyword static makes a variable into what is basically an instance variable for that single method.

I disagree with calling this an "instance variable". It implies this is stored in an object, which it definitely isn't. It's stored in a single static variable that is shared by all callers of this function, across all threads. It's exactly the same as this:
Code:
static NSMutableArray *storage = nil;
- (NSArray *) allSubviews{
     BOOL isFirstCall = NO;
...
except that when it's inside the method body's block, its name is only visible to that block. Outside the block, the name is inaccessible to all other code.

In the above code, the name is visible outside the block (i.e. to other blocks or initializers in the same compilation unit), but the lifetime of the variable is absolutely identical to the "static inside the block" declaration. Even the initializer to nil is identical: it's done exactly once, conceptually when the program is loaded, before main() is called. It's not done each time the method/function is entered.

At no time is 'storage' ever an instance variable. If anything, it's a shared singleton.

One consequence of static storage is in concurrency: this code is not thread-safe. Re-entrancy is another concern, although here the function/method is re-entrant (it calls itself), and the singularity of the static storage is being relied upon by the re-entrant calls. In short, the variable has to be static because that's the only way multiple calls can use the same variable without passing it in.

To eliminate the static storage entirely would make this thread-safe. I'd probably do it by making two methods, or a method and a static function. One method (or the static function) is the recursive one with void return, and modifies its arg-array. The other method is the public one identical in name and args to allSubviews.
 
Obviously you are correct but I want to address this a little more because I believe the question is mostly related to my new method of doing this.

Second thing to know is that the keyword static makes a variable into what is basically an instance variable for that single method*. Every time the method is called it has access to those static declared pieces of memory.

Ironically, my question was directed in general, and not to your method, however this post was very educational. Let me just say it back to you, to see if I get it right. When you did this;

Code:
     static NSMutableArray *storage = nil;

That same instance is going to be used by the instances of the method that are called from within that method?
 
Ironically, my question was directed in general, and not to your method, however this post was very educational. Let me just say it back to you, to see if I get it right. When you did this;

Code:
     static NSMutableArray *storage = nil;

That same instance is going to be used by the instances of the method that are called from within that method?

Not quite. There is exactly one storage that is available within allSubviews whenever it is called, not just in recursive cases. This is, essentially, making a global variable called storage as chown33 said. It just so happens that you can only get to it from allSubviews. The memory for storage (the actual pointer primitive, not what it points to) is on the heap and lives throughout the program. This is distinct from a "regular" (automatic) variable that lives on the stack and "goes away" when the stack frame is popped at the end of the function's execution.

-Lee
 
Ok, so a quick rundown of everything I've learned in this thread.

1. Recursive methods are possible, as long as you prevent an infinite loop.

2. "Static" declarations within a method create an instance variable that will be used by every instance of that method called within the lifetime of the application.

3. Non-"static" variables declared within a method will not automatically be released upon the end of said method.

Correct?
 
Ok, so a quick rundown of everything I've learned in this thread.

1. Recursive methods are possible, as long as you prevent an infinite loop.

2. "Static" declarations within a method create an instance variable that will be used by every instance of that method called within the lifetime of the application.

3. Non-"static" variables declared within a method will not automatically be released upon the end of said method.

Correct?

Some terminology may have gotten mixed up.
1. is correct.
2. Don't say instance variable. jared_kipe was trying to be helpful and relate this to something you may already know, but it's probably better not to mix nomenclature as chown33 pointed out. The rest of what you said is correct. Instead of saying "instance of that method" it might be clearer to think of "every time the method is called".
3. I'm not sure what you mean, but i don't think this is right. Objects live on the heap, they are not tied to any particular method. You might have a pointer to an object that is local to a method, but that is not the object. Primitives (char, int, float, double, any pointer, etc.) declared within a method that have the "automatic" storage class (that is the default if you don't specify) will be allocated on the stack when the method is called. When the method completes, the stack frame is "popped" so the space for these variables is no longer being used. If you're talking about Objects in a non-GC environment, the only time release will happen is if you send the message yourself when you own it, or if you have an autoreleased object and the autorelease pool is drained.

-Lee
 
Ok, let me try again on the last one then. The pointer to the object, if declared within a method, will stop existing after the method ends. For example;

Code:
-(void)foo{
	NSString *test = [[NSString alloc] initWithFormat:@"This is a test"];
	NSLog (@"%@", test);
}

Would the pointer *test cease to exist at the end of this method, leaving the object with a 0 retain count, or would it still be necessary to put [test release] at the end of this code?
 
I disagree with calling this an "instance variable". It implies this is stored in an object, which it definitely isn't. It's stored in a single static variable that is shared by all callers of this function, across all threads. It's exactly the same as this:
Code:
static NSMutableArray *storage = nil;
- (NSArray *) allSubviews{
     BOOL isFirstCall = NO;
...
except that when it's inside the method body's block, its name is only visible to that block. Outside the block, the name is inaccessible to all other code.

I disagree with calling this "exactly the same as this: <code>...</code>", since it isn't the same at all! You're splitting hairs, I'm splitting hairs.

I said "basically an instance variable for that single method." I didn't say "exactly an instance variable" or "is an instance variable".

I was merely relating one concept the OP is probably familiar with another.

...
One consequence of static storage is in concurrency: this code is not thread-safe. Re-entrancy is another concern, although here the function/method is re-entrant (it calls itself), and the singularity of the static storage is being relied upon by the re-entrant calls. In short, the variable has to be static because that's the only way multiple calls can use the same variable without passing it in.

To eliminate the static storage entirely would make this thread-safe. I'd probably do it by making two methods, or a method and a static function. One method (or the static function) is the recursive one with void return, and modifies its arg-array. The other method is the public one identical in name and args to allSubviews.


We could argue over thread safety here.

Firstly, it wasn't a condition of the OP's question, nor did I ever promise it.

Secondly, its only a minor thread "safety" issue. Worst case scenario, subviews that aren't actually subviews get returned if two threads try to use the method at the same time (or duplicate views). It can't create an infinite loop because at no time did I actually look at what is inside the NSMutableArray. And since it is only ever an NSMutableArray OR nil, it is safe to message it if it is in the wrong state. So what would probably happen is one thread would get more than it bargained for (namely some weird subviews) and the other thread would get nil back from its initial calling. (this of course relies on NSMutableArray being thread safe which I hope it is!)

I should point out that actual ObjC instance variables are not inherently thread safe either!


If thread safety is an issue, then I'd suggest two category methods as such:

Code:
- (NSArray *) allSubviews{
     NSMutableArray *storage = [[NSMutableArray alloc] init];
     [self allSubviewsIntoMutableArray: storage];
     return [storage autorelease];
}

- (void) allSubviewsIntoMutableArray: (NSMutableArray *)storage {
     NSArray *subviews = [self subviews];
     [storage addObjectsFromArray: subviews];

     for (NSView *view in subviews) {
          [view allSubviews: storage];
     }
}

I probably would have done it this way in the first place (in actual practice), but decided to challenge myself a little and write it all in one method without needing storage passed into it as per the OP's request/the way the thread was going.



Also I would appreciate it, chown33, if you would never reply to my threads or posts. LOL JK, I hope you get the hilarious joke I make ;)
 
2. "Static" declarations within a method create an instance variable that will be used by every instance of that method called within the lifetime of the application.
...

Jesus, I owe you a cookie chown.

static variables in a method or function are kinda like class variables if you think of the function itself as the class.. I should stop...
 
Ok, let me try again on the last one then. The pointer to the object, if declared within a method, will stop existing after the method ends. For example;

Code:
-(void)foo{
	NSString *test = [[NSString alloc] initWithFormat:@"This is a test"];
	NSLog (@"%@", test);
}

Would the pointer *test cease to exist at the end of this method, leaving the object with a 0 retain count, or would it still be necessary to put [test release] at the end of this code?
the pointer to that string will cease to exist. The object itself will still be in the heap with a retain count of 1, the object doesn't know that the pointer went away.

Now technically, in a garbage collected environment it wouldn't go away right away but it would eventually. Keep in mind that if you have something... like
this and you want to indicate to the collector that you don't need it anymore you can say test = nil; this is due to the details of how garbage collection works internally (it still won't go away immediately).
 
Also, in GCC it should be possible to fix the original function by changing the declaration of static NSMutableArray *storage = nil; to static __thread NSMutableArray *storage = nil;

Making it a thread specifically allocated static local variable.
 
I disagree with calling this "exactly the same as this: <code>...</code>", since it isn't the same at all! You're splitting hairs, I'm splitting hairs.
Except my example really is exactly the same except for the scope of the name. If you look at the generated code, it should be exactly the same except the variable's name is visible to the rest of the compilation unit. A static declared inside a function, however, has a unique name automatically generated for it, and that name is used exclusively inside the function. If the same static name is reused inside another function, then another unique name is generated. Other than that, there should be no differences in the code or the alloted static storage.

I said "basically an instance variable for that single method." I didn't say "exactly an instance variable" or "is an instance variable".

I was merely relating one concept the OP is probably familiar with another.
Unfortunately, there are limits to the power of analogy, and "instance variable" has a pretty specific meaning. And I think it was a poor analogy in the first place. It's like the radio in a car... No, it's like the ears on a horse... No, it's like the wings on a flying fish... No, the radio-controlled ears on a flying horse... with static.


We could argue over thread safety here.
We could, but I won't. It wasn't an original requirement.


Also I would appreciate it, chown33, if you would never reply to my threads or posts. LOL JK, I hope you get the hilarious joke I make ;)
And I would appreciate it, jared_kipe, if I could refrain from having to respond to any of your threads or posts. I find that doing so often makes things go off-track. (I could put a smiley there, but this week's shipment is overdue and I'm over my quota elsewhere.)
 
jared_kipe covered it earlier, but I thought I'd try to hammer it home:
With retain/release a pointer going out of scope has nothing to do with your ownership of an object. If you own an object, and you no longer have a pointer to it, you can no longer relinquish ownership, so the object will live forever and you have leaked the memory associated with the object. No magic happens IRT an object when a variable that points to it changes. The only time you claim ownership or relinquish ownership is by sending retain/release/autorelease messages.

Don't think about retain count or what changes it. Think about ownership. If you are done with an object you own but fail to relinquish ownership, that's bad. If you already own something and claim ownership again, bad. Relinquish ownership of something you don't own, bad. Use something you don't own and hope it works, bad.

-Lee
 
Ok, I never really thought of it as ownership. None of the educational material I've read so far covers it in terms of ownership. Do you know if there's any Apple reference docs that explain it that way?
 
Ok, I never really thought of it as ownership. None of the educational material I've read so far covers it in terms of ownership. Do you know if there's any Apple reference docs that explain it that way?

That's too bad. The Memory Management Programming Guide:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
explains it that way, and is the resource for this topic. The second section is Object Ownership and Disposal:
http://developer.apple.com/library/...ip.html#//apple_ref/doc/uid/20000043-BEHDEDDB

Gaming retain counts is no way to live. Has the material you've read so far dealt with memory management or does it all assume GC? What are you reading?

-Lee
 
Ok, I never really thought of it as ownership. None of the educational material I've read so far covers it in terms of ownership. Do you know if there's any Apple reference docs that explain it that way?

It is sometimes described as a dog and leash metaphor.

The dog will only stay around if there is at least one leash on it. When you buy the dog you automatically leash up to it. You can have friends come along and walk with you and leash to the dog. And those friends -and you- can leave the dog alone at any point by unleashing yourself from the dog.

Where there are no more leashes the dog will run away. There can be any number of leashes on a dog at a time, but there needs to be at least one to keep the dog there.

The number of leashes is the retain count, leashing and unleashing is retaining and releasing.
 
It is sometimes described as a dog and leash metaphor.

The dog will only stay around if there is at least one leash on it. When you buy the dog you automatically leash up to it. You can have friends come along and walk with you and leash to the dog. And those friends -and you- can leave the dog alone at any point by unleashing yourself from the dog.

Where there are no more leashes the dog will run away. There can be any number of leashes on a dog at a time, but there needs to be at least one to keep the dog there.

The number of leashes is the retain count, leashing and unleashing is retaining and releasing.

So autorelease is attaching a leash to a bench that will dissolve at an unknown time in the future?

And if you forget to attach a leash but know where you left the dog, you might find a rhino or pocketwatch when you come back?

How does your backyard fit into this metaphor? Does that count as a virtual leash?

-Lee
 
So autorelease is attaching a leash to a bench that will dissolve at an unknown time in the future?

And if you forget to attach a leash but know where you left the dog, you might find a rhino or pocketwatch when you come back?

How does your backyard fit into this metaphor? Does that count as a virtual leash?

-Lee

I was thinking, autorelease was like dropping your leash so that someone else could pick it up for you. If enough time goes by the dog will figure out that the leash isn't actually holding him there and run away.

If you unleash from the dog but stay in sight of it, the dog will probably disappear and be replaced by some other dog. The new dog will probably not respond to the old dog's name or know the old dog's tricks.

...
On second thought, maybe autorelease is like putting your dog into a cage and walking away. Someone else could come along and leash up the dog, but if nobody comes around soon enough, the dog will die .. I mean run away to your uncles farm upstate.
 
On second thought, maybe autorelease is like putting your dog into a cage and walking away. Someone else could come along and leash up the dog, but if nobody comes around soon enough, the dog will die .. I mean run away to your uncles farm upstate.

Like taking the dog to the pound: they will keep the dog there for a few days, and you can check on it during that time, but eventually it will no longer be there.

Except dogs are kind of a flawed metaphor: chocolate (as in "Cocoa") is toxic to dogs.
 
That's too bad. The Memory Management Programming Guide:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
explains it that way, and is the resource for this topic. The second section is Object Ownership and Disposal:
http://developer.apple.com/library/...ip.html#//apple_ref/doc/uid/20000043-BEHDEDDB

Gaming retain counts is no way to live. Has the material you've read so far dealt with memory management or does it all assume GC? What are you reading?

-Lee

I started with Hillegaas' book. It shows code with the retain and release commands, but only has a very short chapter talking about memory, and as I recall, I don't remember "ownership" coming up in it.
 
I started with Hillegaas' book. It shows code with the retain and release commands, but only has a very short chapter talking about memory, and as I recall, I don't remember "ownership" coming up in it.

That's pretty unfortunate to hear, especially considering that book is recommended so often. In any event, the Memory Management Programming Guide should be all you need if you choose to learn retain/release memory management. If you're more interested in exploring other things right now and want to stick to GC for the time being, that's understandable. I would recommend doubling back at some point to learn proper memory management in case you ever need it.

-Lee
 
Hey guys, just in case you're interested, I just realized that we all missed a biggie on this one. Apparently, an NSTabView only considers the view in the currently selected tab as a subView. I had to add an if to grab the views from all the tabs in a tabView.
 
Hey guys, just in case you're interested, I just realized that we all missed a biggie on this one. Apparently, an NSTabView only considers the view in the currently selected tab as a subView. I had to add an if to grab the views from all the tabs in a tabView.

I didn't miss anything.

If only the current selected tab is a subView than only that tab is a subView and should be returned. Plain and simple. One of the reasons for this is to utilize lazy loading.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.