开发者

iPhone - initialising variables using self

So, let's say you have a local variabl开发者_开发问答e NSArray *myArray declared in your class header file.

You then write @property (nonatomic, retain) NSArray *myArray also in your header file.

In your .m file, you write @synthesize myArray.

All very standard so far. You now have a variable myArray, which can be accessed through setters and getters synthesized by Apple.

A little bit later, you initialise your variable.

NSArray *anArray = [[NSArray alloc] initWithObjects etc etc...];
self.myArray = anArray;
[anArray release];

So now myArray is pointing to an array in memory, which has a release count of one (if I'm not mistaken).

My question is, why can't we write

@property (nonatomic, assign) NSArray *myArray;
@synthesize myArray;

..and then by initialisation write

self.myArray = [[NSArray alloc] initWithObjects etc etc...];

This has TOTALLY confused me ever since the first time I saw it. Is there a technical reason for this? Or moral? ;-) Or theoretical?

Any help would be MUCH appreciated...

Cheers

Karl...


One of the points of properties is to ease us from having to think about memory management ourselves. Making the property assign and then assigning a retained object into it kind of defeats the purpose of using the property.

It's really simple to do:

@property (nonatomic, retain) NSArray * myArray;
@synthesize myArray;

self.myArray = [NSArray arrayWithObjects:etc, etc1, etc2, nil];

And then all the memory management is taken care of for you.


You can.

I mean, it's what I'm doing in my program because I don't like using retain property ^^

It doesn't work ? what is the error ?

By the way you can just write

myArray = [[NSArray alloc] initWithObjects etc etc...];


You can write:

self.myArray = [[[NSArray alloc] initWithObjects etc etc...] autorelease];

(note the addition of the autorelease)

Though it would be simpler to write:

self.myArray = [NSArray arrayWithObjects etc etc...];

Purists might argue that you shouldn't put things into an autorelease pool unless you really need to, however if it makes your code simpler I say go for it, the performance overhead is negligible in many/most cases.

If you use an assign property instead, you need to make sure you release the old contents of myArray yourself, which negates much of the advantage and simplicity.


Memory management in Cocoa (and Cocoa Touch) is very strongly based on conventions. One of those conventions is that objects take ownership of other objects they need to keep around, which means that they must properly retain (to claim ownership) and release (to relinquish ownership) those objects. If you make it an assign property and require every caller to handle the memory for you, this violates the memory management conventions.

It's also poor program design, because rather than have one place (the setter) that is concerned with managing that property, instead you spread the responsibility to every place that accesses the property. Clear separation of concerns is one of the most important aspects of design.

In short: You can do it the way you're asking about. It's just worse in every respect. It violates the assumptions Cocoa makes, it makes bugs more likely, it complicates your design and it bloats your code.

However, in cases where you're setting properties of self, you can do something like what you want. Instead of writing self.someProperty = [[NSString alloc] initWithString:@"Foo"], you can just write someProperty = [[NSString alloc] initWithString:@"Foo"] (assuming someProperty is the underlying instance variable). This is, in fact, the normal way to do it in an initializer method or a dealloc method. This allows you to simply assign the variable in the internal implementation of your class without requiring everybody who uses the class to do the class's memory management for it.


The short answer is that using assign will probably result in memory leaks. Unless you're very careful.

By declaring the array property as retain, you are indicating that the object should take ownership of the array by sending it a retain message and, more importantly, that it should send it a release message when it is no longer interested in keeping the array around. When you use assign, the object won't send the array any retain or release messages. So, in the example you give, there isn't a problem YET. You've created an array with a retain count of one (conceptually) and given it to your object. In this case, the array hangs around in memory with a retain count of 1 just as it would have if you'd used the retain attribute when declaring the property.

The problem comes when you want to change the value of myArray. If your property is declared with retain, an assignment will do something like this:

- (void)setMyArray:(NSArray *)newArray {
    if (myArray != newArray) {
        [myArray release];    // Old value gets released
        myArray = [newValue retain];
    }
}

The old myArray gets sent a release message indicating that the object is done with it. If the retain count of myArray drops to zero, it will get deallocated and its memory reclaimed. If the property is declared with assign, this basically happens:

- (void)setMyArray:(NSArray *)newArray {
    myArray = newArray;
}

The object forgets about the array at myArray without sending it a release message. Therefore, the array previously referred to by myArray probably won't get deallocated.

So, it's not the assignment that's a problem. It is the failure to release the array during reassignment that will cause the memory leak. This might not be a problem if another object owns the array.

If another object owns the array, and the array is just being referenced by myArray, that other object is in charge of making sure the array stays around as long as myArray needs it and of releasing the array when it's no longer needed. This is the pattern typically used for delegates. You then have to be careful that you don't access myArray after that other object has released the array it references.

Essentially, this comes down to the question of who owns the array referenced by myArray. If another object owns it and will handle retaining and releasing it as needed, it's perfectly okay for your object to simply reference it. However, if your object is the owner of myArray (and will be releasing it in dealloc), it makes more sense to use the retain attribute. Otherwise, in order to avoid leaks, you'll require other objects to release the contents of myArray prior to calling your object's setter, since your assign setter won't do it for you.


You definitely can.

Using "assign" properties instead of "retain" properties is actually a common practice (see some core object header files from Apple for examples). The issue here is your code being aware of this memory relationship (if the property has something in it at any given time).

Some programmers prefer this pattern, in fact. Complete personal control of memory.

I would add, however, that it is a very difficult pattern to protect when there are multiple developers on a project unless they are all the types that like manually managing memory. It's much easier to leak memory in this pattern from a simple oversight and compilers have a tougher time interrogating such problems.


There is no reason why you can't do that. You just have to pay some extra attention to your memory.

Because what happens when you later assign to the property again?

Using your example:

@property (nonatomic, assign) NSArray *myArray;
@synthesize myArray;

...

self.myArray = [[NSArray alloc] initWithObjects: @"foo", nil];
self.myArray = [[NSArray alloc] initWithObjects: @"bar", nil]; // MEMORY LEAK!

In this case you would have to manually release your ivar by calling release on it. If you do not, you will have leaked the memory.

Another smart thing about having it retained (or copied, less bug prone) it that you can say:

self.myArray = nil;

This will release the variable AND set the reference to nil, so you avoid getting yourself into trouble.

I absolutely see your point though. It is alot more verbose to have to write 3 lines instead of one. You can as @willcodejavaforfood suggests use autorelease when you are assigning to retained properties, as he seems to have missed). But Apple suggests that on the iPhone you do as little autoreleasing as you can, and we always listen to apple like good little children.

Update:

When you specify a property as (nonatomic, assign) an synthesize it the setter code that is generated looks something like this:

- (void)setMyArray:(NSArray *)newValue {
    myArray = newValue;
}

If you on the other hand define it as (nonatomic, retain) you get:

- (void)setMyArray:(NSArray *)newValue {
    if (myArray != newValue) {
        [myArray release];
        myArray = [newValue retain];
    }
}

Hope it clears things up.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