|
|
 | | From: | Laurent Deniau | | Subject: | return of init | | Date: | Fri, 03 Dec 2004 09:52:44 +0100 |
|
|
 | Does anybody have *good examples* where init should return something else than the incoming object? I have searched for meaningful examples and explanation on the web, including the apple web site without success.
The only useful case I can imagine is to add one more indirection to self (e.g. for registration somewhere) but then received messages by the indirect self should be delegated to next level, that is the true self. I do not see example where self could be safely replaced by another object.
Thanks.
a+, ld.
|
|
 | | From: | Michael Ash | | Subject: | Re: return of init | | Date: | Fri, 03 Dec 2004 05:54:07 -0600 |
|
|
 | Laurent Deniau wrote: > Does anybody have *good examples* where init should return something > else than the incoming object? I have searched for meaningful examples > and explanation on the web, including the apple web site without success. > > The only useful case I can imagine is to add one more indirection to > self (e.g. for registration somewhere) but then received messages by the > indirect self should be delegated to next level, that is the true self. > I do not see example where self could be safely replaced by another object.
Look up Class Clusters in the Apple docs for one widespread use. Basically, classes like NSString actually have a bunch of hidden, private subclasses which are all specialized for different uses. When you do [[NSString alloc] init], you don't get an NSString back, you get an instance of one of these private subclasses.
Another use is for writing singleton classes. Normally when you write a singleton, you just provide a +sharedInstance method and document somewhere that you shouldn't alloc/init the class. However, you can override init like so:
- init { if(sharedInstance) { [self release]; return [sharedInstance retain]; } ....do setup here.... }
One advantage of this approach is that you can do things like instantiate the singleton in a nib and still have the same object everywhere.
|
|
 | | From: | Joe Shimkus | | Subject: | Re: return of init | | Date: | Sat, 04 Dec 2004 01:25:32 -0500 |
|
|
 | In article <1102074847.661944@nfs-db1.segnet.com>, Michael Ash wrote:
> Another use is for writing singleton classes. Normally when you write a > singleton, you just provide a +sharedInstance method and document > somewhere that you shouldn't alloc/init the class. However, you can > override init like so: > > - init { > if(sharedInstance) { > [self release]; > return [sharedInstance retain]; > } > ....do setup here.... > } >
Funny, I just did the above recently so that anybody who uses the class doesn't shoot themselves in the foot by alloc/init.
> One advantage of this approach is that you can do things like instantiate > the singleton in a nib and still have the same object everywhere.
How do you do this? I'm using a static file scope variable as a sort-of class variable to hold the shared instance reference. Can you actually do a connection to it (static or not) through IB where you have an extern declaration in the class header file something like:
IBOutlet extern ClassFoo *sharedInstance;
with the corresponding definition in the .m file?
-- PGP Key (DH/DSS): http://www.shimkus.com/public_key.asc PGP Fingerprint: 89B4 52DA CF10 EE03 02AD 9134 21C6 2A68 CE52 EE1A
Windows has always aspired to be Mac-like without Microsoft ever really understanding what that even means. - Robert Cringely
|
|
 | | From: | Michael Ash | | Subject: | Re: return of init | | Date: | Sat, 04 Dec 2004 03:06:57 -0600 |
|
|
 | Joe Shimkus wrote: > In article <1102074847.661944@nfs-db1.segnet.com>, > Michael Ash wrote: > >> Another use is for writing singleton classes. Normally when you write a >> singleton, you just provide a +sharedInstance method and document >> somewhere that you shouldn't alloc/init the class. However, you can >> override init like so: >> >> - init { >> if(sharedInstance) { >> [self release]; >> return [sharedInstance retain]; >> } >> ....do setup here.... >> } >> > > Funny, I just did the above recently so that anybody who uses the class > doesn't shoot themselves in the foot by alloc/init. > >> One advantage of this approach is that you can do things like instantiate >> the singleton in a nib and still have the same object everywhere. > > How do you do this? I'm using a static file scope variable as a sort-of > class variable to hold the shared instance reference. Can you actually > do a connection to it (static or not) through IB where you have an > extern declaration in the class header file something like: > > IBOutlet extern ClassFoo *sharedInstance; > > with the corresponding definition in the .m file?
