Swift funtime

69
SWIFT FUNTIME MOBICONF 2014 BORIS BÜGLING - @NEONACHO

description

An exploration of the new (?) runtime and how the Objective-C runtime API can be used in Swift. Talk given at mobiconf 2014.

Transcript of Swift funtime

Page 1: Swift funtime

SWIFT FUNTIMEMOBICONF 2014

BORIS BÜGLING - @NEONACHO

Page 2: Swift funtime

COCOAPODS

Page 3: Swift funtime

CONTENTFUL

Page 4: Swift funtime

SWIFT

Page 5: Swift funtime

AGENDA▸ How do you even Swift?▸ What is a Swift object?

▸ Objective-C runtime in the age of Swift▸ Swift runtime▸ (some Q&A)

Page 6: Swift funtime

“Swift’s clean slate [...] is an opportunity to reimagine how software

development works.”

Page 7: Swift funtime

▸ Optionals▸ Tuples▸ Generics

▸ Pattern matching▸ Operator overloading▸ Namespaces▸ Type inference

▸ ...

Page 8: Swift funtime

EMOJI IDENTIFIERS! !class ! { func "() { }}

Page 9: Swift funtime

INTERFACES WITH C, AT FULL

SPEEDWATCH "SWIFT AND C" BY

MIKE ASH

Page 10: Swift funtime

DROPS C++ INTEROPERABILITY

Page 11: Swift funtime

Everyone is a Beginner

Page 12: Swift funtime

▸ The Swift Programming Language by

AND ALSO

▸ Swift by Tutorials: A Hands-On Approach▸ Your First Swift App

▸ Functional Programming in Swift

Page 13: Swift funtime

SWIFT COMMAND LINE TOOLS$ xcrun swiftWelcome to Swift! Type :help for assistance.

$ xcrun swiftc Foo.swift## will compile an executable

$ xcrun swift-stdlib-tool## assembles libraries for an application bundle

$ xcrun swift-demangle _TtCSs29_NativeDictionaryStorageOwner_TtCSs29_NativeDictionaryStorageOwner ---> Swift._NativeDictionaryStorageOwner

Page 14: Swift funtime

LET'S DROP CLOSE TO THE METAL

Page 15: Swift funtime

WHAT IS A SWIFT OBJECT?

Page 16: Swift funtime

IT DEPENDS

Page 17: Swift funtime

class MyObject : NSObject {}

Page 18: Swift funtime

▸ behaves like any old Objective-C object▸ instance variables are properties▸ fully interopable with ObjC

Page 19: Swift funtime

class MyObject {}

Page 20: Swift funtime

▸ has SwiftObject as superclass▸ instance variables are ivars▸ ivars have no type encoding▸ methods are not ObjC methods▸ not interoperable with ObjC

Page 21: Swift funtime

PLAYGROUND!

import ObjectiveC.runtime

butPlayground execution failed: Error in auto-import:failed to get module 'runtime' from AST context

(rdar://problem/18482380)

Page 22: Swift funtime

!"

Page 23: Swift funtime

DEMO: INSPECT OBJECTS

Page 24: Swift funtime

SWIFTOBJECT

Ivar: magic {SwiftObject_s="isa"^v"refCount"q}Protocol: NSObject

NSOBJECT

Ivar: isa #Protocol: NSObject

Page 25: Swift funtime

class MySwiftClass { var foo = "bar";

init() { }}

import Foundationimport ObjectiveC.runtime

var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo")var value : AnyObject = object_getIvar(MySwiftClass(), ivar)!

Segmentation fault: 11

Page 26: Swift funtime

#import <Foundation/Foundation.h>#import <objc/runtime.h>

@interface MyClass : NSObject

@property (nonatomic, retain) NSString* foo;

@end

#pragma mark -

@implementation MyClass

-(instancetype)init { self = [super init]; if (self) { self.foo = @"bar"; } return self;}

@end

#pragma mark -

int main(int argc, char *argv[]){ @autoreleasepool { MyClass* object = [MyClass new]; Ivar ivar = class_getInstanceVariable(object.class, "_foo"); id value = object_getIvar(object, ivar); NSLog(@"%@", value); return 0; }}

Page 27: Swift funtime

VALUE TYPES SHOULD BE STRUCTSstruct MyObject { var a : String var b : Array<Int>}

Page 28: Swift funtime

IN PURE SWIFT, THERE'S NO INTROSPECTION !

Page 29: Swift funtime

THERE IS HOPE/// How children of this value should be presented in the IDE.enum MirrorDisposition { case Struct case Class case Enum case Tuple [...]}

/// A protocol that provides a reflection interface to an underlying value.protocol MirrorType { [...]

/// Get the number of logical children this value has. var count: Int { get } subscript (i: Int) -> (String, MirrorType) { get }

/// Get a string description of this value. var summary: String { get }

[...]}

Page 30: Swift funtime

// From: https://gist.github.com/peebsjs/9288f79322ed3119ece4

infix operator --> {}func --> (instance: Any, key: String) -> Any? { let mirror = reflect(instance)

for index in 0 ..< mirror.count { let (childKey, childMirror) = mirror[index] if childKey == key { return childMirror.value } }

return nil}

//Examplestruct MyPoint { let x: Float let y: Float}

let point = MyPoint(x: 1, y: 2)println(point --> "x")println(point --> "y")

Page 31: Swift funtime

OBJECTIVE-C RUNTIME IN THE

AGE OF SWIFT

Page 32: Swift funtime

INHERIT FROM NSObject AND IT JUST WORKS!

Page 33: Swift funtime

EVEN SWIZZLING !

Page 34: Swift funtime

import Foundationimport ObjectiveC.runtime

extension NSString { func swizzle_description() -> NSString { return "!" }}

var myString = "foobar" as NSString

println(myString.description)

var originalMethod = class_getInstanceMethod(NSString.self, "description")var swizzledMethod = class_getInstanceMethod(NSString.self, "swizzle_description")

method_exchangeImplementations(originalMethod, swizzledMethod)

println(myString.description)

Page 35: Swift funtime
Page 36: Swift funtime
Page 37: Swift funtime

OR REPLACING METHODSimport Foundationimport ObjectiveC.runtime

let myString = "foobar" as NSString

println(myString.description)

let myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in "✋"}

