How to call an Objective-C singleton from Swift? - objective-c

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.

Related

Ambiguous reference to member 'delegate'

I'm trying to create an instance of a singleton class written in Objective-C. I must conform to it's delegate protocol. The Swift implementation looks something like this:
class SomeClass: ManagerDelegate {
let manager = Manager.sharedInstance()
func someFunction(){
manager.delegate = self
}
}
The Objective-C singleton class looks something like this:
// header
#protocol ManagerDelegate <NSObject>
#required
-(void)delegateMethod;
#end
#interface Manager : NSObject {
}
+ (id)sharedInstance;
#property (nonatomic, assign) id delegate;
#end
// class file
+ (id) sharedManager{
static Manager *theSharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
theSharedManager = [[self alloc] init];
});
return theSharedManager;
}
- (void) someInternalMethod{
[delegate delegateMethod];
}
Upon specifying the delegate (manager.delegate = self), I receive the error
Ambiguous reference to member 'delegate'
Is the Objective-C class improperly formed or am I implementing it incorrectly? Am I missing something else?
Your problem is caused by the definition of your sharedInstance method in Objective-C. By declaring it as
+ (id)sharedInstance;
it gets converted to sharedInstance() -> AnyObject! in Swift. Swift then doesn't know which of many potential delegate properties that it can find you mean. If you change your Objective C declaration and implementation to be:
+ (instancetype)sharedInstance;
your problem will go away.
And as #rmaddy points out, your delegate should be weak not assign. If you use assign then delegate will be left as an invalid pointer in the event that the delegate is deallocated.
Also, the use of a delegate with a singleton is a bit contradictory. Given that you will only ever have one instance of the Manager which may be used by a number of other objects and you can only have one active delegate at a time there is the potential for conflicts.
I would suggest that either blocks/closures or NSNotification is a better approach to call backs from a singleton.
Its because whenever you bring instance from objective c code to swift it gives as AnyObject so
let manager = Manager.sharedInstance() //manager is AnyObject due to bridging between objc and swift
gives you AnyObject for manager property. You need to forcefully cast that AnyObject to Manager to use it as manager's instance like below.
class SomeClass: NSObject, ManagerDelegate {
let manager = Manager.sharedInstance() as! Manager //here was your error
func someFunction(){
manager.delegate = self
}
func delegateMethod() {
print("Hello")
}
}
Hope this solves your problem :)

Objective-C Class Method

My header class looks like:
#import "Card.h"
#interface PlayingCard : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
And my implementation:
#implementation PlayingCard
+ (NSArray *) validSuits
{
return #[#"♥︎", #"♣︎", #"♦︎", #"♠︎"];
}
+ (NSArray *) rankStrings
{
return #[#"?", #"1", #"2", #"3", #"4"];
}
- (void)setSuit:(NSString *)suit
{
if ([[PlayingCard validSuits] containsObject:suit])
{
_suit = suit;
}
}
- (NSString *)suit
{
return _suit ? _suit : #"?"; // if suit !nil return suit, else return ? string.
}
+ (NSUInteger)maxRank
{
return [[self rankStrings] count] - 1;
}
#end
So I understand that any method with a + means it's a Class method.
My question is, why must I use [PlayingCard classMethod] e.g. [PlayingCard validSuits] in the setSuit method whereas I can use [self classMethod] e.g. [self rankStrings] in the maxRank method?
I'm assuming it's something to do with the maxRank method being a class method whereas setSuit isn't. But could it be because setSuit is a setter?
I really don't know, I can't visualise what's going on here. I've only just started my foray into Objective-C and am coming from a Java background.
I have realised I can substitute PlayingCard in for self in the maxRank method without any error messages, however substituting self in for PlayingCard in the setSuit method gives me an error saying
No visible #interface for 'PlayingCard' declares the selector for 'validSuits'
Any explanation as to why this is the case and what's going on would be great. Thanks!
The meaning of self in methods
Every Objective-C method receives an implicit self argument. Instance methods receive the instance, while class methods receive the class object (remember: classes are objects).
If you want to send a class method, the compiler lets you use two types of syntax:
[ClassName classMethod]
[classObjectPtr classMethod]
The first syntax is used in [PlayingCard maxRank]. Here, the target is (explicitly) the PlayingCard class.
A class method already has a class object as a target for sending class methods: the self argument. So they can use [self classMethod] to send other class methods.
Why sending a message to self in class methods?
The advantage of the latter is that the class is not explicitly named. This makes it possible to override class methods in subclasses and call them from base classes.
You basically get the same dynamic method dispatch as with instance methods. This is actually a nice feature of Objective-C not present in Java or C++.
Instance methods would use the dynamic version by accessing their class and sending the message to that:
- (void)setSuit:(NSString *)suit
{
if ([[[self class] validSuits] containsObject:suit])
{
_suit = suit;
}
}
Now an imaginary subclass of PlayingCard could override the class method validSuits and implicitly alter the behavior of setSuit:.
self can be an instance or a class depending on the type of method declared.
- (void)setSuit: is an instance method, thus self is an instance inside this method declaration.
+ (NSUInteger)maxRank is a class method, thus self is a class inside inside this method declaration.
+ (void)classMethod;
- (void)instanceMethod;
- (void)setSuit
{ // self is an instance here
[self classMethod]; // warning, class method sent to instance
[self instanceMethod]; // works, instance method sent to instance
}
+ (NSUInteger)maxRank
{ // self is a class here
[self classMethod]; // works, class method sent to class
[self instanceMethod]; // warning, instance method sent to class
}
You tried to called a "class method" on self inside an instance method where self in an "instance".
In a class method, self refers to the class (it refers to an object that represents the class that obj-c runtime creates for you), so you can use it to call class level method.
In an instance method, self refers to the instance. If you want to call class level method in an instance method, you need to use the class name instead.
Java analogy of obj-c class methods is the static method. Java's this keyword is similar to self, except it can't be used to refer to a class.
setSuit is an instance method, and validSuits is a class method. However, both maxRank and rankStrings are class methods. Class methods are basically the same as static methods in C++

