 |
 |
The Dark Side of lazy methods, or Can anybody work around this?
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Norfolk, Va
Status:
Offline
|
|
This is very easy to test for yourself, if you'd like to see. Just create a project for this code and run it.
void main()
{
int i;
NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (i = 0; i < 5; i++) {
[item setObject:[[NSString alloc] initWithFormat:@"Item %d",i] forKey:@"num"];
[array addObject:item];
NSLog([array description]);
}
}
The output of this fine code is not an array dictionaries containing the numbers 0 through 4 for keys "num", nope! It's an array of {4,4,4,4,4} for those keys.
This behavior indicates to me that 'item' is literally setting 'i' itself, not the number represented by 'i', for it's key, and that array is adding the dictionary 'item' itself, not the dictionary represented by 'item', to itself. This "lazy implementing" I can see is efficient for execution, but clearly its terminal in a program.
So could someone please help me find out how I can add the values of these variables to an array?
I mean, why would I want an array of {item, item, item, item, item}? I need these variables reduced to their values immediately upon execution, so that it becomes {0,1,2,3,4}
Thanks.
|
|
you are not your signature
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status:
Offline
|
|
Here's what's happening (and it's the correct behaviour). When you call [item setObject: forKey:], you're using the same key every time. NSDictionaries contain key/value pairs, so when you setObject: for a key that already exists, the existing object is released and replaced by the new object.
So, every time through the loop, you end up with an NSDictionary that has a single key/value pair of "num"/i.
When you addObject: to the NSArray, you're doing just what the selector says - you're adding the object. So, you get an array that has 5 references to the same object. You're not creating a new object - you're putting a reference to the existing NSDictionary into the NSArray.
So, you wind up with 5 copies of the same dictionary, and the last time through the loop, the dictionary's contents are "num"/4.
If, for example, you wanted a distinct object each time, you could do something like [array addObject: [[item copy] autorelease]].
Here's an untested version that will do what you want:
Code:
const unsigned kArrayEntries = 5;
unsigned i;
NSMutableArray *array = [NSMutableArray arrayWithCapacity: kArrayEntries];
for (i = 0; i < kArrayEntries; ++i) {
NSNumber *number = [NSNumber numberWithInt: i];
[array addObject: number];
}
NSLog([array description]);
|
Geekspiff - generating spiffdiddlee software since before you began paying attention.
|
| |
|
|
|
 |
|
 |
|
Dedicated MacNNer
Join Date: Nov 2000
Status:
Offline
|
|
Pointers 1, Gametes 0.
For bonus mayhem you could add a line like this.
That was a very good reply smeger; however, I noticed a minor flaw.
should have read: you're adding a reference to the object.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Norfolk, Va
Status:
Offline
|
|
Smeger, thanks for your help. I understand that the object is what's being added to the array; in fact, that's my problem. How do I add the value of the object to my array, without creating a new variable every go through? You can reference the address of a pointer with "&var", can I access the contents with something?
So, you get an array that has 5 references to the same object. You're not creating a new object - you're putting a reference to the existing NSDictionary into the NSArray.
Exactly. You seem to know what all this is; how do I not add a reference, but the Dictionary?
Fo the moment, this seems to be working:
Putting the variable declaration in the for-loop, and then releasing the object at the end of the loop at each iteration. I imagine it's incredibly inefficient, but that's C's fault, not mine!
Any suggestions?
Thanks!
|
|
you are not your signature
|
| |
|
|
|
 |
|
 |
|
Professional Poster
Join Date: Apr 2001
Location: Long Beach, CA
Status:
Offline
|
|
A pointer is a reference. If you are wanting to add a different object each time through the loop, you need to allocate a new object each time through the loop. Just watch out for memory leaks. 
|

ACSA 10.4/10.3, ACTC 10.3, ACHDS 10.3
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: May 2002
Status:
Offline
|
|
Originally posted by Gametes:
Exactly. You seem to know what all this is; how do I not add a reference, but the Dictionary?
You don't. Objects in Objective-C, or at least in Cocoa, are always passed by reference. NSArrays hold pointers, not things-that-are-not-pointers. Your workaround is correct: For the array to hold differing dictionaries, you must create five different ones.
|
|
[vash:~] banana% killall killall
Terminated
|
| |
|
|
|
 |
|
 |
|
Dedicated MacNNer
Join Date: Nov 2000
Status:
Offline
|
|
You can reference the address of a pointer with "&var", can I access the contents with something?
It's called dereferencing a pointer.
Code:
void foo(void)
{
int a = 42;
int *b;
b = &a;
// Dereference b. Notice the '*'
*b = *b + 20;
// a now equals 62
}
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Originally posted by Gametes:
This "lazy implementing" I can see is efficient for execution, but clearly its terminal in a program.
...
Thanks.
BTW, Lazy Initializaton is not what you are observing here - lazy initialization is when an object does not allocate resources until they are needed, E.G.:
Code:
class Socket
{
private Object _socket;
// constructor does nothing because
// the socket is not needed yet
public Socket()
{
}
// This method actually creates
// the resource when it is called.
// I.E., not until it is needed.
public Object getSocket()
{
if (_socket == null)
{
_socket = new SocketThingy();
}
return _socket;
}
}
This is called lazy because to allocate the resource when the object is first created might be overkill - the getSocket() method might not be called for a long time, if ever.
What you are observing is the phenomenon known as "Pass By Reference" - and the world would be a very poor place if it did not work that way.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Norfolk, Va
Status:
Offline
|
|
Ok... I've been reading tutorials on pointers, but I can't figure out how to make this code not crash on the last line. It boggles my mind, as it's almost exactly like Apple's sample NSScanner code.
Code:
NSString *link;
NSScanner *siteScanner = [NSScanner scannerWithString:@"<My link>"];
NSScanner *linkScanner;
while ( [siteScanner isAtEnd] == NO ) {
[siteScanner scanString:@"<" intoString:NULL];
[siteScanner scanString:@">" intoString:&link];
linkScanner = [NSScanner scannerWithString:link];
For whatever reason, I get an EXC_BAD_ACCESS error (memory could not be accessed) on the last line.
If I allocinit link, it works but is blank.
Why can't scanner just take regular arguments like everything else!?
|
|
you are not your signature
|
| |
|
|
|
 |
|
 |
|
Forum Regular
Join Date: Aug 2000
Location: UK
Status:
Offline
|
|
Originally posted by Gametes:
Code:
[siteScanner scanString:@">" intoString:&link];
linkScanner = [NSScanner scannerWithString:link];
For whatever reason, I get an EXC_BAD_ACCESS error (memory could not be accessed) on the last line.
If I allocinit link, it works but is blank.
Why can't scanner just take regular arguments like everything else!?
When this block of code starts, the value of link is undefined (usually NULL). When you pass a pointer to link into scanString:intoString: it is perfectly acceptable for scanString:intoString: to leave it as NULL, when it failed to scan properly. Then, you are trying to create a new scanner with the link string without checking to see if the link string is NULL.
Try something like this:
Code:
[siteScanner scanString:@">" intoString:&link];
if (link != NULL)
{
linkScanner = [NSScanner scannerWithString:link];
}
The reason why it works when you allocate link yourself is because scanString:intoString: is failing and leaving link untouched (i.e. properly allocated and not NULL).
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Norfolk, Va
Status:
Offline
|
|
running this code in the debugger shows the value of link as "Invalid", however, not nil as I would expect in the scenario you describe.
And if you're suggesting that the scanner is scanning anything, that wouldn't happen in the example above: the scanner is scanning "<my link>" up to the character ">"; that's not nil.
I don't think that's it. If it is, how can I fix the problem? Any other ideas, guys?
|
|
you are not your signature
|
| |
|
|
|
 |
|
 |
|
Junior Member
Join Date: Mar 2002
Location: Maryland
Status:
Offline
|
|
What you want is:
Code:
[siteScanner scanString:@"<" intoString:NULL];
[siteScanner scanUpToString:@">" intoString:&link];
The 'scanString' method scans the passed string if it is located at the current scanner location.
So if finds your <, and then moved one character to the right.
Scan up to string will move along the string until it find your string
If you want to scan 'past' the >, you will need an additional
Code:
[siteScanner scanString:@">" intoString:NULL];
Also keep in mind that those suckers return the # of characters scanned. So if they're returning 0, the value of link will be invalid. It's also a good idea to initialize link with nil, and check it against nil before actually trying to use it.
|
|
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: Mar 2000
Location: London, UK
Status:
Offline
|
|
Anybody else notice some odd behaviour with NSScanner stripping spaces in 10.2?
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Norfolk, Va
Status:
Offline
|
|
Thanks ResImadA, that is exactly it. I can't belevie, for the 15 times I read that method's definition, my brain kept reading what I wanted it to say. Weird.
There ought to be a "scanUntilAfter" method.
|
|
you are not your signature
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: Mar 2000
Location: London, UK
Status:
Offline
|
|
I added a scanUpToAndIncludingString method as a category of NSScanner.
Code:
@implementation NSScanner(CSWAdditions)
/*" Additions to make parsing easier. "*/
/*"
* Scans up to and past string, except if string immediately follows the current position, in which
* case it just scans past it.
"*/
- (BOOL)scanUpToAndIncludingString:(NSString *)string {
if ([self scanString:string intoString:NULL]) {
return YES;
} else {
if ([self scanUpToString:string intoString:NULL]) {
if ([self scanString:string intoString:NULL]) {
return YES;
}
}
}
return NO;
}
@end
|
|
|
| |
|
|
|
 |
 |
|
 |
|
|
|
|
|

|
|
 |
Forum Rules
|
 |
 |
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
|
HTML code is Off
|
|
|
|
|
|
 |
 |
 |
 |
|
 |
|