No, that doesn't work. All you do is just implement -init as above, then select the class and do "Instantiate MyClass" as normal. As far as IB is concerned, it's going to get a new instance of your class. However, the nib loader is just going to do [[class alloc] init] when it creates this instance. If your implementation of -init actually returns your singleton, then of course you'll get the singleton into the nib.
|
|
 | | From: | Joe Shimkus | | Subject: | Re: return of init | | Date: | Sat, 04 Dec 2004 14:33:41 -0500 |
|
|
 | In article <1102151217.198098@nfs-db1.segnet.com>, Michael Ash wrote:
> No, that doesn't work. All you do is just implement -init as above, then > select the class and do "Instantiate MyClass" as normal. As far as IB is > concerned, it's going to get a new instance of your class. However, the > nib loader is just going to do [[class alloc] init]
Doh! Of course.
-- PGP Key (DH/DSS): http://www.shimkus.com/public_key.asc PGP Fingerprint: 89B4 52DA CF10 EE03 02AD 9134 21C6 2A68 CE52 EE1A
Windows has always aspired to be Mac-like without Microsoft ever really understanding what that even means. - Robert Cringely
|
|
 | | From: | Philippe Mougin | | Subject: | Re: return of init | | Date: | 3 Dec 2004 20:11:47 -0800 |
|
|
 | Laurent Deniau wrote in message news:... > Does anybody have *good examples* where init should return something > else than the incoming object? I have searched for meaningful examples > and explanation on the web, including the apple web site without success.
There is an interesting example in the article "Obj-C Optimization: Allocation Basics & Foundation" at http://www.mulle-kybernetik.com/artikel/Optimization/opti-2.html (the example is at the end of the article, in the section titled "Does Foundation suck?").
BTW, this article is part of a famous articles series available at http://www.mulle-kybernetik.com/artikel/Optimization/
Best,
Philippe Mougin http://www.fscript.org - Interactive and scripting environment for Cocoa
|
|
 | | From: | Laurent Deniau | | Subject: | Re: return of init | | Date: | Tue, 07 Dec 2004 11:39:11 +0100 |
|
|
 | Philippe Mougin wrote: > Laurent Deniau wrote in message news:... > >>Does anybody have *good examples* where init should return something >>else than the incoming object? I have searched for meaningful examples >>and explanation on the web, including the apple web site without success. > > > There is an interesting example in the article "Obj-C Optimization: > Allocation Basics & Foundation" at > http://www.mulle-kybernetik.com/artikel/Optimization/opti-2.html (the > example is at the end of the article, in the section titled "Does > Foundation suck?").
Thanks for the links.
I have read the article and I am still not convince by the need for an init returning self. If I understand well, all this machinery (NSPlaceHolderString) is just because they want to put the string characters in the same storage as the String itself, and avoid another malloc. Right?
BTW, the article starts with data alignment and saving space. But I saw nothing about the reference counter. I thought that reference counter is stored in the object just after isa. Right? Is is an unsigned short or an unsigned int?
Finally, I saw many times in Objective-C code
id obj = [[class alloc] init];
What happens to the allocated object in alloc if init raises an exception?
Thanks.
a+, ld.
|
|
 | | From: | Christian Brunschen | | Subject: | Re: return of init | | Date: | 7 Dec 2004 12:05:20 GMT |
|
|
 | In article , Laurent Deniau wrote:
