Swift funtime

Post on 01-Jul-2015

736 Views

Category:

Software

5 Downloads

Preview:

Click to see full reader

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

SWIFT FUNTIMEMOBICONF 2014

BORIS BÜGLING - @NEONACHO

COCOAPODS

CONTENTFUL

SWIFT

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

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

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

development works.”

▸ Optionals▸ Tuples▸ Generics

▸ Pattern matching▸ Operator overloading▸ Namespaces▸ Type inference

▸ ...

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

INTERFACES WITH C, AT FULL

SPEEDWATCH "SWIFT AND C" BY

MIKE ASH

DROPS C++ INTEROPERABILITY

Everyone is a Beginner

▸ The Swift Programming Language by

AND ALSO

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

▸ Functional Programming in Swift

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

LET'S DROP CLOSE TO THE METAL

WHAT IS A SWIFT OBJECT?

IT DEPENDS

class MyObject : NSObject {}

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

class MyObject {}

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

PLAYGROUND!

import ObjectiveC.runtime

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

(rdar://problem/18482380)

!"

DEMO: INSPECT OBJECTS

SWIFTOBJECT

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

NSOBJECT

Ivar: isa #Protocol: NSObject

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

#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; }}

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

IN PURE SWIFT, THERE'S NO INTROSPECTION !

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 }

[...]}

// 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")

OBJECTIVE-C RUNTIME IN THE

AGE OF SWIFT

INHERIT FROM NSObject AND IT JUST WORKS!

EVEN SWIZZLING !

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)

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)

LET'S TAKE A STEP BACK

OBJECTStypedef struct objc_object { Class isa;} *id;

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;

OBJECTS

▸ struct magic▸ contains refCount and isa

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

CLASSES▸ have mangled names, which contain the module name

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

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

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

METHOD IMPLEMENTATIONStypedef struct objc_selector *SEL;

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

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

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

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

-(BOOL)respondsToSelector:(SEL)aSelector;

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

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

▸ ...

DEMO: DYNAMIC TABLE VIEW

NSINVOCATION DOES NOT EXIST

BUT WHAT CAN WE DO ABOUT PURE SWIFT?

SWROUTE▸ PoC of function hooking in Swift

▸ Uses rd_route, a Mach specific injection library for C

#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;}

SWIFT RUNTIME

▸ libswiftCore.dylib

implementations of NSSwiftArray, etc.▸ libswiftRuntime.a

low-level primitives like swift_release

HOPPER

COMPATIBILITY

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

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

TWO FINAL TIDBITS

SPEED▸ less dynamic dispatch

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

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

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)

METHODS ARE CURRIED FUNCTIONS

YOU DON'T NEED OBJECTIVE-C

ANYMORE

UNLESS YOU BUILD FRAMEWORKS OR NEED TO

WORK WITH C++

BUT THE OBJC RUNTIME IS STILL STRONG

THANK YOU!

▸ 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

@NeoNacho

boris@contentful.com

http://buegling.com/talks

top related