Swift isa pointer remapping or other supported method swizzling - objective-c

Do Swift classes have something like an isa pointer that can be remapped?
We've seen that Swift uses a more static method dispatch than objective-C, which (unless a class dervices from Foundation/NSObject) prevents the style of swizzling based on remapping method implementations at runtime.
I'm wondering how we'll implement method interception-based dynamic features like the observer pattern, notifications, etc? Currently all this stuff is provided by the Objective-C layer, and can be easily integrated into Swift. But, if we want to provide these kinds of features in a framework (or app) of our own, is it necessary to implement them in Objective-C? I would assume there's a way to do it 'natively'.
Another kind of swizzling common to objective-C is remapping the isa-pointer to generate a sub-class on the fly. Is this kind of swizzling supported in Swift? If not what is the supported way of intercepting arbitrary method invocations?
Edit: As #jatoben points out, as of arm64 isa-remapping must be done by calling object_setClass() and not by accessing the value directly. This is still referred to as 'isa pointer swizzling'

It looks like both method exchanging and the isa pointer remapping technique only works if the Swift class has NSObject as a super-class (either directly or further up). It does not currently work, when the Swift class has no super-class or some other non-Foundation base class.
The following test shows this:
Class: Birdy
class Birdy: NSObject {
func sayHello()
{
print("tweet tweet")
}
}
Class: HodorBirdy
class HodorBirdy: Birdy {
override func sayHello()
{
super.sayHello()
print("hodor hodor")
}
}
Test:
func testExample() {
let birdy : Birdy = Birdy()
object_setClass(birdy, HodorBirdy.self)
birdy.sayHello();
}
And the output was as expected:
tweet tweet
hodor hodor
In this test both the base-class and sub-class were created in advance. Though they could also be created dynamically using the Objective-C runtime as long as the class has NSObject as an ancestor.
When a Swift class does not derive from the Objective-C foundation, then the compiler will favor static- or vtable-based dispatch, therefore its not clear how method interception will work at all in this case!
Unless the language/compiler make a specific allowance for it, we'll be foregoing dynamism in favor of performance. (Interception, which is the foundation of 'dynamic' behaviors can either be done at compile-time or run-time. In the case of static- or vtable-dispatch without a virtual machine, only compile-time applies).

I can't answer your question about swift "isa" equivalent, but I think I know part of the answer to your underlying question.
Property Observers seem to be the built-in means for the Observer Pattern. Instead of runtime discovery of "type" (RTTI, what-have-you) it is woven in explicitly.
From 'The Swift Programming Language' page 345:
Property observers observe and respond to changes in a property's
value. Property observers are called every time a property's value is
set, even if the new value is the same as the property's current
value.
You can add property observers to any stored properties you define,
apart from lazy stored properties. You can also add property observers
to any inherited property (whether stored or computed) by overriding
the property within a subclass.
You have the option to define either or both of these observers on a
property:
willSet is called just before the value is stored.
didSet is called immediately after the new value is stored.
I am not sure how this is all going to work out, but I am intrigued.
Relying on run-time type discovery also seems to run counter to strong static type orthodoxy.

Related

objective-c override system method

I find a c++ system method causes crash in ios and I try to swizzle the method. However, I do not how to do that because it's a method of a c++ class. Anyone know whether can I do that?
Method swizzling is unique to objective-c (and even there one has to use it carefully), and is not applicable to c++.
I suppose that you don't have access to the source code of the c++ class.
Then the only way to "exchange" the implementation of a method at a specific c++-class is to derive a subclass, override the method, and then make sure that the subclass is used instead of the other class. It is still unlikely that you have a chance; the method being not virtual, the class to be replaced being used in non-polymorphic ways, the class to be replaced already having several subclasses, each of these points will prevent you from being successful.
Good luck though!

When to use respondsToSelector in swift?