Objective-C hidden static method calling

I google this question and spend some time to figure it out by myself but with a bad luck.
I need to call class's static method which is hidden for class's user.
// MyClass.h
#interface MyClass : NSObject
#end
// MyClass.m
#implementation MyClass
- (NSString *)myInstanceMethod
{
return #"result string";
}
+ (NSString *)myStaticMethod
{
return #"result string";
}
#end
------------------------------------------------------------
// MyCallerClass.m
#import "MyClass.h"
#implementation MyCallerClass
- (void) testMethod
{
MyClass *inst = [MyClass new];
// call 1
NSString *resultInstance = [inst performSelector:#selector(myInstanceMethod)];
// call 2
NSString *resultStaitc = [inst performSelector:#selector(myStaticMethod)];
// call
[MyClass myStaticMethod];
}
#end
Call 1 works good, Call 2 returns nil, Call 3 does not compile.
How can I call static method which does not defined in .h file and give correct returned object?
Thank in advance,
Rost
For Call 2 ,
since it is an class method you should call like
NSString *resultStaitc = [[inst class] performSelector:#selector(myStaticMethod)];
inst is the object.To call a class method you must call with class.
The object instance's class is supposed to be calling the method, not the instance itself.
For call 3
It should be working fine,The result value is never used .the compile error is because
+ (NSString *)myStaticMethod;
not declared in .h
use
NSString *resultStaitc1 =[MyClass myStaticMethod];
and it will return the value to the resultStaitc1
Another option is to declare an informal protocol for MyClass at the top of MyCallerClass.m. An informal protocol is just a category interface without the implementation block. You can stick you method declaration(s) in there. It does raise synchronisation problems between the two source files, but so does performSelector:. Doing it this way lets you call methods that have a different signature to just take [0-2] object arguments and return and object.

Should I use "self" in a block to access class methods?

I have a class to perform little conversions like NSDate to NSString with a specific format, etc.
Every methods are class methods, eg +[Tools humanReadableStringForDate:(NSDate*)date];
I sometime need my method +[Tools A] to call a method +[Tools B] of my class, but inside a block.
Should I create a __block safeSelf = self; or is it unnecessary because I use class level methods ?
EDIT :
here is an example, not my actual code :
#implementation FileManager
+(void) uploadEveryFile:(void (^)(NSObject*))thingToDo :(NSArray*) fileArray {
for(NSString *s in fileArray) {
[[SomeWebAPI uploadFile:s withSuccess:^(NSNumber *responseCode) {
[self logUploadOk:s];
}];
}
}
+(void) logUploadOk:(NSString*)s {
NSLog(#"File upload ok : %#", s)
}
#end
I think this make things clearer. I like to use self keyword even for class methods when I can - in this example I can because I am in the same class and refer to a class level method - because it seems to make more sense, and can be helpful if I have to modify my class name.
So is it correct to write it like this ? Is it working but not really correct ? Do I really need to call logUploadOk using [FileManager logUploadOk:s] ?
Thank you !
It is unnecessary to use __block or __weak or anything like that. You are talking about self in a class method, which is the class object itself. The class object lives for the whole program, so memory management like retain and release on it have no effect. So you don't need to worry about retain cycles.
+ (NSString *)A
{
NSString *something = [Tools B];
NSString *something = [self B]; // both are same inside class method
}
+ (NSString *)B
{
//..
}
That's unnecessary because you're using class method, not instance method. To call Class methods, you use the class name: [Tools doThisForMe].
However, it sounds like you could use Objective-C Categories in this case. It would allow you to extend the different classes and make your code more readable such as [myNSDate humanReadableString].
In your case, it would go along the lines of:
NSDate+Human.h
#interface NSDate (Human)
- (NSString *)humanReadableString;
#end
NSDate+Human.m
#implementation NSDate (Human)
- (NSString *)humanReadableString {
// do whatever you want.
// now 'self' refers to the NSDate instance
}
#end

Objective-C Static Class Level variables

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.