Objective-C memory management, xml parser and other non-trivial examples

前端 未结 6 1601
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-04 18:00

I know the basic principles about memory management (retain count, autorelease pools etc) in Cocoa, but once you go past simple retain/release, it\'s getting a bit more confusin

6条回答
  •  爱一瞬间的悲伤
    2021-02-04 18:42

    Thanks to all for answers. Those answers helped, though at few places only, the rest I had to figure out myself. I was much more focusing on the idea, not line-by-line implementation, but it was hard to describe here without pasting huge chunks of code. It's just that delegate for xml object parsing is a very specific example, because it doesn't return value per se, the value has to be taken and assigned externally.

    I'm marking Adam's answer as best one, as being most-detailed, even though not answering all my problems.

    For others - http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html is an excellent read. Also read my own answers to questions :

    1 - Of course I shouldn't release that object. The memory leak wasn't caused by this.

    2 - This method is nothing special, it just returns plain autoreleased object, as others tried to explain to me here. The root of my initial troubles was that I previously didn't have that retain, but called [model release] soon after, causing autorelease pool sending release to non-existant object. I shouldn't do [model release] because I'm not the owner of that object. In fact, this retain here isn't even needed, because I just need the object to get value from it, and then I can thrash it, so it can be safely passed to autorelease pool, without retain.

    3 - I intended this method (loadData) to be independant, therefore not setting any instance variables, but returning array for others. It was a parallel example, it's not that I have two variables with the same name in method.

    If I declare object within this method (situation #2), then just so it happens that it's autoreleased at the end of this method, because after it finishes, the control goes back to application and release pool kicks in. It was ok with me in this example, because I don't need the array later on. In real world I should probably have instance variable (situation #1) and then go with self.objectArray = [self loadData], because this will launch setter and the autoreleased object will be retained for me here.

    4 - I've confused few things here. Basically I was trying to code in objecive-c with manual memory management, but still having "garbage collector" attitude. It's very important to remember that if you do [[object alloc] init] and then at later time [object release] - it doesn't automatically have to be that the object will be destroyed! Release isn't nullifying! This is of course a fundamental rule (retain/release) but even knowing it, it's easy to forget. Track what you are doing with your object between those two lines - the object may actually live for a long long time after, because "someone" else will become its owner. The release method at the end of this class livecycle doesn't mean - object is now destroyed, but means : "I no longer care, my hands are clean"

    So, line by line :

    objectArray = [[parserDelegate objectArray] copy]; 
    

    This is perfectly fine, I don't deep copy. I'm copying an array, which means it allocates new memory for the array object, but not for the contents. BUT, copy sent to objectArray also sends retain to each object. In my example, I'm releasing my parserDelegate which also releases its own objectArray, decreasing retainCount for each object. If I wouldn't do the copy here, objects will reach retainCount = 0 and be destroyed. This way I have new array, with pointers to old objects, but they essentially become my objects, because previous array is destroyed, and because of my retain, I'm becoming the owner. Sorry if this is talking a bit too much, but I really had to focus to understand this.

    else if ([elementName isEqualToString:@"name"]) {
        // do i have to init currentObject.name (NSString) here? i guess not?
        [self setParseChars:YES]; // just set the flag to make parse control easier
    }
    

    The question here was to, whether I should initialize currentObject.name NSString property, because it will be filled soon after foundCharacters kicks in. Now this is interesting. When you initialize whole object, its NSString properties are nil. Now, later, I do

    currentObject.name = currentChars;
    

    Which launches setter. This setter is defined as (nonatomic, retain), which means that new value is retained, old value is released and pointer is assigned. Funny enough, it doesn't matter if the previous value is initialized or is nil - if it is initialized it will be released anyway, if it is nil, then nil can still accept release (though I'm not 100% sure of it?) - nothing will happen. But for the sake of correctness, I guess the initial line should look like :

    else if ([elementName isEqualToString:@"name"]) {
        currentObject.name = [NSString new]; // just alloc/init routine
        [self setParseChars:YES]; // just set the flag to make parse control easier
    }
    

    Now :

    [currentChars autorelease];
    

    Should not be there. This is confusing design and it was a bad fix. All it should be there, is just string init.

    [objectArray addObject:[currentObject copy]];
    

    Copy is unnecessary here. addObject will retain anyway. No point in creating another allocation. This was my one of causes of my leaks.

    Everything else is fine. Because I'm releasing currentChars right after setting the value to my object, he now retains it, and becomes the owner, and I'm just releasing it "here" in parser, because I don't need it anymore (It will be allocated in next loop).

    It might be confusing, but I can imagine, there will be others with non-standard memory assignment problems and even for experienced people it might be a challenge to put things in right places. Maybe my story will help then.

提交回复
热议问题