let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self))let method = class_getInstanceMethod(myString.dynamicType, "description")method_setImplementation(method, myIMP)

println(myString.description)

Page 38: Swift funtime

LET'S TAKE A STEP BACK

Page 39: Swift funtime

OBJECTStypedef struct objc_object { Class isa;} *id;

Page 40: Swift funtime

CLASSESstruct objc_class { Class isa;

#if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;

Page 41: Swift funtime

OBJECTS

▸ struct magic▸ contains refCount and isa

▸ methods are in virtual table, like in C++

CLASSES▸ have mangled names, which contain the module name

Page 42: Swift funtime

NAME MANGLINGanother C++ concept

_TFV4test1eCfMS0_FT_S0_ ---> test.e.init (test.e.Type)() -> test.e_TMLCCC4test1a1b1c ---> lazy cache variable for type metadata for test.a.b.c_TMmCCC4test1a1b1c ---> metaclass for test.a.b.c_TMnCC4test1a1b ---> nominal type descriptor for test.a.b

_TTWOV4test1e1fSs9EquatableFS2_oi2eeUS2___fMQPS2_FTS3_S3__Sb ---> protocol witness for Swift.Equatable.== infix <A : Swift.Equatable>(Swift.Equatable.Self.Type)(Swift.Equatable.Self, Swift.Equatable.Self) -> Swift.Bool in conformance test.e.f : Swift.Equatable

_TWoFC4test1aCfMS0_FT_S0_ ---> witness table offset for test.a.__allocating_init (test.a.Type)() -> test.a

_TWoFCCC4test1a1b1c1dfS2_FT1zS0_1xS1_1vFT1xSi_Si_OVS_1e1f ---> witness table offset for test.a.b.c.d (test.a.b.c)(z : test.a, x : test.a.b, v : (x : Swift.Int) -> Swift.Int) -> test.e.f

Page 43: Swift funtime

HOW ARE EMOJI FORMED?$ echo 'class ! {}'|xcrun swiftc -emit-library -o test -$ nm -g test...0000000000000db0 T __TFC4testX4ypIhD...$ xcrun swift-demangle __TFC4testX4ypIhD_TFC4testX4ypIhD ---> test.!.__deallocating_deinit

X4 ypIh ~ xn--yp8h

Page 44: Swift funtime

METHODSstruct objc_method { SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE;} OBJC2_UNAVAILABLE;

Page 45: Swift funtime

METHOD IMPLEMENTATIONStypedef struct objc_selector *SEL;

typedef id (*IMP)(id self, SEL _cmd ,...);

Page 46: Swift funtime

MESSAGE FORWARDING+(BOOL)resolveInstanceMethod:(SEL)aSEL;

-(void)forwardInvocation:(NSInvocation*)anInvocation;

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector;

-(BOOL)respondsToSelector:(SEL)aSelector;

Page 47: Swift funtime

FROM UIVIEWCONTROLLER.H- (void)attentionClassDumpUser:(id)arg1 yesItsUsAgain:(id)arg2 althoughSwizzlingAndOverridingPrivateMethodsIsFun:(id)arg3 itWasntMuchFunWhenYourAppStoppedWorking:(id)arg4 pleaseRefrainFromDoingSoInTheFutureOkayThanksBye:(id)arg5;

Page 48: Swift funtime

CHANGE CLASSES AT RUNTIME▸ method_setImplementation()▸ class_addMethod()

▸ ...

Page 49: Swift funtime

DEMO: DYNAMIC TABLE VIEW

Page 50: Swift funtime

NSINVOCATION DOES NOT EXIST

Page 51: Swift funtime

BUT WHAT CAN WE DO ABOUT PURE SWIFT?

Page 52: Swift funtime

SWROUTE▸ PoC of function hooking in Swift

▸ Uses rd_route, a Mach specific injection library for C

Page 53: Swift funtime

#include <stdint.h>

#define kObjectFieldOffset sizeof(uintptr_t)

struct swift_func_object { uintptr_t *original_type_ptr;#if defined(__x86_64__) uintptr_t *unknown0;#else uintptr_t *unknown0, *unknown1;#endif uintptr_t function_address; uintptr_t *self;};

uintptr_t _rd_get_func_impl(void *func) { struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset);

return obj->function_address;}

Page 54: Swift funtime

SWIFT RUNTIME

Page 55: Swift funtime

▸ libswiftCore.dylib

implementations of NSSwiftArray, etc.▸ libswiftRuntime.a

low-level primitives like swift_release

Page 56: Swift funtime

HOPPER

Page 57: Swift funtime

COMPATIBILITY

▸ App Compatibility ✅▸ Binary Compatibility ⛔️▸ Source Compatibility ⛔️

Page 58: Swift funtime

Foo.app boris$ find . -type f./Frameworks/libswiftCore.dylib./Frameworks/libswiftCoreGraphics.dylib./Frameworks/libswiftCoreImage.dylib./Frameworks/libswiftDarwin.dylib./Frameworks/libswiftDispatch.dylib./Frameworks/libswiftFoundation.dylib./Frameworks/libswiftObjectiveC.dylib./Frameworks/libswiftUIKit.dylib./Info.plist./PkgInfo./Foo

Page 59: Swift funtime

TWO FINAL TIDBITS

Page 60: Swift funtime

SPEED▸ less dynamic dispatch

▸ omits _cmd - freeing one register▸ usually no pointer aliasing

int *ptrA = malloc(100 * sizeof(*ptrA));int *ptrB = ptrA;

Page 61: Swift funtime

class BankAccount { var balance: Double = 0.0

func deposit(amount: Double) { balance += amount }}

let account = BankAccount()account.deposit(100)

let depositor = BankAccount.depositdepositor(account)(100)

BankAccount.deposit(account)(100)

Page 62: Swift funtime

METHODS ARE CURRIED FUNCTIONS

Page 63: Swift funtime
Page 64: Swift funtime

YOU DON'T NEED OBJECTIVE-C

ANYMORE

Page 65: Swift funtime

UNLESS YOU BUILD FRAMEWORKS OR NEED TO

WORK WITH C++

Page 66: Swift funtime

BUT THE OBJC RUNTIME IS STILL STRONG

Page 67: Swift funtime

THANK YOU!

Page 68: Swift funtime

▸ https://www.mikeash.com/pyblog/▸ http://airspeedvelocity.net/

▸ https://developer.apple.com/swift/blog/▸ http://www.russbishop.net/swift-how-did-i-do-horrible-things

Page 69: Swift funtime

@NeoNacho

[email protected]

http://buegling.com/talks