Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.
Related
I have difficulties understanding naming conventions of ARC. I have always coded with ARC, and I guess this is the reason.
1. Class methods
What name should I choose for the following method?
What are the differences, concerning memory management, between theses two names?
This name:
+ (MyObject *)newObjectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
or this name ?
+ (MyObject *)objectFrom:(MyObject *)anObject
withOptions:(NSDictionary*)options
{
MyObject * newObject = [anObject copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
2. Instance methods
What name should I choose for the following method?
What are the differences, concerning memory management, between theses two names?
This name:
- (MyObject *)newObjectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
or this name?
- (MyObject *)objectwithOptions:(NSDictionary*)options
{
MyObject * newObject = [self copy] ;
[newObject modifyWith:options] ;
return newObject ;
}
2. Simple rules for naming methods
Is there a basic, simple rule to follow when naming methods?
By "basic, simple", I mean
a rule similar to "strong when the object belongs to the class", "weak when the object is just referred to by this class, and (thus) owned by another class";
(and/or) a rule that does not refer to the memory management without ARC;
(and/or) a rule that does not use words such as "autorelease", "release".
When Rivera said that method names are not important, he probably followed his experience: It always works. And this is correct. And you are correct that you probably do not understand the role of method names because you has always used ARC. So what is the big deal with it?
I added a synopsis to show the problem with MRR at the end of this answer. As you can see there, you do not have to care about naming rules using ARC the way you had to with MRR.
To give you a more detailed explanation, you have to understand what happens using MRR:
Prior to ARC one had to do memory management manually. Doing so he had to know, what kind of ownership a return value has. To make a long story short, one had to know, whether he has to release a returned object:
Rule 1: You do not own an object returned by a method automatically. If you want to hold it, retain it and release it, when you are done with it.
id object = [[object methodThatReturnsAnObject] retain]; // I want to hold it
…
[object release]; // Done with it
id object = [object methodThatReturnsAnObject]; // I do not want to hold it
…
// I do not release it
Doing a deep analysis you can see that there are sometimes problems. (Object deallocation as a side effect.) But this was the basic approach.
The advantage of that was that a retain-release pair could be handled locally (inside a compound statement) and it was easy to follow the ownership of an object.
Rule 2: But when an object was created the first time this could not work: The sender of the message has always to hold it. Otherwise it would be destroyed immediately (= before the sender has the chance to retain it.) So there was a additional rule:
If the name of a class method that returns an object starts with alloc, init, or new you have to handle the returned object as you did a retain on it. Or in one word: Ownership transfer:
id object = [Class allocOrInitOrNewMethod];
…
[object release];
As -retain took the ownership explicitly, alloc–, init…, new… transferred it implicitly.
All other methods behaves like rule 1.
Rule 3: Sometime you need an autorelease pool.
To make the advantage of ARPs visible think of this code: (It is useless, just to demonstrate the problem
Case 1.1:
Person *person = [[Person alloc] initWithName:…]; // Ownership transfer, release person, when you are done
NSString *name = [person name]; // the name object is hold by the person object only
[person release]; // I do not need the person object any more
[name doSomething]; // Crash: The person was the only object holding the name
Another problem:
Case 2.1:
Person *person = [[Person alloc] initWithName:…]; // Ownership transfer, release person, when you are done
if (…)
{
return; // break, continue, goto
}
…
[person release];
Because the last line is never reached, the object is never released.
You can repair that with autoreleasing methods. An object moved to the ARP lives as long as the control flow returns to the run loop. So it lives through every method, through method return and so on. To do it explicitly:
Case 1.2:
Person *person = [[[Person alloc] initWithName:…] autorelease]; // No ownership transfer, persons belongs to the ARP
NSString *name = [person name]; // the name object is hold by the person object only
[name doSomething]; // No Crash: The person object holding the name object is still alive
Case 2.2:
Person *person = [[[Person alloc] initWithName:…] autorelease]; // No ownership transfer, prsons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
Because one has to be too lazy to type such a long message chain, convenience allocators has been invented to do this for you:
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
With the result:
Case 2.3:
Person *person = [personWithName:…]; // No ownership transfer, persons belongs to the AR.
if (…)
{
return; // break, continue, goto
}
…
// No release necessary.
And you can do the same with getters:
- (NSString*)name
{
return [[_name retain] autorelease];
}
So we at the end of the day we have simple rules:
Rule 1: If you want to keep a returned object in memory retain it – and release it, when you do not need it any more.
Rule 2: If the class method's name starts with alloc, new, or init, think of it as an implicit retain (and therefore release it, when you are done with the object.)
Rule 3: Use -autorelease to delay the deallocation of returned objects for convenience.
Synopsis:
Alloc-init creation
// MRR:
Person *person = [[Person alloc] initWithName:#"Amin"];
…
[person release]; // You create it, you release it
// ARC:
Person *person = [[Person alloc] initWithName:#"Amin"];
…
New creator
// MRR:
Person *person = [[Person newPersonWithName:#"Amin"];
…
[person release]; // You create it, you release it
// ARC:
Person *person = [[Person newPersonWithName:#"Amin"];
…
Convenience allocator
// MRR:
Person *person = [[Person personWithName:#"Amin"]; // Autoreleased
…
// ARC:
Person *person = [[Person personWithName:#"Amin"];
…
As you can see, there is no difference for the three ways of object creation using ARC. So Riviera is right, when he said that this is not important any more. But under the hood the last way is different, because it moves the object to the ARP.
It is the same with the implementation of this methods:
Implementing a new allocator
// MRR
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
// ARC
+ (Person*)newPersonWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
Implementing a convenience allocator
// MRR
+ (Person*)personWithName:(NSString*)name
{
return [[[self alloc] initWithName:name] autorelease];
}
// ARC
+ (Person*)personWithName:(NSString*)name
{
return [[self alloc] initWithName:name];
}
Again the implementation for both methods is identical using ARC.
-> You do not need it any of this rules any more. ARC cares four you.
Especially you do not need convenience allocators any more, because there is nothing inconvenient any more. (And even they are optimized at run time, there is still a minimal runtime penalty.) I do not implement convenience allocators any more, but new allocators.
But ARC has to be compatible with MRR. So it remembers all that rules. To make your code readable for others you should repeat this rules, too. But, of course, now the implementation of a convenience allocator and a new allocator is bitwise identical – the rest is done by ARC.
Method names are important. The official documentation of how ARC interprets method names can be found in the clang ARC documentation in the section on method families.
Method naming conventions are important when converting the code from MRC to ARC, and when interoperating with MRC code from ARC Code.
Apple guide says that with "ARC code only" the naming conventions are "less important", but this is not 100% true.
For example (and this is only one example, I think that there are many other), look at this project
I swizzled release-autorelease calls to log it, and you can see the difference:
when a method begins with a special naming (for example "new"), ARC returns an object with a retain count of +1, that is balanced by release calls.
When a difference name is used, ARC returns an AUTORELEASED object. (look at the NSLog calls)
This can be a big difference in code that alloc a great number of objects in a loop. In this case, the programmer should create an autorelease pool.
So, it's not true that the naming conventions are less important when using ARC-only code.
I don't know all of the details on naming methods 100% conventionally because there are different situations that apply to each. however I think this article in the Apple docs will help you. https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html
My experience in the apple world is that factory methods like your examples would usually include 'create' by convention. This is probably less important now with ARC but in the old days 'create' in a method or function signature was a pretty sure fire way to be sure you understood you were taking ownership of the resulting object (owing a release/free() on it) rather than an instance type which you would assume to autorelease [NSArray array] , [UIColor colorWith....] etc, so I still definitely like to follow this convention.
ARC gave semantics to what used to be a naming convention for tracking reference counting responsibilities. So now the compiler uses the same naming patterns to determine whether a method returns a retained object, etc. See the link in #JodyHagens' answer and continue reading with http://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics-of-method-families
Methods in the alloc, copy, mutableCopy, and new families — that is, methods in all the currently-defined families except init — implicitly return a retained object as if they were annotated with the ns_returns_retained attribute. This can be overridden by annotating the method with either of the ns_returns_autoreleased or ns_returns_not_retained attributes.
The "new family" is the message selectors that start with "new" followed by an upper case letter. So there are memory management differences between newObjectFrom:withOptions: and objectFrom:withOptions:, but you can use an annotation to override that (don't go there), and the compiler should complain if you get it wrong.
(All methods that implement a given message selector had better provide the same reference counting semantics.)
In addition to Apple's article on Conventions that #iRebel_85 points out, Apple has an extensive set of naming guidelines.
Google's style guide adds a few more naming guidelines.
These guidelines are well thought out and useful for making code easier to understand and collaborate on.
First, memory management has nothing to do with the names you choose for your methods or classes. In other words if it compiles and you're not using reserved keywords or critical method names you should be good. (Please refer to Edit note)
As for 1, preceding new is very uncommon in Objective-C, so it's better to use objectFrom: instead.
Even better would be to be more precise about the kind of object you're creating. For example:
[NSArray arrayWithArray:]
[NSString stringWithFormat:]
Or in your case as you create a copy, and supposing you're creating "Client" objects:
[Client clientWithClient:options:]
Where with is not really needed.
For 2 I would choose:
copyWithOptions:
As you're more or less customizing [NSObject copy]. I would also implement only this method and delete the now redundant 1 as less methods is clearer, easier to document, and easier to maintain!
In case of doubt just search the SDK documentation to see how similar methods were named by Apple.
Edit:
In the first paragraph I don't mean to encourage bad naming practices, just to say that they are not the reason for your memory management concerns. Yet you should try to follow the naming conventions pointed in the other answers, or as I said, "do as Apple does".
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.
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.
I was told by a fellow StackOverflow user that I should not use the getter method when releasing a property:
#property(nonatmic, retain) Type* variable;
#synthesize variable;
// wrong
[self.variable release];
// right
[variable release];
He did not explain in detail why. They appear the same to me. My iOS book said the getter on a property will look like this:
- (id)variable {
return variable;
}
So doesn't this mean [self variable], self.variable, and variable are all the same?
For a retained property with no custom accessor, you can release the object by:
self.variable = nil;
This has the effect of setting the ivar (which may not be called 'variable' if you have only declared properties) to nil and releasing the previous value.
As others have pointed out, either directly releasing the ivar (if available) or using the method above is OK - what you must not do is call release on the variable returned from a getter.
You can optionally write custom getter behavior, which may result in completely different behavior. So, you cannot always assume that [variable release] has the same results as [self.variable release].
As well, you can write custom properties without an exclusive ivar backing them... it can get messy fast if you start releasing objects from references returned by getters!
There may be additional reasons that I'm unaware of...
A typical getter will look more like this:
- (id)variable {
return [[variable retain] autorelease];
}
So if you use [self.variable release] you have an additional retain and autorelease that you don't really need when you just want to release the object and that cause the object to be released later than necessary (when the autorelease pool is drained).
Typically, you would either use self.variable = nil which has the benefit that it also sets the variable to nil (avoiding crashes due to dangling pointers), or [variable release] which is the fastest and may be more appropriate in a dealloc method if your setter has custom logic.
not all getters take this form:
- (id)variable { return variable; }
...that is merely the most primitive form. properties alone should suggest more combinations, which alter the implementation. the primitive accessor above does not account for idioms used in conjunction with memory management, atomicity, or copy semantics. the implementation is also fragile in subclass overrides.
some really brief examples follow; things obviously become more complex in real programs where implementations become considerably more complex.
1) the getter may not return the instance variable. one of several possibilities:
- (NSObject *)a { return [[a copy] autorelease]; }
2) the setter may not retain the instance variable. one of several possibilities:
- (void)setA:(NSObject *)arg
{
...
a = [arg copy];
...
}
3) you end up with memory management implementation throughout your program, which makes it difficult to maintain. the semantics of the class (and how it handles instance variables' ref counting) should be kept to the class, and follow conventions for expected results:
- (void)stuff:(NSString *)arg
{
const bool TheRightWay = false;
if (TheRightWay) {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[string release];
// - or -
NSMutableString * string = [[arg mutableCopy] autorelase];
[string appendString:#"2"];
self.a = string;
}
else {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[self.a release];
}
}
failing to follow these simple rules makes your code hard to maintain and debug and painful to extend.
so the short of it is that you want to make your program easy to maintain. calling release directly on a property requires you to know a lot of context of the inner workings of the class; that's obviously bad and misses strong ideals of good OOD.
it also expects the authors/subclassers/clients to know exactly how the class deviates from convention, which is silly and time consuming when issues arise and you have to relearn all the inner details when issues arise (they will at some point).
those are some trivial examples of how calling release on the result of a property introduces problems. many real world problems are much subtler and difficult to locate.
I'm a bit new to Objective-C, and I've been trying to do something that apparently isn't allowed, even though it's common practice in other languages (I think).
As a specific example, I want to subclass NSMutableArray to make a SortedMutableArray that always maintains itself in a sorted state. So I subclassed NSMutableArray in the usual manner, adding a NSComparator property that determines the sort order. I overrode the addObject: method to insert objects in a sorted manner:
- (void) addObject:(id)anObject {
for (int i = 0; i < [self count]; ++i) {
NSComparisonResult result = (NSComparisonResult)self.comparator([self objectAtIndex:i], anObject);
if (result == NSOrderedDescending || result == NSOrderedSame) {
[super insertObject:anObject atIndex:i];
break;
}
else {
if (result != NSOrderedAscending) {
[NSException raise:#"InvalidBlockException" format:#"Block must return one of NSOrderedDescending, NSOrderedAscending, or NSOrderedSame"];
}
}
}
}
and everything compiles great. But when I run the program, I get an error indicating that insertObject:atIndex: is now abstract and needs to be implemented. Reading the documentation, it lists several methods that must be implemented in any subclass of NSMutableArray, one of which is indeed insertObject:atIndex:. But I don't need to change the functionality of insertObject:atIndex:; I want it to word exactly as it does in NSMutableArray. Is there a way that I can do this (in general, too, not just for this specific problem)? Why must certain methods be implemented in subclasses like this? Doesn't that kind of defeat one of the purposes of inheritance, code reuse? I've never seen anything like this in other languages, where a method is concrete in a superclass but becomes abstract when it is subclassed. Does this pattern/concept have a name?
Thanks in advance for any help, and I'm sorry if I'm duplicating another question, but I didn't know what to search for other than "subclass" in the objective-c tag, which gave too many results to find what I was looking for.
Bad idea. NSArray is actually a class cluster (which is our word for [essentially] an abstract factory). This means that when you alloc/init an NSArray, you don't actually get an NSArray back. You get (usually) an NSCFArray, which is a private subclass.
NSMutableArray is the same deal (it's abstract). When you alloc/init an NSMutableArray, you get an NSCFArray back that has a little internal mutable bit flipped.
The upshot of this is that subclass a class cluster is generally discouraged, because it's a bit more complex than just creating a normal subclass.
What I would recommend is to instead check out the CHDataStructures framework, which has a whole bunch of data structures that do what you're looking for already.
See Dave DeLong's post about why this is a not a good idea.
If you really want to do something like this, you could try, uhmm, "fake-subclassing" it.
in the .h file,
...
NSMutableArray *mutableArray;
...
#property (nonatomic, retain) NSMutableArray *mutableArray;
...
- (void) addObject:(id)anObject;
in the .m file,
...
#synthesize mutableArray;
...
- (void) addObject:(id)anObject {
[mutableArray addObject:id];
[mutableArray sortUsingSelector:#selector(yourSortingSelector);
}
- (NSMutableArray)mutableArray {
return mutableArray;
}
...
Which works and everything. My colleague did a similar class to this before (we were objective-c noobs at the time, about 2-3 weeks into learning how to code).
What I would recommend, however, is to use a Key-Value Observing approach if you can. Try to listen in whenever an element is added, and sort your array when you get the notification. I haven't done this to an NSMutableArray before though, so I don't know how this will work or if it even will.
My 2 cents, hope it helps. Happy holidays! ^_^
You shouldn't be subclassing NSMutableArray, look up categories. It provides a way to add newer methods to classes
apple's link to categories