While attempting my first sub-class in Objective-C I have come across the following warning which I cannot seem to resolve. The call to decimalNumberWithMantissa gives a warning of "initialization from distinct Objective-C type".
#import <Foundation/Foundation.h>
#interface NSDecimalNumberSub : NSDecimalNumber {
}
#end
#implementation NSDecimalNumberSub
#end
int main (int argc, char *argv[]) {
NSDecimalNumberSub *ten = [NSDecimalNumberSub
decimalNumberWithMantissa:10
exponent:0
isNegative:NO];
}
Does a class method have to be treated differently with a sub-class? Am I missing something simple? Any help would be appreciated.
NSDecimalNumber defines the decimalNumberWithMantissa:... method to return an NSDecimalNumber, so you're going to get back an instance of the base class and not your custom subclass. You'll have to create your own convenience method to return an instance of your subclass, or just alloc and initialize it another way.
If you're writing your own class you can define a convenience method like that to return type id, and then use [[self alloc] init] when creating the instance to make your class safe for subclassing.
Related
In this tutorial here: http://www.raywenderlich.com/62989/introduction-c-ios-developers-part-1
It mentions that for Objective-C:
Even if you only declare a method inside the implementation of a
class, and don’t expose it in the interface, you technically could
still call that method externally.
How is this done?
There are a lot of ways.
For example, as long as a compatible method is declared somewhere, you can call it normally with dynamic typing. Here's a demonstration:
// MyClass.h
#interface MyClass : NSObject
#end
// MyClass.m
#interface MyClass()
- (void)addObject;
#end
#implementation MyClass
- (void)addObject:(id)object {
NSLog(#"Whoa, I got called!");
}
#end
// main.m
#import <Foundation/Foundation.h>
#import "MyClass.h"
int main() {
id something = [[MyClass alloc] init];
[something addObject:#"Look ma, no errors!"];
return 0;
}
Since there is a known method named addObject: that takes an object, and id variables are dynamically typed, this is 100% valid and will call MyClass's addObject: method.
They could even get it with a statically typed variable and a method that isn't known by declaring the method in a category. A few other options:
using performSelector: as #michaels showed in his answer
going straight to objc_msgSend()
getting the method IMP and calling it directly.
You can use the performSelector: method of NSObject, though the compiler will give you a warning if the selector is not publicly declared anywhere
[someObject performSelector:#selector(someMethod)];
Presume I have an Object, an instance of MyClass. In Objective-C one can ask the Object to "perform" a selector by either sending it a message or using NSObject's "perform".
This selector has to be defined at compile time as part of the Class definition, more precisely as an Instance method of that class OR with the help of the Obj-C Runtime, have the method added to the (entire) MyClass at runtime with class_addMethod.
My question is as follows:
Would it be possible to send an object the IMP and ask it to execute it on itself? Essentially I want Objects, different instances of MyClass to execute things on themselves without the entire MyClass knowing about it. Essentially I would call these "per Object methods", an Object1 gets this IMP executed on itself then another Object2 gets a different IMP, and so on. These IMPs are stored somewhere else and it's that Object that knows and decides where to send things.
yes that works
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface DonorTemplateClass : NSObject
- (void)testMethod:(NSString*)aStringAsParameter;
#end
#implementation DonorTemplateClass
- (void) testMethod:(NSString*)aStringAsParameter {
NSLog(#"%# :: %#", self.class, aStringAsParameter);
}
#end
#interface AClass : NSObject
#end
#implementation AClass
#end
#interface AnotherClass : NSObject
#end
#implementation AnotherClass
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
SEL justTheMethodSelector = #selector(testMethod:);
IMP justTheMethodImplementation = class_getMethodImplementation([DonorTemplateClass class], justTheMethodSelector);
AClass *anAClassInstance = [[AClass alloc] init];
AnotherClass *anotherClassInstance = [[AnotherClass alloc] init];
NSString *aString = #"Test1";
typedef void (*MyTypeName)(id,SEL, NSString*); // more info about the block syntax on http://goshdarnblocksyntax.com scroll down to "typedef"
MyTypeName blockName = (MyTypeName)justTheMethodImplementation;
blockName(anAClassInstance, justTheMethodSelector, aString);
blockName(anotherClassInstance, justTheMethodSelector, aString);
}
}
NOTE that I cast my IMP to a typedef'd pointer type. it compiles fine when I just call I but objc_retain crashes then for me ... so I'd say you need to typedef your IMPs before using them but then you can execute them in the context of any suitable class
IMP is just a typedef for a regular C function pointer. It's meant to point to method implementations, which are C functions with first parameter being an object, and second parameter being a selector. And "an Object to execute an IMP" simply means to call the C function, passing the object as first argument, and a selector as second.
You said you want to be able to "send an object the IMP and ask it to execute it on itself" (i.e. call the IMP passing the object and a selector), yet you do not want "the entire MyClass knowing about it" (which I take to mean you do not want it as a method).
So basically, that sounds like you just want a bunch of standalone C functions, not methods, which you can call, passing various objects, as needed. And you can store these C function pointers as you like. Is that right?
I'm familiar with NSSelectorFromString function with which we can create a new SEL from a string.
The problem is that I cannot use it to perform a Class Method call since the performSelector method works only with instances as its receiver.
I need something that may function like this:
NSString* colorName = colorsArray[num];
NSString* methodName = [NSString stringWithFormat:#"%#Color", colorName]; //will create blueColor or redColor, etc...
SEL colorMethod = NSSelectorFromString(methodName);
self.view.backgroundColor = [UIColor performSelector:colorMethod]; //this is not valid... since NSObject only has performSelector as an Instance method...
Is there another way to hold an on-the-run variable representing a Method that can function as a Class Method and can be created from an NSString?
Or a way to message a Class with a selector?
The problem is that I cannot use it to perform a Class Method call
since the performSelector method works only with instances as its
receiver.
This is wrong. What makes you think that the UIColor class object is not an "instance"? Class objects are objects, which means they are "instances" of some class. Class objects are instances of (some subclass of) their root class, which in the case of UIColor is NSObject. (In other words, the UIColor class objects is also an NSObject and supports all NSObject instance methods.)
If you want to understand how this works, every class object is an instance of a metaclass. Every class has its own metaclass, and metaclasses have inheritance following their classes (i.e. if A is superclass of B, then A's metaclass is superclass of B's metaclass). At the end, the metaclass of the root class inherits from the root class itself (so NSObject's metaclass inherits from NSObject). What this means is that class methods are inherited, and furthermore that the root class (in this case NSObject)'s instance methods are inherited as class methods by all classes with that root class.
just call performSelector on the class
#import <Foundation/Foundation.h>
#interface T : NSObject
+ (NSString*)foo;
+ (NSString*)redColor;
#end
#implementation T
+ (NSString*)foo {
return #"bar";
}
+ (NSString*)redColor {
return #"RED";
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
id tclass = [T class];
NSLog(#"%#", [tclass performSelector:#selector(foo)]);
NSLog(#"%#", [tclass performSelector:#selector(redColor)]);
SEL sel = NSSelectorFromString(#"redColor");
NSLog(#"%#", [tclass performSelector:sel]);
}
}
You can also use NSInvocation:
SEL sel = NSSelectorFromString(#"whiteColor");
NSMethodSignature *sig = [UIColor methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setSelector:sel];
[invocation setTarget:[UIColor class]];
CFTypeRef retVal;
[invocation invoke];
[invocation getReturnValue:&retVal];
NSLog(#"retVal: %#", retVal);
From Cocoa Fundamentals Guide (obsolete but this part is still valid):
The runtime system treats methods defined in the root class in a
special way. Instance methods defined in a root class can be performed
both by instances and by class objects. Therefore, all class objects
have access to the instance methods defined in the root class.
The Foundation framework provides two root classes: NSObject and NSProxy. NSObject has a method documented as – performSelector:, but because it is a root class, any subclass is able to use it as a class method. For example: both +[UIColor performSelector:] and -[UIColor performSelector:] work. The explanation at a runtime level is in newacct's answer.
One calls the class method and the other calls the instance method. Example:
#import <Foundation/Foundation.h>
#interface A : NSObject
#end
#implementation A
+(void) x { NSLog(#"class method"); }
-(void) x { NSLog(#"instance method"); }
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[A performSelector:#selector(x)];
[[A new] performSelector:#selector(x)];
}
}
Prints
class method
instance method
I tried to answer Using a UITableView subclass with a UITableViewController with ISA Switching like so:
self.tableView->isa = [MyTableView class];
But, I get the compile error: Instance variable 'isa' is protected.
Is there a way to get around this? And, if so, is it safe to do?
I'm asking because #AmberStar's answer to that question seems slightly flawed. (See my comment.)
If your tableview class provides ANY storage this will break. I would not recommend the path you're going down. But the correct method would be to use object_setClass(tableView, [MyTableView class]).
Please make sure this is really what you want.
Here is a small code-sample showing how this is a horrible idea.
#import <objc/runtime.h>
#interface BaseClass : NSObject
{
int a;
int b;
}
#end
#implementation BaseClass
#end
#interface PlainSubclass : BaseClass
#end
#implementation PlainSubclass
#end
#interface StorageSubclass : BaseClass
{
#public
int c;
}
#end
#implementation StorageSubclass
#end
int main(int argc, char *argv[])
{
BaseClass *base = [[BaseClass alloc] init];
int * random = (int*)malloc(sizeof(int));
NSLog(#"%#", base);
object_setClass(base, [PlainSubclass class]);
NSLog(#"%#", base);
object_setClass(base, [StorageSubclass class]);
NSLog(#"%#", base);
StorageSubclass *storage = (id)base;
storage->c = 0xDEADBEEF;
NSLog(#"%X == %X", storage->c, *random);
}
and the output
2011-12-14 16:52:54.886 Test[55081:707] <BaseClass: 0x100114140>
2011-12-14 16:52:54.889 Test[55081:707] <PlainSubclass: 0x100114140>
2011-12-14 16:52:54.890 Test[55081:707] <StorageSubclass: 0x100114140>
2011-12-14 16:52:54.890 Test[55081:707] DEADBEEF == DEADBEEF
As you can see the write to storage->c wrote outside the memory allocated for the instance, and into the block I allocated for random. If that was another object, you just destroyed its isa pointer.
The safe way is to create a new instance.
Swapping isa is not safe - you have no idea what the memory layout of a class is or what it will be in the future. Even moving up the inheritance graph is really not safe because objects' initialization and destruction would not be performed correctly - leaving your object in a potentially invalid state (which could bring your whole program down).
I am trying to create a custom object that simply inherits the NSString class and overrides the 'description' method.
When I compile, however, I am getting a warning:
Incompatible pointer types initializing 'OverrideTester *' with an expression of type 'NSString *'
Here is my code:
main.m
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import "OverrideTester.h"
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *str = #"Programming is fun";
OverrideTester *strOverride = #"Overriding is fun";
NSLog (#"%#", str);
NSLog (#"%#", strOverride);
[pool drain];
return 0;
}
OverrideTester.h
#import <Foundation/Foundation.h>
#interface OverrideTester : NSString
-(void) description;
#end
OverrideTester.m
#import "OverrideTester.h"
#implementation OverrideTester
-(void) description
{
NSLog(#"DESCRIPTION!\n");
}
#end
NSString is part of a class cluster. You cannot just create arbitrary subclasses of it, and when you do, you can't assign constant strings to them (which are type NXConstantString). See Subclassing Notes in the NSString documentation. Generally you don't want to subclass NSString. There are better solutions for most problems.
you are assigning an instance of NSString to your variable of type OverrideTester. If you want an instance of your class, you need to instantiate an instance of that class; type-casting will never change the class of an instance.
description is defined as returning an NSString*:
- (NSString *)description;
Do not try to learn about subclassing and overriding methods by subclassing NSString (or any other class cluster). If you want to play with subclassing and such -- a very good idea when new to the language, assuredly -- then subclass NSObject, potentially multiple levels , and play there.
How do you mean to subclass NSObject,
potentially multiple levels? Isn't it
possible NSObject might have
conflicting methods compared to other
class clusters or just not have them
available to override?
If your goal is to figure out how method overrides work (which I thought it was), then you'd be better off doing it entirely yourself.
I may have mis-read your question.
In any case, subclassing NSString is pretty much never done. There are very very few cases where it is useful. Overriding description in anything but custom classes specifically for debugging purposes is useful, yes. Calling description in production code should never be done.
Also, why would description return an
NSString* in this code?
What would happen if something that expects an NSString* return value were to call your version that doesn't return anything?
A crash.
You are declaring a variable named strOverride of type pointer to OverrideTester. But to that variable, you are trying to assign a pointer to an NSString. You cannot assign a superclass to a variable of a subclass. Imagine a generic class TwoWheeled and a derived class Motorbike. A Motorbike can be treated like a TwoWheeled, but not the other way round as the Motorbike has features a normal TwoWheeled might not have like a motor.