The following code implements an NSProxy subclass which forwards methods to an NSNumber instance.
However when calling [nsproxy floatValue] I get 0.0 under GCC 4.2.
Under LLVM-Clang I get the correct answer 42.0.
Any idea what is going on?
(by the way this is running under Garbage Collection)
-(id) init;
{
_result = [NSNumber numberWithFloat:42.0];
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [[_result class] instanceMethodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:_result];
[anInvocation invoke];
return;
}
Your proxy class doesn't declare the message signature for -floatValue. Since it returns a floating point number, that can be a problem. Because you haven't declared it anywhere and presumably aren't casting your proxy object to its' represented class the compiler has to guess at the method signature. In that case the GCC compiler guesses that the message will return an id pointer.
In Objective-C, depending on the signature of the message and the architecture of the machine, different functions get used for processing the message and its' return values. On x86 machines messages that return a floating point value are called through objc_msgSend_fpret while functions that return void and id use objc_msgSend.
Since the GCC compiler is assuming that the return value is an id it uses the latter function and incorrectly handles the result. That Clang is able to handle this properly is interesting but I would hesitate to rely on that behavior. It would be better to declare a category on your proxy for any methods that you'll be forwarding. That also has the benefit of removing the warning that was being generated for the line of code calling the floatValue method.
#interface Foo (ProxyMethods)
- (float)floatValue;
#end
Any init method should call [super init]; to prevent unexpected behaviour. As so:
- (id)init {
self = [super init];
if (self) {
// Your init code
}
return self;
}
Related
I have the following method:
- (FDModel *)_modelForClass: (Class)modelClass
withIdentifier: (NSString *)identifier
which should take in a Class and a identifier, create an instance of modelClass, assign the identifier and do some other work based on the fact that it assumed modelClass is a subclass of FDModel.
I can put in a check that raises some error or exception if [modelClass isSubclassOfClass: [FDModel class]] == NO but I was trying to see if there was a way to enforce this at compile time.
EDIT: I understand that some people see this as a obvious factory method but the modelClass parameter is actually passed in by the user of my library through a delegate callback - (Class<FDModel>)modelClassForDictionary: (NSDictionary *)dictionary;. This question was more aimed at making the user of my library return a Class that has a specific subclass.
I would consider the plain answer to your question being no; there is no way of checking if a class passed as a parameter is of a certain kind.
But I'd like to argue that the essence of your question primarily points to a design issue, i.e. can't your instance-generating method be expressed as a factory method? Like so:
#interface FDModel
+ (instancetype)modelWithIdentifier:(NSString *)identifier;
#end
In the above case you would simply do:
[FDModel modelWithIdentifier:anIdentifier];
The actual class returned (and the initialisation logic) being specified by the factory method implementation through subclassing of the FDModel class:
#implementation FDModelSubclass
+ (instancetype)modelWithIdentifier:(NSString *)identifier
{
FDModel *model = [super modelWithIdentifier:identifier];
if (model)
{
// do additional init stuff
}
return model;
}
#end
Nothing to check, no chance to go wrong.
After some research I don't think you can do it at compile time - you have to do it at runtime as you expected.
BOOL classConformsToProtocol = [class conformsToProtocol:#protocol(OKAProtocol)];
OR
BOOL classConformsToProtocol = [self class:[OKAClass class] conformsToProtocol:#"OKAProtocol"];
------
- (BOOL)class:(Class)class conformsToProtocol:(NSString *)protocol;
{
return [class conformsToProtocol:NSProtocolFromString(protocol)];
}
static NSMutableDictionary * allTheSingletons;
#implementation BGSuperSingleton
+(instancetype)singleton
{
return [self singleton1];
}
+(id) singleton1
{
NSString* className = NSStringFromClass([self class]);
if (!allTheSingletons)
{
allTheSingletons = NSMutableDictionary.dictionary;
}
id result = allTheSingletons[className];
PO(result);
if (result==nil)
{
result = [[[self class] alloc]init];
allTheSingletons[className]=result;
}
return result;
}
BGSuperSingleton should be the parents of all singleton classes.
Then I do in one of the subclass:
+(NSPredicate *)withinASquare:(double)distance{
CLLocation * anchorWeUsed=[self singleton].mapCenterLocation; //Error map center is not of type ID
return [self withinASquare:distance fromLocation:anchorWeUsed];
}
It looks like CLANG doesn't understand that singleton is of type +(instancetype) and think the type is id instead.
What am I missing?
Replacing self with the MySubSingletonClass (which is something that's known at compile time) works though.
Any explanation?
Not sure (and all the beneath are only my assumptions) but seems that at compile time compiler does not know the class of [self singleton1].
As it's said in docs (if we extrapolate that behavior on instancetype also):
... and the method will have a related result type if its return type is compatible with the type of its class...
I.e. singleton1 returns object of an unknown class and singleton as well considers that it returns object of incompatible to BGSuperSingleton class (as far as it's unknown at compile time), thus related result magic is not working here.
Was interested in that and also checked:
+ (NSPredicate*) withinASquare: (double)distance {
CLLocation* anchorWeUsed = [[self alloc] init].mapCenterLocation; // Error map center is not of type ID
return [self withinASquare:distance fromLocation:anchorWeUsed];
}
alloc and init return related result class and the error is still there. The thing helped was:
+ (NSPredicate*) withinASquare: (double)distance {
BGSuperSingleton* bgSuperSingleton = [[self alloc] init]; // class is known at compile time
CLLocation* anchorWeUsed = bgSuperSingleton.mapCenterLocation; // no error here
return [self withinASquare:distance fromLocation:anchorWeUsed];
}
I'm still interested in this and hope somebody could approve or correct my assumptions.
This is currently what I have for my init,
- (id)init
{
self = [super init];
if (self) {
self.url = [[NSURL alloc] init];
self.blurb = [[NSString alloc] init];
self.author = [[NSString alloc] init];
}
return self;
}
It does nothing, but I have another method called initWithObject: that will use its argument to fill up the instance variables url, blurb, and author. I don't know what I should be doing with this init. Should I throw an exception? What other options do I have?
If you want to override your standard -init method you could either return nil (if you do not want -init to be used) or do:
- (instancetype)init
{
return [self initWithObject:nil];
}
If you want to stop the use of -init completely you can tag it as an unavailable attribute or use NSAssert:
// .h
// ...
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
// ...
// .m
- (instancetype)init
{
NSAssert(NO, #"This method should not be used");
return nil;
}
You can use either UNAVAILABLE_ATTRIBUTE or NSAssert(), but if you use UNAVAILABLE_ATTRIBUTE you need some kind of implementation of -init, even if it just returns nil.
You don't have to have a plain init—you can simply have your initWithObject: method. If you're doing any basic setup that will remain the same 80% of the time, or if you have some common code in all your initializers, you can override init, but you are not required to.
Also consider changing your initWithObject: name to be more specific, to something like initWithPost: (I'm assuming this is some kind of blog-entry fetcher based on your ivars) so it's more apparent what object is desired.
I think you misinterpreted what you read. I don't think you would throw an exception. You could; however, leak memory. If your initWithObject: method looks like this:
- (id)initWithObject:(id)obj {
if ((self = [self init])) {
self.url=[obj url];
self.blurb=[obj blurb];
self.author=[obj author];
}
return self;
}
And you would be perfectly fine. You could get an exception if your object was instantiated with -init and you used a variable which was assigned, assuming it was real. So in your subsequent methods be sure to check that the objects exist before using them.
If you made your object with -init rather than -initWithObject this could throw an exception:
- (void)dealloc {
[url release];
[blurb release];
[author release];
[super dealloc];
}
The rule that Apple has established for Cocoa programming is that every class must have one initializer method which is the "Designated Initializer". Every other initializer for the class must call that D.I.* The D.I. itself must call the superclass's D.I. Generally, the initializer with the greatest number of arguments (the one that most completely specifies the state of the new object) is the D.I.
In your case, with the bare init, and initWithObject:, the second would likely be the D.I. You would therefore override init to call initWithObject: with some default argument:
- (id) init {
return [self initWithObject:[Object objectWithURL:[NSURL URLWithString:#"http://www.apple.com"]
blurb:#""
author:#""]];
}
This will result in a sort of dummy object, which is correctly initialized with useless data. (Outside of ARC, be sure to watch the memory management of the default argument(s) -- you want to use an autoreleased/unowned object(s).)
*Sometimes an exception is made for initWithCoder:.
If you have any method that you don't want called and that you don't want your subclass to support, throwing an exception in a Debug build is perfectly reasonable.
The Xcode 4 static analyzer flags this method as a having an over-released return value when that does not seem to be the case.
- (id)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithURL:self.url postString:self.postString];
}
There is an arrow pointing from the return keyword to the expression following it, and another from that expression to the analyzer warning. Here is the static analysis:
Method returns an Objective-C object with a +1 retain count
Object sent -autorelease message
Object returned to caller as an owning reference (single retain count transferred to caller)
Object returned to caller with a +0 (non-owning) retain count
Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected
Is the static analyzer incorrect or is there something wrong with this code?
By request, the -initWithURL:postString: method:
- (id)initWithURL:(NSURL *)u postString:(NSString *)p
{
if ( (self = [super init]) )
{
self.url = u;
self.postString = p;
}
return self;
}
I continue to get this warning even after cleaning and rebuilding the project.
UPDATE: The Xcode static analyzer no longer flags this as an issue after upgrading to Xcode 4.2.
That's a bug in Xcode. The code is alright.
It is common practice to write MyClass* obj = [[MyClass alloc] initWithX:X] in Objective-C. initWithX is usually defined as
- (MyClass*) initWithX: (MyArgClass*) X {
if (self = [super init]) {
// initialize
}
return self;
}
My question is: what if initialize fails? I don't want to throw exceptions, but, how do I indicate error? If I return nil, the caller will not be able to release the pointer.
If initialization fails for any reason you should release self. For an exception that may occur in your initialization you need to add you #try #catch as appropriate so you can release self.
- (MyClass*) initWithX: (MyArgClass*) X {
if (self = [super init]) {
// initialize
if(myInitializationCodeFailed)
{
[self release];
return nil;
}
}
return self;
}
Update
If it is possible for your initialization fail I would not raise an exception from with in your initialization code. If you would like to provide the caller with information I would refactor the initializer to accept an NSError to be returned.
- (MyClass*) initWithX: (MyArgClass*) X error:(NSError**)error {
As Alexei Sholik points in the comments check out the Handling Initialization Failure section of Allocating and Initializing Objects.
Basically, this answers your question.
Handling Initialization Failure
In general, if there is a problem during an initialization method, you should call the release method on self and return nil.
There are two main consequences of this policy:
Any object (whether your own class, a subclass, or an external caller) that receives nil from an initializer method should be able to deal with it. In the unlikely case that the caller has established any external references to the object before the call, you must undo any connections.
You must make sure that dealloc methods are safe in the presence of partially initialized objects.
...