>I have read the article and I am still not convince by the need for an >init returning self. If I understand well, all this machinery >(NSPlaceHolderString) is just because they want to put the string >characters in the same storage as the String itself, and avoid another >malloc. Right?
No. There are different NSString subclasses to handle different types of string. For instance, to create a string object that references a sub-range of another string object, one could devise an NSSubString class which could referenc the original string, rather than making its own copy (if the original String is an immutable one). Similarly, if you have different character encodings and character sets, you may be able to have specific subclasses which are optimized to handle that particular encoding or character set.
All of these subclasses would still be instantiated through one of NSString's '-init...' methods. The NSPlaceholderString object that is returned from NSString's +alloc simply dispatches the -init... method as necessary, to instantiate an object of the appropriate subclass.
>BTW, the article starts with data alignment and saving space. But I saw >nothing about the reference counter. I thought that reference counter is >stored in the object just after isa. Right? Is is an unsigned short or >an unsigned int?
No. Cocoa actually uses out-of-line reference counts for most of its classes, IIRC, and only actually stores retain counts for objects whose retain count becomes > 1 . Since many objects have such a simple lifecycle, it cold be considered a waste to store the reference count if it never does anything than start at 1 and then go down to 0, so using an out-of-line reference count, that storage space can be saved - which may make a significant sifference, especially for small objects.
>Finally, I saw many times in Objective-C code > >id obj = [[class alloc] init]; > >What happens to the allocated object in alloc if init raises an exception?
The same thing that usually happens if an exception gets raised. There is absolutely nothing magical about +alloc or -init. If you have an exception handler somewhere that will intercept the raised exception, that will get called; otherwise, I expect the unhandled exception may cause your application to terminate.
Generally, though, a failure to allocate and/or initialize an instance will be signalled by way of the returned object being 'nil', so you should check for that.
>Thanks.
Best wishes,
>a+, ld.
// Christian Brunschen
|
|
 | | From: | Laurent Deniau | | Subject: | Re: return of init | | Date: | Tue, 07 Dec 2004 14:24:06 +0100 |
|
|
 | Christian Brunschen wrote: > In article , > Laurent Deniau wrote: > > >>I have read the article and I am still not convince by the need for an >>init returning self. If I understand well, all this machinery >>(NSPlaceHolderString) is just because they want to put the string >>characters in the same storage as the String itself, and avoid another >>malloc. Right? > > > No. There are different NSString subclasses to handle different types of > string. For instance, to create a string object that references a > sub-range of another string object, one could devise an NSSubString class > which could referenc the original string, rather than making its own copy > (if the original String is an immutable one). Similarly, if you have > different character encodings and character sets, you may be able to have > specific subclasses which are optimized to handle that particular encoding > or character set. > > All of these subclasses would still be instantiated through one of > NSString's '-init...' methods. The NSPlaceholderString object that is > returned from NSString's +alloc simply dispatches the -init... method as > necessary, to instantiate an object of the appropriate subclass.
