Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift funtime

Swift funtime

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

Boris Bügling

October 02, 2014
Tweet

More Decks by Boris Bügling

Other Decks in Technology

Transcript

  1. AGENDA ▸ How do you even Swift? ▸ What is

    a Swift object? ▸ Objective-C runtime in the age of Swift ▸ Swift runtime ▸ (some Q&A)
  2. ▸ Optionals ▸ Tuples ▸ Generics ▸ Pattern matching ▸

    Operator overloading ▸ Namespaces ▸ Type inference ▸ ...
  3. ▸ The Swift Programming Language by  AND ALSO ▸

    Swift by Tutorials: A Hands-On Approach ▸ Your First Swift App ▸ Functional Programming in Swift
  4. SWIFT COMMAND LINE TOOLS $ xcrun swift Welcome 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
  5. ▸ behaves like any old Objective-C object ▸ instance variables

    are properties ▸ fully interopable with ObjC
  6. ▸ has SwiftObject as superclass ▸ instance variables are ivars

    ▸ ivars have no type encoding ▸ methods are not ObjC methods ▸ not interoperable with ObjC
  7. PLAYGROUND! import ObjectiveC.runtime but Playground execution failed: Error in auto-import:

    failed to get module 'runtime' from AST context (rdar://problem/18482380)
  8. !"

  9. class MySwiftClass { var foo = "bar"; init() { }

    } import Foundation import ObjectiveC.runtime var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo") var value : AnyObject = object_getIvar(MySwiftClass(), ivar)! Segmentation fault: 11
  10. #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; } }
  11. 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 } [...] }
  12. // 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 } //Example struct MyPoint { let x: Float let y: Float } let point = MyPoint(x: 1, y: 2) println(point --> "x") println(point --> "y")
  13. import Foundation import 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)
  14. OR REPLACING METHODS import Foundation import 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)
  15. CLASSES struct 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;
  16. OBJECTS ▸ struct magic ▸ contains refCount and isa ▸

    methods are in virtual table, like in C++ CLASSES ▸ have mangled names, which contain the module name
  17. NAME MANGLING another 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
  18. 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
  19. SWROUTE ▸ PoC of function hooking in Swift ▸ Uses

    rd_route, a Mach specific injection library for C
  20. #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; }
  21. 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
  22. SPEED ▸ less dynamic dispatch ▸ omits _cmd - freeing

    one register ▸ usually no pointer aliasing int *ptrA = malloc(100 * sizeof(*ptrA)); int *ptrB = ptrA;
  23. class BankAccount { var balance: Double = 0.0 func deposit(amount:

    Double) { balance += amount } } let account = BankAccount() account.deposit(100) let depositor = BankAccount.deposit depositor(account)(100) BankAccount.deposit(account)(100)