I'm new to Objective-c and after having read some articles about good practices, it seems that the (void)initialize class method is the best place to initialize my global static variables :
static NSArray *tableCellBackgrounds = nil;
+ (void)initialize
{
if (self == [VariableDisplayViewController class])
{
tableCellBackgrounds = [NSArray arrayWithObjects:
[[UIImage imageNamed:#"ListLine_Pair.png"] stretchableImageWithLeftCapWidth:40 topCapHeight:27],
[[UIImage imageNamed:#"ListLine_Impair.png"] stretchableImageWithLeftCapWidth:40 topCapHeight:27],
nil
];
[tableCellBackgrounds retain];
}
}
My problem now is: when/where should I send a 'release' message?
Thanks...
Well, you don't :)
The memory of your app will get released anyway when closing the application so that is not an issue. There could be an issue however if in your singletons you open file handles which need to be closed on app shutdown.
I do not agree with Joris that you don't need to release these.
Sometimes you allocate rather large static/global tables and data in +(void)initialize that serve all the instances of specific class, but when you release the last instance of that class, you would want to throw these resources away from memory, and not wait for the whole application process to shutdown.
Unfortunately, I haven't found any "+(void)deinitialise" in the ObjC NSObject methods, so the only technique I can suggest is that you also override the class methods
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
and keep count of your class instances, adding 1 on 'alloc' and 'allocWithZone' and subtracting 1 on 'dealloc'.
Then, when your instance count goes to zero - go on and release those globals you allocated if you want.
Caution though: +(void)initialize won't be called again, when new instance will be created.
More Caution: thread safety should is also important here.
I do not agree with Joris that you don't need to release these.
Your question hints that resources you load as static/global in +(void)deinitialise are for internal use by all the instances of a specific class.
Such resources may have large memory footprint (data blocks needed for special algorithms, tables of constant data, images etc.
When the last instance of this class is released - it makes sense to release the global "class" data, and only load them again when new instances of the class are created.
Obviously, you don't want to wait for the whole application process to shutdown.
Unfortunately, I haven't found any "+(void)deinitialise" in the ObjC NSObject methods, so the technique I can suggest is that you manage this manually.
For that, you need also override the class methods
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
to keep count of your class instances, adding 1 on 'alloc' and 'allocWithZone' and subtracting 1 on 'dealloc'. (of course call super first).
Then, when your instance count goes to zero - go on and release those globals you allocated if you want.
You can also forget about +(void)initialize and load/initialize your resources when the first instance is created.
A word of Caution: thread safety should is also important here.
Related
I thought I had a decent understanding of objects, but I guess not. What happens when two objects are the same name? They are both pointing to the same location in memory? So if I had one class that said:
SomeClass *someObject = [SomeClass new];
someObject.text = #"test";
And another class instantiates the same object with the same name:
SomeClass *someObject = [SomeClass new];
someObject.textColor = [UIColor redColor];
This would modify the same object to be a red text that says "test" right ?
Thanks!
~Carpetfizz
No, those two pointers point to two different objects in the memory. It doesn't matter if they have the same name. They were allocated and initialized separately in two different classes.
Btw, you should never use the new method to allocate and initalize the object. The new message is discouraged, as allocation and initialization are two different processes. You should use this instead:
SomeClass *someObject = [[SomeClass alloc] init];
Nope. Just because two variables happen to share the same name does not mean they share the same memory location. When this compiles, the compiler strips the variable names (but not the class names) and calculates memory offsets and messages instead of names and classes. Besides, as a local variable, as soon as they pass out of scope -most likely at the end of each function that created them- they will be destroyed immediately.
No. You can't do that.
Any modern-day compiler will attempt to strangle you before compiling that code - for that exact reason: It doesn't know what to do!
Even if you could get the compiler to make it work, just because the two objects have the same name doesn't mean they have the same memory address.
NSArray *array = [dictionary objectForKey:#"field"];
and
NSArray *array = [[NSArray alloc] initWithArray:[dictionary objectForKey:#"field"]];
I see both kind of approaches very frequently in objective C code.
When tried to understand, I found both of them used in similar situation too, which makes contradiction. I am not clear on when I should use 1st approach and when 2nd one?
Any idea?
Detailed explanation and useful references are moms welcome.
First off, those two examples are doing slightly different things. One is retrieving something from an existing dictionary and one is creating a new array by retrieving something from an existing dictionary (the value of that key is an array).
But, if you're asking the difference between getting objects by alloc vs. convenience methods. ([NSString alloc] init vs [NSString stringWith ...), by convention, you own anything that you call alloc, new copy or mutableCopy on. Anything that you call that is not those, is autoreleased.
See the memory guide here. Specifically, look at the rules.
Getting an autoreleased object means it will go away at some point in the near future. If you don't need to hold onto outside the scope of that function, then you can call autorelease on it or use one of the convenience methods that's not alloc, etc...
For example:
// my object doesn't need that formatted string - create the autoreleased version of it.
- (NSString) description {
return [NSString stringWithFormat:#"%# : %d", _title, _id];
}
// my object stuffed it away in an iVar - I need the retained version of it. release in dealloc
- (void) prepare {
_myVal = [[NSString alloc] initWithFormat:"string I need for %d", _id];
}
In the first example, I created a convenience methods for others to call, my class doesn't need that object beyond the scope of that method so I create the autoreleased version of it and return it. If the caller needs it beyond the scope of his calling method, he can retain it. If not he can use it and let it go away. Very little code.
In the second example, I'm formatting a string and assigning it to an iVar variable that I need to hold onto for the lifetime of my class so I call alloc which will retain it. I own it and releasing it eventually. Now, I could have used the first version here and just called retain on it as well.
You have a fundamental misunderstanding of allocations versus instance methods.
The first example, NSDictionary's -objectForKey method, returns id, not an instance of NSDictionary, therefore it does not allocate or initialize the variable.
The second, however is the classic retain part of the retain-release cycle.
The two methods are fundamentally equal (if we are to assume that array is alloc'd but empty in the first, and nil in the second), and both get ownership of the array object. I would go with the second, as it guarantees a reference, and it's shorter.
What I think you're confusing this with are new and convenience methods. Convenience methods (like NSNumber's +numberWithInt:, NSString's +stringWithFormat:, and NSMutableArray's +array), return an autorelease instance of the class (usually). New takes the place of alloc and init in just one word.
There are two choices for constructors in Objective C/Cocoa:
1. Class Constructor
Product *product = [Product productWithIdentifier:#"Chocolate"];
// Use product
2. Alloc/init Constructor
Product *product = [[Product alloc] initWithIdentifier:#"Chocolate"];
// Use product
[product release];
What I do
I tend to use the class method just because it looks cleaner and you don't need a release.
I see a lot of alloc/init - what's the advantage to doing it this way?
My Question
Which one is preferable? Or is it just a matter of taste?
Code
For context, the class Product would have the following:
+(Product *)productWithIdentifier:(NSString *)identifier_ {
return [[[[self class] alloc] initWithIdentifier:identifier] autorelease];
}
-(Product *)initWithIndentifier:(NSString *)identifier_ {
self = [super init]
if (self) {
identifier = identifier_;
}
return self;
}
If you're using ARC, there's not that much of a difference between the two. If you're not using ARC, the difference is extremely important.
The alloc/init combination gives you an owning reference. That means you must release it later on. The classnameWithFoo variant returns a non-owning reference. You may not release it.
This follows the usual Cocoa naming conventions. All methods return non-owning (autoreleased) instances, except for the methods that start with alloc, copy, mutableCopy and new. These return owning references that you must release.
Which one to use is mostly a matter of taste. However, if you need temporary objects that you can dispose quickly the alloc variant results in slightly fewer method calls (the autorelease) and in a loop, it also reduces the maximum memory footprint. However, in most cases this reduced cost is neglectable.
IMO, the biggest difference between the two approaches comes from the fact that using a "class constructor" you get an autoreleased object; this is the most convenient option when you:
assign the allocated object to a retain properties;
when you create "temporary" objects (think of the various NSString methods that build a string from another string: in many case it happens you need to "chain" such calls; the constructor allows you to "forget" about memory management);
when you add the object to some object that retains it (think: addSubview)
In such cases the syntactical advantage of "class constructor" is foremost, but I think it also makes your code "safer" in terms of memory management.
On the other hand, when you create an object to assign it to an assign property (or directly to an ivar for which you have no property), then alloc/init will do perfectly and be preferable to the "constructor" (IMO).
So, in the end, it depends on the way you are going to use the objects you allocate. The class constructor is a convenience method.
I am new to Objective-C, so this might be a dumb question.
I cannot help but see the similarities between ObjC and Microsoft's COM with respect to memory management (AddRef/Release vs retain/release). In a COM environment, it's more or less imposed on you to always AddRef (retain) an object before returning it to the caller. From what I've seen so far (I'm a third through Cocoa® Programming for Mac® OS X (3rd Edition)), the memory management part is somewhat fuzzy.
Assuming there is no GC, what is the idiomatic way to return an object?
Read Memory Management Programming Guide about autorelease pools.
In Objective-C, by convention, objects should be returned autoreleased (unless the method returning the object has a name that begins with “alloc”, “new”, “copy”, or “mutableCopy”). Autoreleased objects are tracked by Objective-C in a pool and automatically handled, which means you don't need to care about sending a final release to them. This greatly simplifies reference counting compared to COM, and this is why you're not seeing any release calls on returned objects most of the time. In contrast, the same convention specifies that all objects returned by a method whose name begins with alloc, new, copy, or mutableCopy, are the responsibility of the method caller. You have to manually call release on these objects or your program will have memory leaks.
Cocoa goes around the limitations of AddRef/Release in COM by introducing a third sibling; autorelease.
retain - I need this, make it stick around.
release - I don't need this anymore, you may remove it immediately.
autorelease - I don't need this, but let it stay around a few seconds in case someone else wants to pick it up first.
This tiny addition allow most return values to be handles as-if we had garbage collection. If you are not interested in keeping the return value around, just do nothing extra.
In order to get this to work there is a convention (a convention good enough to let the compiler do the memory stuff automatically for you with upcoming ARC):
Method names beginning with these must return retained instances:
alloc
copy
new
retain
All other must return autoreleased instances.
Three example implementation for how this can be applied in practice:
-(NSString*)newHelloWorldString {
NSString* s = [NSString stringWithString:#"Hello world"];
// Apply retain because s in now autoreleased
return [s retain];
}
-(NSString*)helloWorldString {
NSString* s = [[NSString alloc] initWithString:#"Hello world"];
// Apply autorelease because s is now retained.
return [s autorelease];
}
-(NSString*)fullName {
// No memory management needed, everything is autoreleased and good.
NSString* fn = [self firstName];
NSString* ln = [self lastName];
NSString* s = [NSString stringWithFormat:#"%# %#", fn, ln];
return s;
}
Generally something like
return [object autorelease];
and you can retain on the other end.
If you are planning to deploy on Lion/iOS5 or are using the latest SDK then also check out ARC.
Essentially i would recommend making the class that receives it retain it. i.e class stackoverflow receives object answer.
i.e
-(void) setAnswer:(Answer*) _answer{
self.answer = _answer; // If the answer is created from a returned message.
[_answer release];
}
edit: I think I might have put up the wrong stuff up there now that i am looking at it the 2nd time . Meant something along the lines:
Answer *_answer = [stackoverflow createAnswer];
self.answer = _answer;
[_answer release];
If you return an object , it is up to the owner to retain it , i would avoid autoreleases wherever possible because once the nspool kicks in, those objects are gone and if they are still used, it will cause problems.
i.e Answer *answer = [stackoverflow getAnswer] and if answer was created in the getanswer method then whomever is retrieving it is responsible in releasing it.
Makes sense?
In objective-c, I have a utility class with a bunch of static methods that I call for various tasks. As an example, I have one method that returns an NSArray that I allocate in the static method. If I set the NSArray to autorelease, then some time later, the NSArray in my calling method (that is assigned to the returned pointer) losses it's reference because the original form the static method is cleaned up. I can't release the NSArray object in the static method because it needs to be around for the return and assignment.
What is the right way to return an object (like the NSArray) from a static class, and have it hang around for the calling class, but then get cleaned up later when it is no longer needed?
Do I have to create the object first in the caller and pass in a pointer to the object and then return that same object form the static method?
I know this is a basic O-O problem, I just never had this issue in Java and I do not do much C/C++.
Thanks for your help.
Your autorelease is correct in the return just retain it when you call the static method.
NSArray *data = [[StaticClass getArray] retain];
If you have a property for the place your assigning the return value to, you can just do self.data = .. and the retain is automatic.
Please take the time to read over the rules. These apply to all of the frameworks you'll be using, and should apply to your code as well. Burn these into your head, and they'll become second nature. Thankfully, it's not complex, rather simple.
It's quite simple. If you do not own an object, it will go away at some indeterminate point in the future. In your case, the "indeterminate" point is when the autorelease pool gets drained, which in the normal case, is at the end of processing the current event.
If you want an object to hang around, you need to own it. The memory management rules referred to by jer will tell you exactly how you get ownership of an object. In this case, you need to retain the object. You must then, of course, release it later when you have done with it.
Regards your comment to Matt's answer. Your code is this:
for (NSString * date in dateList)
{
[historyList addObject:[[BIUtility historyForDate:date] retain]];
}
and
+ (NSArray *) historyForDate:(NSString *)date
{
NSMutableArray * ret = [[[NSMutableArray alloc] init] autorelease];
}
The first thing you need to know is that collections retain their members, so, in fact, your retain of the historyForDate is unnecessary. You don't want to own that object, historyList does. If it's disappearing, it's probably because historyList itself is being deallocated (because you don't own it) or is nil.
By the way, historyForDate: does nothing with the date. Is that correct?