I was talking only of -init not -init... I do not see in -init how you can switch to another class better than directly in +alloc without add-in information. About the -init... if the underlying object is not the one asked for then you are returning an object of mutable type (in the Liskov sens). So what you do can only be done with langages which support either genericity or dynamic message dispatch (like ObjC). The later forbid optimization through static message resolution since you use the primary class only as a class dispatcher and object factory. In that case, why not directly provide a +newConstantString and disable -init to underline the point since the +alloc -init scheme is not anymore suitable for these constructions and may even be dangerous if used by further derivation?
>>BTW, the article starts with data alignment and saving space. But I saw >>nothing about the reference counter. I thought that reference counter is >>stored in the object just after isa. Right? Is is an unsigned short or >>an unsigned int? > > > No. Cocoa actually uses out-of-line reference counts for most of its > classes, IIRC, and only actually stores retain counts for objects whose > retain count becomes > 1 . Since many objects have such a simple > lifecycle, it cold be considered a waste to store the reference count if > it never does anything than start at 1 and then go down to 0, so using an > out-of-line reference count, that storage space can be saved - which may > make a significant sifference, especially for small objects.
Right. But how do you retrieve the reference counter when you need to know its value (I am not talking about the message, but about the underlying structure, array, hash table, ...). Because if the counter is stored in a third-party object then the efficiency of retrieving its value (base on the object address?) could be rather slow. This could introduce a huge penalty for cases where you need to know for example if refcnt > 1 and ask for a clone before doing a write access to the shared object.
>>Finally, I saw many times in Objective-C code >> >>id obj = [[class alloc] init]; >> >>What happens to the allocated object in alloc if init raises an exception? > > > The same thing that usually happens if an exception gets raised. There is > absolutely nothing magical about +alloc or -init. If you have an exception > handler somewhere that will intercept the raised exception, that will get > called; otherwise, I expect the unhandled exception may cause your > application to terminate.
I was talking about the ressources leaks. I have the feeling that most ObjC code is not exception safe. I agree that it quite difficult to make such code, but as soon as you have exception in ObjC, you should write exception-safe code. Don't you?
> Generally, though, a failure to allocate and/or initialize an instance > will be signalled by way of the returned object being 'nil', so you should > check for that.
AFAIR, sending a message to nil does nothing and return nil. So does it means that after all [[class alloc] init], one should still check for a nil id? (something that I never see in the example/paper).
Thanks.
a+, ld.
|
|
 | | From: | Gregory Weston | | Subject: | Re: return of init | | Date: | Tue, 07 Dec 2004 16:45:02 GMT |
|
|
 | In article , Laurent Deniau wrote:
> >>BTW, the article starts with data alignment and saving space. But I saw > >>nothing about the reference counter. I thought that reference counter is > >>stored in the object just after isa. Right? Is is an unsigned short or > >>an unsigned int?
It's an unsigned int, according to the docs.
> > No. Cocoa actually uses out-of-line reference counts for most of its > > classes, IIRC, and only actually stores retain counts for objects whose > > retain count becomes > 1. ... > > Right. But how do you retrieve the reference counter when you need to > know its value (I am not talking about the message, but about the > underlying structure, array, hash table, ...). Because if the counter is > stored in a third-party object then the efficiency of retrieving its > value (base on the object address?) could be rather slow. This could > introduce a huge penalty for cases where you need to know for example if > refcnt > 1 and ask for a clone before doing a write access to the shared > object.
a) It doesn't introduce a huge penalty. The mechanism used is sufficient for heavy use. b) I might question your reliance on the retain count to control anything other than memory management. It's a black box.
G
-- Change account to gw when responding by mail.
|
|
 | | From: | Michael Ash | | Subject: | Re: return of init | | Date: | Tue, 07 Dec 2004 11:01:17 -0600 |
|
|
 | In article you wrote: >> >> All of these subclasses would still be instantiated through one of >> NSString's '-init...' methods. The NSPlaceholderString object that is >> returned from NSString's +alloc simply dispatches the -init... method as >> necessary, to instantiate an object of the appropriate subclass. > > I was talking only of -init not -init... I do not see in -init how you > can switch to another class better than directly in +alloc without > add-in information.
