Swift: Equivalent Objective-C Runtime Class

swift: Equivalent objective-c runtime class

First, it's hard to translate that code to Swift without knowing what you used that class object for in Objective-C.

In Objective-C, class objects are objects, and the type Class can hold a pointer to any class object. However, when Objective-C APIs are bridged to Swift, the type Class is converted to AnyClass! in Swift, where AnyClass is defined as AnyObject.Type. Types in Swift are not objects, and thus are not directly equivalent to class objects in Objective-C. However, if you intend to use an Objective-C API from Swift, it will have been bridged to expect AnyClass anyway, so you have to pass a type. You can get the type of any expression using .dynamicType; for example:

self.dynamicType

(If you really want to get the class object as an Swift object the same way as in Objective-C, and not as a Swift type, there are some convoluted ways to do that too.)

However, your description of your problem reveals another issue. If you just want to get the type of an object, and self is an object, then var klass: AnyClass = object_getClass(self) should have worked, since object_getClass() takes an AnyObject and returns an AnyClass. The only explanation for it not working is if self is not an object. Your error message reveals that, indeed, self is a type, not an object.

self is a type if this code is running in a class method. You should have really given context for your code (obviously, you didn't put Class class = [self class]; at the top level of a file), because taken out of context it's easy to misunderstand. In Objective-C Cocoa, there are two very different methods named class: an instance method, -class, which returns the class of the object, and a class method, +class, which simply returns the (class) object it's called on. Since your code is in a class method, in Objective-C, self points to a class object, and [self class] runs the class method +class, which just returns the object it's called on. In other words, [self class] is exactly identical to self. You should have just written self all along, but didn't realize it.

So the answer is that the Objective-C should have been

Class class = self;

and similarly the Swift should be

var klass: AnyClass = self

Swift Objective-C runtime class naming

They're not random. They're the length of the following value. This is similar to common C++ name mangling and supports identifiers that may have fairly arbitrary characters in them without needing some new separator character. It also can make them easier to parse, especially in C.

In this particular case, it's _TtC then "an eleven character module name" then the module name and then "a fourteen character class name" and the class name. I assume C is class. Not sure about Tt (maybe "type").

What are objective c runtime features?

Quoting the apple docs

The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps. Objective-C runtime library support functions are implemented in the shared library found at /usr/lib/libobjc.A.dylib.

That API is useful primarily for developing bridge layers between Objective-C and other languages, or for low-level debugging. You most likely don't need to use it.

Even when written without a single line of Objective-C code, every Swift app executes inside the Objective-C runtime, so that's why you can access it.

You can do things like swizzling

equivalent to [obj class] in Swift?

The equivalent of [obj class] in SWIFT is :

obj.dynamicType

Getting class name in SWIFT equivalent to Objective C [self class]?

You can use print("Class = \(type(of: self))" in Swift.

Swift runtime - calling superclass method

As Martin says, objc_msgSendSuper isn't available in Swift because it's a C variadic function, which Swift doesn't import due to the lack of type safety.

One alternative is to use class_getMethodImplementation in order to get a pointer to the function to call for a selector on a given class type. From there, you can cast it to a function type which Swift can call using unsafeBitCast, taking care that the parameter and return types match up.

For example:

import Foundation

class C {
@objc func foo() {
print("C's foo")
}
}

class D : C {
override func foo() {
print("D's foo")
}
}

let d = D()

let superclass: AnyClass = class_getSuperclass(type(of: d))!
let selector = #selector(C.foo)

// The function to call for a message send of "foo" to a `C` object.
let impl = class_getMethodImplementation(superclass, selector)!

// @convention(c) tells Swift this is a bare function pointer (with no context object)
// All Obj-C method functions have the receiver and message as their first two parameters
// Therefore this denotes a method of type `() -> Void`, which matches up with `foo`
typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> Void

let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
fn(d, selector) // C's foo

Note that like objc_msgSendSuper this assumes that the return type bridged to Obj-C is layout compatible with a pointer. This is true in most cases (including yours), but wouldn't be true for a method returning a type such as CGRect, which is represented in Obj-C using a C structure type.

For those cases, you would need to use class_getMethodImplementation_stret instead:

import Foundation

class C {
@objc func bar() -> CGRect {
return CGRect(x: 2, y: 3, width: 4, height: 5)
}
}

class D : C {
override func bar() -> CGRect {
return .zero
}
}

let d = D()

let superclass: AnyClass = class_getSuperclass(type(of: d))!
let selector = #selector(C.bar)
let impl = class_getMethodImplementation_stret(superclass, selector)!

typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> CGRect

let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
let rect = fn(d, selector)
print(rect) // (2.0, 3.0, 4.0, 5.0)

The distinction between class_getMethodImplementation and class_getMethodImplementation_stret is due to the difference in calling convention – a word sized type can be passed back through a register, however a structure of larger size needs to be passed back indirectly. This matters for class_getMethodImplementation because it could pass back a thunk for message forwarding in the case where the object doesn't respond to the selector.

Another option is to use method_getImplementation, which doesn't perform message forwarding and therefore doesn't need to distinguish between stret and non-stret.

For example:

let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)

However bear in mind that the documentation notes:

class_getMethodImplementation may be faster than method_getImplementation(class_getInstanceMethod(cls, name)).

At runtime, how do I list all Objective-C class methods for a given class?

Each Objective-C @implementation is represented at run time by an object, the “class object”. The class object manages the method dictionary for instance methods.

For example, for UIView, there is an UIView class object. When you call NSClassFromString(@"UIView") (or [UIView class] or [UIView self]), it returns the UIView class object. The UIView class object manages the method dictionary for UIView instance methods.

The UIView class object is itself an instance of another class, called the UIView metaclass. The UIView metaclass is represented by another class object, the UIView metaclass object. The UIView metaclass object manages the method dictionary for UIView class methods.

So, to get the class method list for UIView, you need to pass the UIView metaclass object to class_copyMethodList.

Class UIView_class = NSClassFromString(@"UIView");
Class UIView_metaclass = object_getClass(UIView_class);
unsigned int count;
Method *classMethods = class_copyMethodList(UIView_metaclass, &count);

Swift - Objective-C load class method?

Prior to Swift 1.2:

override class func load() {
NSLog("load");
}

EDIT:

As of Swift 1.2 you can no longer override the load method. Look into the method initialize instead, it behaves different than load though, it get's called the first time the class is being referenced somewhere rather than on application initial load



Related Topics



Leave a reply



Submit