I tested out some isa swizzling with Swift, and found that it only works when NSObject is a super-class (directly or further up), or by using the '#objc' decoration. Otherwise it will follow a static- and vtable-dispatch style, like C++.
Is it normal to define a Swift class without a Cocoa/NSObject base class? If it is I'm concerned this means foregoing much of the dynamism of Objective-C, such as method interception and run-time introspection.
Dynamic run-time behavior sits at the heart of features like property observers, Core Data, Aspect Oriented Programming, Higher Order Messaging, analytical & logging frameworks and so on.
Using Objective-C's style of method invocation adds around 20 machine code operands to a method call, so in certain situations (many tight calls to methods with small bodies) C++ style static and vtable dispatch can perform better.
But given the general 95-5 rule (95% of performance gains come from tuning 5% of the code), doesn't it makes sense to start with the powerful dynamic features and harden where necessary?
Swift classes that are subclasses of NSObject:
are Objective-C classes themselves
use objc_msgSend() for calls to (most of) their methods
provide Objective-C runtime metadata for (most of) their method implementations
Swift classes that are not subclasses of NSObject:
are Objective-C classes, but implement only a handful of methods for NSObject compatibility
do not use objc_msgSend() for calls to their methods (by default)
do not provide Objective-C runtime metadata for their method implementations (by default)
Subclassing NSObject in Swift gets you Objective-C runtime flexibility but also Objective-C performance. Avoiding NSObject can improve performance if you don't need Objective-C's flexibility.
Edit:
With Xcode 6 beta 6, the dynamic attribute appears. This allows us to instruct Swift that a method should use dynamic dispatch, and will therefore support interception.
public dynamic func foobar() -> AnyObject {
}
I also found that if basing a Swift class on NSObject, I saw some unexpected run-time behaviour that could hide coding bugs. Here is an example.
In this example, where we don't base on NSObject, the compiler correctly spots the error in testIncorrect_CompilerShouldSpot,
reporting "... 'MyClass' is not convertible to 'MirrorDisposition'"
class MyClass {
let mString = "Test"
func getAsString() -> String {
return mString
}
func testIncorrect_CompilerShouldSpot() {
var myString = "Compare to me"
var myObject = MyClass()
if (myObject == myString) {
// Do something
}
}
func testCorrect_CorrectlyWritten() {
var myString = "Compare to me"
var myObject = MyClass()
if (myObject.getAsString() == myString) {
// Do something
}
}
}
In this example, where we base on NSObject, the compiler doesn't spot the error in testIncorrect_CompilerShouldSpot:
class myClass : NSObject {
let mString = "Test"
func getAsString() -> String {
return mString
}
func testIncorrect_CompilerShouldSpot() {
var myString = "Compare to me"
var myObject = MyClass()
if (myObject == myString) {
// Do something
}
}
func testCorrect_CorrectlyWritten() {
var myString = "Compare to me"
var myObject = MyClass()
if (myObject.getAsString() == myString) {
// Do something
}
}
}
I guess the moral is, only base on NSObject where you really have to!
According to the language reference, there is no requirement for classes to subclass any standard root class, so you can include or omit a superclass as needed.
Note that omitting a superclass from the class declaration, doesn't assign a implicit base superclass of any kind. It defines a base class, which will effectively become the root for an independent class hierarchy.
From the language reference:
Swift classes do not inherit from a universal base class. Classes you
define without specifying a superclass automatically become base
classes for you to build upon.
Trying to reference super from a class without a super class (i.e. a base class) will result in a compile time error
'super' members cannot be referenced in a root class
The following is copied from Apple's Swift-eBook and gives an appropriate answer to your question:
Defining a Base-Class
Any class that does not inherit from another class is known as a base class.
Swift classes do not inherit from a universal base class. Classes you define without specifying a superclass automatically become base classes for you to build upon.
Reference https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251
I believe that the vast majority of Swift data will not be objc. Only those parts that do need to communicate with te Objective C infrastructure will be explicitly marked as such.
To which extent runtime introspection will be added to the language, I do not know. Method interception will likely become only possible if the method explicitly allows it. This is my guess, but only the language designers within Apple really know where they are really heading.
It is normal. Look at the design goals of Swift: The goal is to make huge classes of programming problems disappear. Method swizzling is probably not one of the things that you want to do with Swift.
Related
I have to implement this method in a DataSource protocol of an Objective-C library
(nullable id<SomeClass>)someMethod;
I am trying to implement it in my class in Swift, specifically, the AppDelegate, with what I believe keeps equal the signature
extension AppDelegate: LIBDataSource {
#objc func someMethod<T: SomeClass>() -> T? {
return nil // temporary
}
}
The problem is that
As it is, I have a warning and an error. The error says Method cannot be marked #objc because it has generic parameters (the warning below is also shown)
If I remove #objc, the warning says Non-#objc method 'someMethod()' cannot satisfy optional requirement of #objc protocol LIBDataSource
Is there a way to implement a generic Obj-C method of a Obj-C protocol in Swift? Or do I have to do a separate Objective-C class to accomplish this?
The syntax
id<SomeClass>
is nothing to do with lightweight generics, it means "any Objective-C class as long as it conforms the protocol SomeClass". Your method doesn't need to be generic but it does need to return an object that conforms to the SomeClass protocol. It's signature should probably be something like
func someMethod() -> SomeClass?
Objective-C APIs do a lot of object construction using class methods:
+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session
Sometimes I even see these appearing in old Swift tutorials as class methods, but when I try to call them, I get compiler errors like these:
date() is unavailable, use object construction NSDate()
urlWithString() is unavailable, use object construction NSURL(string:)
layerWithSession() is unavailable, use object construction AVCaptureVideoPreviewLayer(session:)
(I see declarations like convenience init(URL url: NSURL) in the docs, and in Xcode's generated Swift interfaces for SDK classes, but what do they have to do with any of this?)
This even happens when I declare class methods in my own ObjC code, like for a singleton pattern:
#interface MyManager: NSObject
+ (instancetype)manager;
#end
When I try to use them from Swift, I get the same kind of errors:
let manager = MyManager.manager()
// error: 'manager()' is unavailable, use object construction 'MyManager()'
What gives? How do I fix these errors?
Swift does away with the alloc/init initialization pattern from Objective-C and replaces it with a construction syntax similar to that seen in Java or C++.
When, in a Swift class interface (or in Apple's documentation), you see something like:
class Thing {
init(number: Int)
}
That means you can create a Thing by calling it with the following "construction syntax":
let thingTwo = Thing(number: 2)
It doesn't matter if it's a class or a struct, or if you see other things like convenience or required or public before init... that's how you use it. (Of course, replace Thing with the type you're using, number with any parameter label(s) in the actual initializer you want, and 2 with an appropriate value for that parameter.)
When classes originally defined in ObjC are imported into Swift, the Swift compiler does a bunch of things to make those APIs look and feel more like native Swift code. One of those things is to replace the common pattern of using class methods as convenience constructors with real initializers.
All the examples from the question:
+ (NSDate *)date;
+ (NSURL *)urlWithString:(NSString *)string;
+ (instancetype)layerWithSession:(AVCaptureSession *)session;
... are such convenience constructors. That means they import into Swift not as class methods (NSDate.date(), NSURL.urlWithString(), etc), but as initializers:
class NSDate {
init()
}
class NSURL {
init?(string: String)
}
class AVCaptureVideoPreviewLayer {
init!(session: AVCaptureSession)
}
... and you call them with object construction syntax:
let date = NSDate()
let url = NSURL(string: "http://apple.com")
let layer = AVCaptureVideoPreviewLayer(session: mySession)
In fact, when you see error messages about "is unavailable, use object construction", Xcode pretty much always offers an automatic fix-it to insert the proper syntax.
How does this work? Swift looks for possible "base names" of a class and matches between those names and the class method / initializer method names. (It also checks for class methods that return an instance of that class, initializers that start with "init...", etc.)
For example, Swift sees the following as a convenience constructor:
#interface PDQMediaManager: NSObject
+ (PDQMediaManager *)manager;
#end
This may be a problem if what you (or the original developer of PDQMediaManager) intend is for that class to act as a singleton, and the +manager method to return the singleton instance — if a Swift developer writes PDQMediaManager(), they'll be constructing a new instance instead of retrieving the singleton.
In this case, one good solution is to rename the singleton method:
+ (PDQMediaManager *)defaultManager;
This won't get seen as a convenience constructor, so Swift clients can call PDQMediaManager.defaultManager() to get the singleton instance.
You can also use the NS_SWIFT_NAME to rename the singleton method only for Swift clients:
+ (PDQMediaManager *)manager NS_SWIFT_NAME(defaultManager());
If you don't control the codebase with the offending method, your best bet is probably to write your own ObjC code to forward to the singleton method and expose that to your Swift code via a bridging header.
You can declare functions as inlines like this:
#ifdef DEBUG
void DPrintf(NSString *fmt,...);
#else
inline void DPrintf(NSString *fmt,...) {}
#endif
so that when you're not in DEBUG, there's no cost to the function because it's optimized and inline. What if you want to have the same thing but for a class method?
My class is declared like this:
#interface MyClass : NSObject {
}
+ (void)DPrintf:(NSString *)format, ...;
// Other methods of this class
#end
I want to convert 'DPrintf' into something similar to the inline so that there's no cost to invoking the method.
But I can't do this:
inline +(void)DPrintf:(NSString *)format, ...; {}
How can I have a zero-cost static method of a class turned off for non-debug compilations?
Be careful. Objective-C methods are not the same as C functions. An Objective-C method is translated by the compiler into the objc_msgSend() function call; you don't have control over whether a method is inline or not because that is irrelevant. You can read more about the Objective-C runtime here (Objective-C Runtime Programming Guide), here (Objective-C Runtime Reference), and here (CocoaSamurai post), and a quick Google search should bring up more info.
There is no such thing as a static method in Objective-C. There are only class methods, which are just like instance methods except they belong to a class. This means that, just like instance methods, a message send to a class must go through the message dispatch machinery to determine the correct method to call, and that is done at runtime. You could inline the call to the method dispatch machinery, but the method body still can't be inlined without a crazy level of optimization that doesn't exist in any Objective-C compiler at the moment.
At any rate, this is a micro-optimization. If profiling shows it to be necessary (which it almost never will), then you can go through the gymnastics. Otherwise, worry about the actual performance concerns in your application.
Yes!
You can accomplish this with blocks
-(void)viewDidLoad {
void(^inlineFunction)(int) = ^(int argument) {
NSLog(#"%i", argument);
};
inlineFunction(5);//logs '5'
}
Apple even documents this here (archive).
Enjoy!
I'd like to specify an Objective-C protocol with an optional routine. When the routine is not implemented by a class conforming to the protocol I'd like to use a default implementation in its place. Is there a place in the protocol itself where I can define this default implementation? If not, what is the best practice to reduce copying and pasting this default implementation all over the place?
Objective-C protocols have no affordance for default implementations. They are purely collections of method declarations that can be implemented by other classes. The standard practice in Objective-C is to test an object at runtime to see if it responds to the given selector before calling that method on it, using -[NSObject respondsToSelector:]. If e object does not respond to the given selector, the method isn't called.
One way you could achieve the result you're looking for would be to define a method encapsulating the default behavior you're looking for in the calling class, and call that method if the object doesn't pass the test.
Another approach would be to make the method be required in the protocol, and provide default implementations in the superclasses of any classes wherein you may not want to provide a specific implementation.
There are probably other options as well, but generally speaking there isn't a particular standard practice in Objective-C, except perhaps to just not call the given method if it hasn't been implement by the object, per my first paragraph, above.
There is no standard way for doing that as protocols should not define any implementations.
Since Objective-C comes with a neat runtime, you can of course add such a behavior if you really think you need to do it that way (and there's no possibility by achieving the same with inheritance).
Say you declared MyProtocol, then just add an interface with the same name in the .h file under your protocol declaration:
#interface MyProtocol : NSObject <MyProtocol>
+ (void)addDefaultImplementationForClass:(Class)conformingClass;
#end
And create a corresponding implementation file (using MAObjCRuntime for readability here, but the standard runtime functions wouldn't be much more code):
#implementation MyProtocol
+ (void)addDefaultImplementationForClass:(Class)conformingClass {
RTProtocol *protocol = [RTProtocol protocolWithName:#"MyProtocol"];
// get all optional instance methods
NSArray *optionalMethods = [protocol methodsRequired:NO instance:YES];
for (RTMethod *method in optionalMethods) {
if (![conformingClass rt_methodForSelector:[method selector]]) {
RTMethod *myMethod = [self rt_methodForSelector:[method selector]];
// add the default implementation from this class
[conformingClass rt_addMethod:myMethod];
}
}
}
- (void)someOptionalProtocolMethod {
// default implementation
// will be added to any class that calls addDefault...: on itself
}
Then you just have to call
[MyProtocol addDefaultImplementationForClass:[self class]];
in the initializer of your class conforming to the protocol and all default methods will be added.
A truly fascinating way is to use the runtime. At the start-up, very early in the program execution, do the following:
Enumerate all the classes, find classes which implement the protocol
Check if the class implements a method
If not, add to the class the default implementation
It can be achieved without that much trouble.
I agree with "w.m." A very nice solution is to put all the default implementations into an interface (with the same name as the protocol). In the "+initialize" method of any subclass it can simply copy any unimplemented methods from the default interface into itself.
The following helper functions worked for me
#import <objc/runtime.h>
// Get the type string of a method, such as "v#:".
// Caller must allocate sufficent space. Result is null terminated.
void getMethodTypes(Method method, char*result, int maxResultLen)
{
method_getReturnType(method, result, maxResultLen - 1);
int na = method_getNumberOfArguments(method);
for (int i = 0; i < na; ++i)
{
unsigned long x = strlen(result);
method_getArgumentType(method, i, result + x, maxResultLen - 1 - x);
}
}
// This copies all the instance methods from one class to another
// that are not already defined in the destination class.
void copyMissingMethods(Class fromClass, Class toClass)
{
// This gets the INSTANCE methods only
unsigned int numMethods;
Method* methodList = class_copyMethodList(fromClass, &numMethods);
for (int i = 0; i < numMethods; ++i)
{
Method method = methodList[i];
SEL selector = method_getName(method);
char methodTypes[50];
getMethodTypes(method, methodTypes, sizeof methodTypes);
if (![toClass respondsToSelector:selector])
{
IMP methodImplementation = class_getMethodImplementation(fromClass, selector);
class_addMethod(toClass, selector, methodImplementation, methodTypes);
}
}
free(methodList);
}
Then you call it in your class initializer such as...
#interface Foobar : NSObject<MyProtocol>
#end
#implementation Foobar
+(void)initialize
{
// Copy methods from the default
copyMissingMethods([MyProtocol class], self);
}
#end
Xcode will give you warnings about Foobar missing methods, but you can ignore them.
This technique only copies methods, not ivars. If the methods are accessing data members that do not exist, you could get strange bugs. You must ensure that the data is compatible with the code. It is as if you did a reinterpret_cast from Foobar to MyProtocol.
As Ryan mention there are no default implementations for protocols, another option to implementing in the superclass would be is to implement a "Handler" kind of class that can be contained in any class that want to provide the default implementation, the appropriate method then calls the default handlers implementation.
I ended up creating a macro that has a default implementation of the method.
I've defined it in the protocol's header file, and then it's just a one-liner in each implementation.
This way, I do not have to change the implementation several places, and it's done on compile time, so no run-time magic is necessary.
I'm new to Objective C and I haven't been able to find out if there is the equivalent of a static constructor in the language, that is a static method in a class that will automatically be called before the first instance of such class is instantiated. Or do I need to call the Initialization code myself?
Thanks
The +initialize method is called automatically the first time a class is used, before any class methods are used or instances are created. You should never call +initialize yourself.
I also wanted to pass along a tidbit I learned that can bite you down the road: +initialize is inherited by subclasses, and is also called for each subclasses that doesn't implement an +initialize of their own. This can be especially problematic if you naively implement singleton initialization in +initialize. The solution is to check the type of the class variable like so:
+ (void) initialize {
if (self == [MyParentClass class]) {
// Once-only initializion
}
// Initialization for this class and any subclasses
}
All classes that descend from NSObject have both +class and -class methods that return the Class object. Since there is only one Class object for each class, we do want to test equality with the == operator. You can use this to filter what should happen only once ever, versus once for each distinct class in a hierarchy (which may not yet exist) below a given class.
On a tangential topic, it's worth learning about the following related methods, if you haven't already:
- isMemberOfClass:(Class)aClass (true only for aClass itself)
- isKindOfClass:(Class)aClass (true for aClass and children)
+ isSubclassOfClass:(Class)aClass (same as above, but a class method)
Edit: Check out this post by #bbum that explains more about +initialize: https://web.archive.org/web/20201108095221/http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/
Also, Mike Ash wrote a nice detailed Friday Q&A about the +initialize and +load methods:
https://www.mikeash.com/pyblog/friday-qa-2009-05-22-objective-c-class-loading-and-initialization.html
There is the +initialize class method that will be called before a class is used.
A bit of an addendum to this topic:
There is another way to create a 'static constructor' in obj-c, using an __attribute directive:
// prototype
void myStaticInitMethod(void);
__attribute__((constructor))
void myStaticInitMethod()
{
// code here will be called as soon as the binary is loaded into memory
// before any other code has a chance to call +initialize.
// useful for a situation where you have a struct that must be
// initialized before any calls are made to the class,
// as they would be used as parameters to the constructors.
// e.g.
myStructDef.myVariable1 = "some C string";
myStructDef.myFlag1 = TRUE;
// so when the user calls the code [MyClass createClassFromStruct:myStructDef],
// myStructDef is not junk values.
}