You can't. However, the other -initWith... methods exist, and so using an alloc/init combo keeps things consistent.
> About the -init... if the underlying object is not > the one asked for then you are returning an object of mutable type (in > the Liskov sens). So what you do can only be done with langages which > support either genericity or dynamic message dispatch (like ObjC). The > later forbid optimization through static message resolution since you > use the primary class only as a class dispatcher and object factory. In > that case, why not directly provide a +newConstantString and disable > -init to underline the point since the +alloc -init scheme is not > anymore suitable for these constructions and may even be dangerous if > used by further derivation?
The entire point of the alloc/init combo is to separate object creation from object initialization. I can write a new alloc method (say, allocFromCache or something) without having to do a lot of evil behind the scenes. It also makes a lot of sense to have init be an instance method rather than a class method, where you have to do evil things like reassigning 'self', directly twiddling instance variables, or you're restricted to doing all of your initialization via accessors. Without the alloc/init separation, your creation code is uglier, and you get a combinatorial explosion if you want to have more than one way to create an object. >> No. Cocoa actually uses out-of-line reference counts for most of its >> classes, IIRC, and only actually stores retain counts for objects whose >> retain count becomes > 1 . Since many objects have such a simple >> lifecycle, it cold be considered a waste to store the reference count if >> it never does anything than start at 1 and then go down to 0, so using an >> out-of-line reference count, that storage space can be saved - which may >> make a significant sifference, especially for small objects. > > Right. But how do you retrieve the reference counter when you need to > know its value (I am not talking about the message, but about the > underlying structure, array, hash table, ...). Because if the counter is > stored in a third-party object then the efficiency of retrieving its > value (base on the object address?) could be rather slow. This could > introduce a huge penalty for cases where you need to know for example if > refcnt > 1 and ask for a clone before doing a write access to the shared > object.
Given how common retain and release are, one would assume that retrieving an object's refcount is fairly fast. It's not as fast as as storing it inline, of course. This design decision was made back when a savings of four bytes per object was a big deal. I have a feeling it also makes thread safety quite a bit easier, though.
However, you should not be writing code that inspect's an object's reference count for purposes other than debugging. There are exceptions, but they are rare. Circumstances like the one you describe are generally handled with the mutable/immutable distinction and defensive copies. If you want an object that's guaranteed not to change, copy it. If it's immutable, then copy will end up just retaining it for you. This has the same kinds of advantages, but it won't break just because somebody did a [[obj retain] autorelease] and the autorelease pool didn't pop yet.
>>>Finally, I saw many times in Objective-C code >>> >>>id obj = [[class alloc] init]; >>> >>>What happens to the allocated object in alloc if init raises an exception? >> >> The same thing that usually happens if an exception gets raised. There is >> absolutely nothing magical about +alloc or -init. If you have an exception >> handler somewhere that will intercept the raised exception, that will get >> called; otherwise, I expect the unhandled exception may cause your >> application to terminate. > > I was talking about the ressources leaks. I have the feeling that most > ObjC code is not exception safe. I agree that it quite difficult to make > such code, but as soon as you have exception in ObjC, you should write > exception-safe code. Don't you?
If you throw an exception from init, then you should release self first. Otherwise, it's just like anything else.
Writing exception-safe ObjC code doesn't seem too horrible. The autorelease pool makes it easier, since autoreleased objects will eventually be released even if an exception is thrown. That means that typical Cocoa code, which rarely calls alloc but rather calls lots of convenience constructors (like the +stringWith... methods) will generally not leak by default. Any time you're manually writing an alloc/init/release sequence, you have to be careful to release the object if an exception occurs in the middle, but this is fairly rare.
>> Generally, though, a failure to allocate and/or initialize an instance >> will be signalled by way of the returned object being 'nil', so you should >> check for that. > > AFAIR, sending a message to nil does nothing and return nil. So does it > means that after all [[class alloc] init], one should still check for a > nil id? (something that I never see in the example/paper).
There is only one way that alloc will fail on a modern system, and that is if you run out of address space. If you actually run out of memory (not the same as running out of address space), then there is no way to detect or recover from the problem on a modern OS, due to lazy allocation strategies. It is more possible that -init will fail, depending on which -init message you're sending, but this is documented.
Very often, you don't need to check, and you can just cascade the nil through all of your messages without ever needing to check. Example:
- (NSData *)checksumOfFile:(NSString *)file { NSMutableData *fileData = [[NSMutableData alloc] initWithContentsOfFile:file]; // returns nil if file doesn't exist, etc. [fileData appendData:salt]; // is a NOP if fileData is nil NSData *md5 = [fileData md5Hash]; // returns nil if fileData is nil [fileData release]; // is a NOP if fileData is nil return md5; }
Here's a method which returns nil on error, with no explicit checks. This isn't always the case with every piece of code, but it often works out nicely.
|
|
|