Swift funtime
Post on 01-Jul-2015
736 Views
Preview:
DESCRIPTION
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