How to reference to a class (not an instance of it!) which implements a specific protocol?
+(id<Data>) dataForName:(NSString *)name {
id<DataManager> manager = SpecializedDataManager; // <-- which datatype does "manager" have to be?
return [[manager sharedManager] get:name]; //Getting data over a singleton of manager
}
Where Data and DataMangerare protocols and SpecializedDataManager is a class implementing the DataManager protocol.
I haven't tried this myself, but you should be able to use a pointer-to-class, although I doubt that you could specify that the class must implement a certain protocol:
static Class manager = NULL;
+ (void)someInitMethod
{
manager = [SpecializedDataManager class];
NSAssert([manager conformsToProtocol:#protocol(DataManager)], #"Achtung!");
}
If I understood you correct you want to write something like this:
id<SomeProtocol> someObject = AnotherObjectConformingThisProtocol;
Class class = [(NSObject*)SomeObject class];
if ([someObject isKindOfClass:[AnotherObjectConformingThisProtocol class]]) {}
if (class == [AnotherObjectConformingSomeProtocol class]) {}
If no - please clarify what exactly do you mean.
UPDATE: I've read your comment near another post and got it:
Create wrapper classes for your DataManager and Data protocols:
#interface DataClass : NSObject <Data>
#end
#interface DataManagerClass : NSObject <DataManager>
#end
And use code like this:
+(DataClass*) dataForName:(NSString *)name {
DataManagerClass* manager = SpecializedDataManager; // <-- which datatype does "manager" have to be?
return [[manager sharedManager] get:name]; //Getting data over a singleton of manager
}
Is this what you're looking for:
+(id<Data>) dataForName:(NSString *)name {
Class<DataManager> manager = [SpecializedDataManager class];
return [[manager sharedManager] get:name];
}
Related
I hope the title is precise enough.
I was wondering, how I can pass a interface implementation to an object in objc language.
In java it would look like:
public interface MyInterface {
void onData();
}
The implementing class
public class ImplementMyInterface {
// ...
// other methods
///
void registerInterface(){
MyInterface myInterface = new MyInterface(){
#Override
public void onData(){
// process data here within the class
}
};
}
}
And in objc?
id myinterface.
How to implement it in the class?
Is there only the possibility to let the class inherit the interface?
Like
interface MyInterface : NSObject
and the implementing class
MyImplementingClass : MyInterface
Or is there another possibility?
Thank you in advance
Objective-C has anonymous functions (blocks), but it doesn't have anonymous classes. So, the only way to implement a protocol (which is the objective-c term for an interface) is to make some class conform to that protocol (using your terminology, make that class "inherit" from that protocol) and add a protocol implementation inside that class' implementation.
I was able to solve my problem.
I was only able to import the MyInterface header file in my ImplementMyInterface.m file, but rather in the ImplementMyInterface.h file.
So everything I could do was inside the ImplementMyInterface.m file.
// ImplementMyInterface.m
#import "MyInterface.h"
// inner class
#interface MyInternalInterface : NSObject<MyInterface>
#property (retain) ImplementMyInterface * implementation;
#end
// the actual class
#implementation ImplementMyInterface
MyInternalInterface * _internalInterface;
+(instancetype) build {
// construct myself
ImplementMyInterface * implementatMyInterface = [[ImplementMyInterface alloc] init];
// init inner class object
_internalInterface = [[MyInternalInterface alloc] init];
// register myself
[_internalInterface setImplementation:implementatMyInterface];
return implementatMyInterface;
}
- (NSString *) theActualData {
return #"The actual data";
}
// end of implementation class
#end
// implementation of inner class
#implementation MyInternalInterface
#synthesize implementation;
- (NSString *) onData {
if(implementation != nil)
return [implementation theActualData];
return #"";
}
// end of ImplementMyInterface.m
Say I had a function that does something with a class:
- (void)doSomethingWithClass:(Class)class
{
[class doSomething];
}
but of course the method + (void)doSomething isn't defined for all classes. Let's say it's defined only for subclasses of the SomeClass class. How can I "restrict" the type of class of the class parameter that's given to the function? I want to be able to do something like this:
- (void)doSomethingWithClass:(/* type of [SomeClass class] */)class
{
[class doSomething];
}
so that I don't have to resort to something like this:
- (void)doSomethingWithClass:(Class)class
{
if (![class isSubClassOfClass:[SomeClass class]])
{
// error
}
[class doSomething];
}
Is this possible to do in Objective-C, and is it ever even a good idea to do something like this?
Class is really a c struct that contains metadata about a class type. Structs do not have inheritance.
You can restrict a method by passing in the instance of the object type instead of the Class struct type.
- (void)doSomethingWithClass:(SomeClass *)sender
{
Class class = [sender class];
[class doSomething];
}
If your method really must accept a Class object as the parameter, then isKindOfClass: or isSubclassOfClass is the way to go.
You should use Protocol, to create a reuse interface for your classes. If you classes conform this protocol, so it will implement method doSomething.
#protocol ClassProtocol
+ (void)doSomething;
#end
#interface ClassEngine : NSObject
#end
#implementation ClassEngine
- (void)doSomethingWithClass:(Class)class {
if([class conformsToProtocol:#protocol(ClassProtocol)])
[(Class<ClassProtocol>)class doSomething];
}
}
#end
It's not possible to specify such a type in Objective-C. But it's possible in Swift -- SomeClass.Type
I have an objective-C singleton as follows:
#interface MyModel : NSObject
+ (MyModel*) model;
...
+ (MyModel*) model
{
static MyModel *singlton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
singlton = [[MyModel alloc] initSharedInstance];
});
return singlton;
}
- (MyModel*) initSharedInstance
{
self = [super init];
if (self)
etc.
}
Which gets called in multiple places within the GUI code as:
[[MyModel model] someMethod];
And therefore the model will get created as a consequence of whichever part of the GUI happens to reference it first.
I'm not sure how to implement the equivalent of accessing the class via [[MyModel model] someMethod] in Swift as all examples of using Swift involve creating an object using an initializer and when Objective C class method code is converted to Swift initializer code there is a problem with it not working when the method does not have parameters.
UPDATE
++++++++++
The workaround below is only necessary if you name your singleton method with a name derived from the suffix of the class name i.e. the OPs question the method name is model and the class is called MyModel.
If the method is renamed to something like singleton then it is possible to call it from Swift just like this:
let m = MyModel.singleton()
+++++++++++
I don't know if this is good/bad practice but I was able to get around the problem with initializer conversion not working when there are no parameters by adding a dummy init method. So using the code from the other answer as an example:
#interface XYZThing : NSObject
+ (XYZThing*) thing;
+ (XYZThing*) thingWithFoo:(int)foo bar:(int)bar;
#end
#implementation XYZThing
+ (XYZThing*) thing
{
NSLog(#"This is not executed");
return nil;
}
+ (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
{
NSLog(#"But this is");
return nil;
}
#end
...
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
With this code above the thing method is not called, but the thingWithFoo:bar: method is.
But if it is changed to this then now the thing method will get called:
#interface XYZThing : NSObject
+ (XYZThing*) init;
+ (XYZThing*) thing;
+ (XYZThing*) thingWithFoo:(int)foo bar:(int)bar;
#end
#implementation XYZThing
+ (XYZThing*) init
{
return nil;
}
+ (XYZThing*) thing
{
NSLog(#"Now this is executed");
return nil;
}
+ (XYZThing*)thingWithFoo:(int)foo bar:(int)bar
{
NSLog(#"And so is this");
return nil;
}
#end
...
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
If the Swift compiler mistakenly identifies a method as a class factory method, you can use the NS_SWIFT_NAME macro, passing the Swift signature of the method to have it imported correctly. For example:
+ (id)recordWithQuality:(double)quality NS_SWIFT_NAME(record(quality:));
so,your method should be this:
+ (MyModel*)model NS_SWIFT_NAME(log());
Do exactly what the compiler warning tells you to:
MyModel().someMethod()
Read on to see why...
Swift automatically recognizes ObjC conventions for initializers and convenience constructors. If you have a class that looks like this:
#interface XYZThing : NSObject
+ (instancetype)thing;
+ (instancetype)thingWithFoo:(int)foo bar:(int)bar;
#end
...then, when Swift turns them into initializers, it elides the part of the method name that's the generic name of the class (Thing/thing), moves the part of the selector that refers to the parameter to be a parameter label, and drops any prepositions connecting those parts. So the initializer declarations look like this in Swift:
class XYZThing: NSObject [
init()
init(foo: Int, bar: Int)
}
and you construct objects like this:
let thing = XYZThing()
let otherThing = XYZThing(foo:3, bar:7)
A followup: because class methods like +[XYZThing thing] are treated like initializers by the ObjC to Swift translator (even if that doesn't seem to fully work right now), that naming pattern is a bad idea for singletons. A singleton retrieval method shouldn't be an initializer, because an initializer always creates a new instance.
A singleton retrieval method should instead have a name that doesn't start with the generic name of the class; e.g. +sharedThing, +defaultThing, +oneThingToRuleThemAll, etc.
I have a class Film, each of which stores a unique ID. In C#, Java etc I can define a static int currentID and each time i set the ID i can increase the currentID and the change occurs at the class level not object level. Can this be done in Objective-C? I've found it very hard to find an answer for this.
Issue Description:
You want your ClassA to have a ClassB class variable.
You are using Objective-C as programming language.
Objective-C does not support class variables as C++ does.
One Alternative:
Simulate a class variable behavior using Objective-C features
Declare/Define an static variable within the classA.m so it will be only accessible for the classA methods (and everything you put inside classA.m).
Overwrite the NSObject initialize class method to initialize just once the static variable with an instance of ClassB.
You will be wondering, why should I overwrite the NSObject initialize method. Apple documentation about this method has the answer: "The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.)".
Feel free to use the static variable within any ClassA class/instance method.
Code sample:
file: classA.m
static ClassB *classVariableName = nil;
#implementation ClassA
...
+(void) initialize
{
if (! classVariableName)
classVariableName = [[ClassB alloc] init];
}
+(void) classMethodName
{
[classVariableName doSomething];
}
-(void) instanceMethodName
{
[classVariableName doSomething];
}
...
#end
References:
Class variables explained comparing Objective-C and C++ approaches
As of Xcode 8, you can define class properties in Obj-C. This has been added to interoperate with Swift's static properties.
Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as: #property (class) NSString *someStringProperty;. They are never synthesized. (23891898)
Here is an example
#interface YourClass : NSObject
#property (class, nonatomic, assign) NSInteger currentId;
#end
#implementation YourClass
static NSInteger _currentId = 0;
+ (NSInteger)currentId {
return _currentId;
}
+ (void)setCurrentId:(NSInteger)newValue {
_currentId = newValue;
}
#end
Then you can access it like this:
YourClass.currentId = 1;
val = YourClass.currentId;
Here is a very interesting explanatory post I used as a reference to edit this old answer.
2011 Answer: (don't use this, it's terrible)
If you really really don't want to declare a global variable, there another option, maybe not very orthodox :-), but works... You can declare a "get&set" method like this, with an static variable inside:
+ (NSString*)testHolder:(NSString*)_test {
static NSString *test;
if(_test != nil) {
if(test != nil)
[test release];
test = [_test retain];
}
// if(test == nil)
// test = #"Initialize the var here if you need to";
return test;
}
So, if you need to get the value, just call:
NSString *testVal = [MyClass testHolder:nil]
And then, when you want to set it:
[MyClass testHolder:testVal]
In the case you want to be able to set this pseudo-static-var to nil, you can declare testHolder as this:
+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
static NSString *test;
if(shouldSet) {
if(test != nil)
[test release];
test = [_test retain];
}
return test;
}
And two handy methods:
+ (NSString*)test {
return [MyClass testHolderSet:NO newValue:nil];
}
+ (void)setTest:(NSString*)_test {
[MyClass testHolderSet:YES newValue:_test];
}
Hope it helps! Good luck.
On your .m file, you can declare a variable as static:
static ClassName *variableName = nil;
Then you can initialize it on your +(void)initialize method.
Please note that this is a plain C static variable and is not static in the sense Java or C# consider it, but will yield similar results.
In your .m file, declare a file global variable:
static int currentID = 1;
then in your init routine, refernce that:
- (id) init
{
self = [super init];
if (self != nil) {
_myID = currentID++; // not thread safe
}
return self;
}
or if it needs to change at some other time (eg in your openConnection method), then increment it there. Remember it is not thread safe as is, you'll need to do syncronization (or better yet, use an atomic add) if there may be any threading issues.
As pgb said, there are no "class variables," only "instance variables." The objective-c way of doing class variables is a static global variable inside the .m file of the class. The "static" ensures that the variable can not be used outside of that file (i.e. it can't be extern).
Here would be an option:
+(int)getId{
static int id;
//Do anything you need to update the ID here
return id;
}
Note that this method will be the only method to access id, so you will have to update it somehow in this code.
(Strictly speaking not an answer to the question, but in my experience likely to be useful when looking for class variables)
A class method can often play many of the roles a class variable would in other languages (e.g. changed configuration during tests):
#interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
#end
#implementation
+ (NSString*)theNameThing { return #"Something general"; }
- (void)doTheThing {
[SomeResource changeSomething:[self.class theNameThing]];
}
#end
#interface MySpecialCase: MyCls
#end
#implementation
+ (NSString*)theNameThing { return #"Something specific"; }
#end
Now, an object of class MyCls calls Resource:changeSomething: with the string #"Something general" upon a call to doTheThing:, but an object derived from MySpecialCase with the string #"Something specific".
u can rename the class as classA.mm and add C++ features in it.
Another possibility would be to have a little NSNumber subclass singleton.
I have to call an objective C method from a cpp Function.
I have a class C, whose object address is required in this function. I did come across another link which guided me on how to have a reference to the class C, and use it for invocation from the cpp function.
In my case, there is one small difference in that the Class C is already instantiated, and I would not want to allocate an object again. So how can I get its object address?
The code looks like this:
C.h
import Cocoa/Cocoa.h
id refToC
#interface C: NSObject
{
;
somemethod;
;
}
#end
C.m
#implementation C
- (void) somemethod
{
;
;
}
#end
B.mm
import C.h
void func()
{
//I need the address of object here, so as to invoke:
[refToC somemethod];
}
Thanks in Advance
~ps7
The id type is already a pointer to an object. Once you have created a valid object, e.g.:
refToC = [[C alloc] init]
The easiest way is to make use of the singleton design pattern. Here's a common way to make use of that pattern in Objective-C:
Widget.h
#interface Widget : NSObject {
// ...
}
// ...
- (void)someMethod;
+ (Widget *)sharedWidget;
#end
Widget.m
#implementation Widget
// ...
+ (Widget *)sharedWidget {
static Widget *instance;
#synchronized (self) {
if (!instance)
instance = [[Widget alloc] init];
}
return instance;
}
#end
CppWrapper.mm
void methodWrapper() {
[[Widget sharedWidget] someMethod];
}
Thanks a lot for your pointers. I had missed telling that the class C is a controller class. I tried assigning refToC to self in awakeFromNib, and the invocation in func() worked like a charm.
Thanks Matt and John for your pointers.
~ ps7