My question is more of a general question rather than a specific problem. It appears that for a class that is written in Swift, you can use the optional workflow to verify if the method does exist(?). If it doesn't, you can assume it returned nil.
This appears only to apply if the class is written in Swift. (Is that a correction assumption)?
Now, if you are referencing an Objective-C class and want to check to see if a method exists, you can use the respondsToSelector check with the selector #selector.
Is this meant to only be used on Objective-C classes and Swift classes, protocols, protocol /class extensions that inherit from Objective-C classes?
Let me know if I should expand on any part of my questions.
There is scarcely any need to call respondsToSelector explicitly in Swift. The only time when you'd want to do it is in dealing with an NSObject, and in that case, it is presumably an AnyObject or a delegate protocol adopter, and in either case you can just use question-mark syntax (which calls respondsToSelector for you).
It appears that for a class that is written in Swift, you can use the
optional workflow to verify if the method does exist(?). If it
doesn't, you can assume it returned nil.
This appears only to apply if the class is written in Swift. (Is that
a correction assumption)?
This is not correct. When you say "use the optional workflow to verify if the method does exist", I assume you mean what happens when you call methods on type AnyObject described in "Unrecognized Selectors and Optional Chaining" in the "id Compatibility" section in the Using Swift with Cocoa and Objective-C guide.
This feature only works with Objective-C (#objc) methods. AnyObject allow you to dynamically access only Objective-C (#objc) methods.
You can call any Objective-C method and access any property on an
AnyObject value without casting to a more specific class type. This
includes Objective-C compatible methods and properties marked with the
#objc attribute.
You cannot use AnyObject to call pure Swift (non-#objc) methods.

Extend all Swift Objects

Is it possible to extend all existing Swift objects as when adding a category over NSObject in Objective C?
According to this article, all Swift objects inherit from the SwiftObject class, but I can't add an extension to it.
Is there any solution to this?
No. Swift objects do not actually inherit from any root base class. The compiler may insert one as an implementation detail, but the language does not have this.
The solution is a function, and usually a generic function, rather than a method. Something that applies to "every kind of object" isn't really encapsulated. There are very few actions that apply to every conceivable thing in the universe. But functions can map absolutely anything to absolutely anything else, so are a better tool here.
Just as a note, not all objects inherit from NSObject either, and it's not a language requirement. But you're correct that the vast majority do. Take a look at NSProxy for the top of a non-NSObject tree (it implements the NSObject protocol, but does not inherit from the NSObject class). That's why id is not the same thing as NSObject*.
To your question about associated objects, this is built-in:
import Foundation
class Thing { }
let thing = Thing()
var MyKey: Character = "0"
objc_setAssociatedObject(thing, &MyKey, "I'm a value!", objc_AssociationPolicy(OBJC_ASSOCIATION_COPY))
println(objc_getAssociatedObject(thing, &MyKey))
Is this what you were trying to create? Note that objc_setAssociatedObject is also a function, not a method, in ObjC.

Can an ObjC class object conform to a protocol?

Is there a way to indicate to the compiler that a class object conforms to a protocol?
As I understand, by creating +(void)foo class methods, an instance of that class object will have those methods as instance methods. So, as long as I create +(void)foo methods for all required protocol methods, I can have a class object act as a delegate.
My problem of course is that in the class's header file, I only know how to indicate that instances of the class conform to the protocol (as is typically the case). So, the best I've figured out is to cast the class object like so:
something.delegate = (id<SomethingDelegate>)[self class]
Any ideas?
Related, but different:
ObjC: is there such a thing as a "class protocol"?
What you're doing now is correct as it will silence warnings which is your goal. You will be sending the class object messages defined in the protocol for instances which is a bit confusing, but the runtime doesn't care.
Think about it this way: you want to set a delegate to an object that responds to the messages defined in the protocol. Your class does this, and your class is also an object. Therefore, you should treat your class like an object that conforms to that protocol. Therefore, what you've written is completely correct (based on what you're trying to do).
One thing to note, though, is this class will not properly respond to conformsToProtocol:. This is generally okay for a delegate setup anyway (delegates don't usually check if the class conforms — they just check if it can respond to a selector).
As a side note, one thing you can do syntactically is:
Class<SomethingDelegate> variable = (Class<SomethingDelegate>)[self class];
The difference here is that the compiler will use the class methods from the protocol instead of instance messages. This is not what you want in your case, though.
There is no Objective-C syntax to indicate that a metaclass conforms to a protocol.
I think you can do it at runtime, by using class_addProtocol on the metaclass. But I haven't tried it.
I guess you could also write a +conformsToProtocol: method on your class, and lie about your conformance. This could have unexpected side-effects, since there's already a +conformsToProtocol: on NSObject (in addition to -conformsToProtocol:).
Neither of these will eliminate the need for a cast to shut the compiler up. Just use a singleton.

Implement a pure virtual method in Objective-C

I want to go to there. Seriously though, how does one implement a pure virtual method in an "Apple" way? Do you use a Protocol with your base class and throw exceptions on those methods?
When you program in Objective-C you need to purge your mind of such things as virtual methods. You don't call methods on Objective-C objects, you send messages to them. Objects either respond to messages or they don't, but due to the dynamic binding, you can't tell this until run time.
Thus, you can declare a method on a base object and not not provide an implementation, no problem (except for the compiler warning), but you can't have the compiler flag up when you directly instantiate an object with such methods and it won't throw an error at runtime unless you actually send that message to the object.
The best way to create "virtual" base classes (in my opinion) is to declare the method and give it a stub implementation that throws a suitable exception.
In Objective-C, there is no pure virtual support as in C++.
A simulation would be that you declare a method in your interface but don't implement it in your .m file. Of course you'd get compiler warnings but IIRC you can turn those off. But you won't get warnings/errors if you don't overwrite them in the subclass, which you get in C++ (IIRC).
An alternative would be to implement them with just an NSAssert(NO, #"Subclasses need to overwrite this method"); body. Still, you'd only catch this at runtime, not compiletime.
Depending on what you're doing the delegate pattern may be more appropriate than a subclass, where the delegate is defined as id<YourDelegateProtocol>. The compiler will generate a warning if the required methods in the delegate protocol are not implemented.
Subclassing is generally avoided in Objective-C since objects cannot inherit from multiple superclasses but they can implement multiple protocols.
You should use the:
- (void)doesNotRecognizeSelector:(SEL)aSelector method.
As noted by Apple, here: https://developer.apple.com/library/mac/#documentation/cocoa/reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
You have a few options, but you're on the right track.
ObjC doesn't support this directly, forcing subclasses to implement a protocol is the best way to check it at compilation.
'Secretly' implementing the method in the base class and asserting is what I do to confirm the subclasser has subclassed correctly at runtime. Some people have mixed feelings about assertions, or must leave them active, so that's not always a good solution.
You can also force subclasses use a specific class constructor and initialization sequence, then verify they have implemented everything required before returning an instance, in case compiler warnings don't cut it.
But ObjC is missing some lang features which allow clients to shoot themselves in the foot, or workaround what they wish so... you shouldn't get too stuck on enforcing it.
note: Exceptions are very uncommon (and a bit unsafe, too) in ObjC.
A virtual method is a method whose behavior can be overridden within an inheriting class by a function with the same signature (i.e same name with same number of params and type of params).
Example:-
#implementation BaseClass
-(void)viewDidLoad
{
[self virtualMethod:123];
}
-(void)virtualMethod:(int)param
{
//implement this method in subclass
}
#end
////////////////////////////////////////////////////
#interface ChildClass:BaseClass
#end
#implementation ChildClass
-(void)virtualMethod:(int)param
{
NSLog(#"There is no keyword "Virtual" in Objective C.");
}
#end
Output:-
"There is no keyword "Virtual" in Objective C."