I need to call a class method on a variable of type Class, which I know holds a subclass of a base class, MyBaseClass...
- (void)foo:(Class)clazz {
// Now i want to call +myClassMethod on clazz
}
I don't know if this is actually possible. If it is, is there a safer way to guarantee that clazz is actually a subclass of MyBaseClass?
Thanks in advance
You can use -respondsToSelector:, just like you would with any Objective-C object. You can also use +isSubclassOfClass: to test whether the class is a subclass of another class.
- (void)foo:(Class)clazz
{
if ([clazz respondsToSelector:#selector(myClassMethod)])
{
[clazz myClassMethod];
}
else
{
// clazz does not implement that class method.
}
if ([clazz isSubclassOfClass:[MyBaseClass class]])
{
[clazz myClassMethod];
}
else
{
// clazz is not a subclass of MyBaseClass.
}
}
Try this:
if ( [clazz isKindOfClass:[MyBaseClass class]] ) {
[clazz myClassMethod];
}
Related
I need to write a method that returns a class.
#implementation MyFactory
- (Class)defaultViewClass {
return [MyView class];
}
#end
The returned class is not type safe. You can cast it to any random class. You can also call any random initializer method without a compiler error.
MyFactory *factory = [MyFactory new];
Class viewClass = [factory defaultViewClass];
RandomView *view = [[viewClass alloc] initWithRandomStuff];
How do I redefine my method to return a Class of a particular type? I want to do something like this, but the compiler complains that it can't return specific class types.
- (AbstractViewClass)defaultViewClass {
return [ConcreteViewClass class];
}
The reason why I need to deal with classes rather than object pointers is because I need to call a static function polymorphically.
You won't get the benefit of compile-time type checking when working with Class objects in Objective-C due to the dynamic nature of the language. However, you can enforce such constraints in a strictly typed language like Swift.
protocol AbstractView {
init(parameter: Int)
}
class ConcreteView: AbstractView {
required init(parameter: Int) { }
}
class ViewFactory {
func defaultViewClass() -> AbstractView.Type {
return ConcreteView.self
}
}
let factory = ViewFactory()
let viewClass = factory.defaultViewClass()
let view = viewClass.init(parameter: 0)
print("\(view.self)", terminator: "") // Prints "ConcreteView"
AbstractView is a protocol as there is no concept of an abstract class in Swift, although it could just as easily be a class.
Say I have a method:
- (void)method:(id)anObject
{
// do something
}
As we all know Objective-C classes or objects too. So I've been wondering how do I determine if the id passed inside the method an INSTANCE of a class or a CLASS itself? Because it may be used it like:
[object method:[NSObject new]];
or:
[object method:[NSObject class]];
how can I find out that the passed argument is an instance? I can't check it just by sending class message because for a class it will return the same result as for an instance
EDITED
The approach provided by Alexander does not suits me because I don't know what class will be passed so I can't check for membership of a particular class. I just need to know if it is a class (no matter what class) or is an instance (of no matter what class)
Classes return self from the class message, instances return their class. So just test if the result from class is identical to the receiver:
- (void)method:(id)anObject
{
if (anObject == [anObject class]) {
// it's a class
} else {
// it's an instance
}
}
Edit: Though simple, there are two subtle problems with above code:
The receiver has to implement class. All NSObject (and NSProxy) derived classes do so but for custom root classes this might not be the case. Extremely rare.
If a class does not return self the code would break. This might be the case with classes of objects that are being observed (KVO). Not so rare.
Here's code that will always work and is immune to these issues:
#import <objc/runtime.h>
- (void)method:(id)anObject
{
if (class_isMetaClass(object_getClass(obj))) {
// it's a class
} else {
// it's an instance
}
}
I have class tructure like this
#interface SuperClass: NSObject
+ (void) someName;
#end
#interface MyClass: SuperClass
#end
There is the case that i only want to call the someName if it is a class method of MyClass not MyClass's superclass. Since [[MyClass class] respondsToSelector:#selector(someName)] return YES if a class or its super response to the selector. How to tell that MyClass doesnt contain tosomeName?
In my application i want to print the string that contains chains of string return from a class method.
Take abve class structure as a example, i want to print something like:
somenameFromSuper.somenameFromClass.someNameFromeSubClass.
if a class doesnot implement someName method, i want to replace it by `notavailable, for ex:
somenameFromSuper.notavailable.someNameFromeSubClass.
_Bool class_implementsMethodForSelector( Class cls, SEL selector )
{
unsigned methodsCount;
Method* methods = class_copyMethodList(cls, &methodsCount);
for (unsigned methodIndex=0; methodIndex<methodsCount; methodIndex++)
{
if (method_getName(methods[methodIndex]) == selector)
{
break;
}
}
free(methods);
return methodsIndex<methodsCount;
}
…
Class classToTest = …;
classToTest = object_getClass(classToTest); // For checking class methods
if (class_implementsMethodForSelector(classToTest, #selector(someName))
{
…
}
else
{
…
}
Typed in Safari.
Edit: Made a function of it. Still typed in Safari.
I'm having some trouble accessing a Swift Singleton from Objective-C.
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}
swiftSharedInstance can not be reached.
Nicky Goethlis's answer is correct but I just want to add another way of Singleton creation termed as One line Singleton" in Swift which I came across recently and it does not use Struct:
Singleton.swift
#objc class Singleton: NSObject {
static let _singletonInstance = Singleton()
private override init() {
//This prevents others from using the default '()' initializer for this class.
}
// the sharedInstance class method can be reached from ObjC. (From OP's answer.)
class func sharedInstance() -> Singleton {
return Singleton._singletonInstance
}
// Some testing
func testTheSingleton() -> String {
return "Hello World"
}
}
SomeObjCFile.m
Singleton *singleton = [Singleton sharedInstance];
NSString *testing = [singleton testTheSingleton];
NSLog(#"Testing---> %#",testing);
Swift 5 and above
final class Singleton: NSObject {
#objc static let shared = Singleton()
#objc var string: String = "Hello World"
private override init() {}
}
use in Objective-C
#import <ProjectName-Swift.h> // change ProjectName to actual project name
NSLog("Singleton String = %#", [Singleton shared].string);
For now I have the following solution. Maybe I am overlooking something that would enable me to access "swiftSharedInstance" directly?
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
// the sharedInstance class method can be reached from ObjC
class func sharedInstance() -> SingletonTest {
return SingletonTest.swiftSharedInstance
}
// Some testing
func testTheSingleton() -> String {
return "Hello World"
}
}
Then in ObjC I can get the sharedInstance class method (after importing the xcode generated swift header bindings)
SingletonTest *aTest = [SingletonTest sharedInstance];
NSLog(#"Singleton says: %#", [aTest testTheSingleton]);
To make members of the SingletonTest class accessible (swiftSharedInstance is a member of this class), use #objcMembers modifier on the class, or add #objc modifier directly on the swiftSharedInstance:
#objc #objcMembers class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}
Or:
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
#objc class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}
After creating the Bridging header, be sure to have the Objective-C Generated Interface Header Name set in your Build Settings from your app target. If the value is empty, add the following value:
$(SWIFT_MODULE_NAME)-Swift.h
You need add #objc property wrapper to your singleton:
#objc final class Singleton: NSObject {
#objc static let sharedInstance = Singleton()
#objc func foo() { }
}
Then, in the Objective-C class, import the following:
// Replace the "App" with your Target name.
#import "App-Swift.h"
Finally, after compiling the project, you will be able to use your singleton from Swift inside your Objective-C class:
[[Singleton sharedInstance]foo];
You pretty much have it. To use Swift classes in Obj-C you both need to #import "SingletonTest-Swift.h the generated header or forward declaration with #class MySwiftClass.
Additionally the class needs to inherit from an Obj-C class like you have don here with NSObject or be marked with #objc to expose it. You don't need to do both though, #objc is there to be a more granular option when choosing things to expose.
Apple has some good documentation on all of this and there are two different WWDC sessions you can watch on the topic of Obj-C interoperability as well.
Don't forget to set sharedInstance as public
public final class TestSwiftMain: NSObject {
#objc public static let sharedInstance = TestSwiftMain()
private override init() {}
#objc public func test() {
print("testing swift framework")
}
}
Using it in Objc
[[testSwiftMain sharedInstance] test];
Update 12 Oct 2022
ObcMember So you won't have to write objC behind every function
Swift Class
#objcMembers class SwiftHelpingExtentions: NSObject {
static let instanceShared = SwiftHelpingExtentions()
func testingMethod() {
print("testing")
}
}
}
On objective C View Controller
import : #import "App-Swift.h"
Call The method:
[SwiftHelpingExtentions.instanceShared testingMethod];
In my code I want to know if a method is a class method or an instance method. The code I am currently using works, but I wonder if there is a beter way.
Current code to "detect" if it is a class method or instance:
Method method = class_getInstanceMethod(class, selector);
if (method) {
__strong Class object = [[class alloc] init];
objc_msgSend(object, selector);
}else {
method = class_getClassMethod(class, selector);
if (method) {
objc_msgSend(class, selector);
}
}
There's very little that you can improve beyond two if statements. You can use respondsToSelector: method instead, but since you do not have an object to start with, you will end up with an if inside an if rather than an a better-looking else if:
if ([class respondsToSelector:selector]) {
// Call class method
} else {
id object = [[class alloc] init];
if ([object respondsToSelector:selector]) {
// Call instance method
}
}
If you could modify your program flow to start with an object instead of a class, you coud do this instead:
if ([object respondsToSelector:selector]) {
// Call instance method
} else if ([[object class] respondsToSelector:selector]) {
// Call class method
}