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

Understanding Objective-C Inside and Out

Jeff Kelley
September 27, 2013

Understanding Objective-C Inside and Out

How many times have you tried to debug something in Objective-C without really knowing *why* it works a certain way? Do you start sweating when Xcode throws linker errors your way, or when you have to mix ARC and non-ARC code? This talk will reveal how Objective-C works, what actually happens when your application is linked, and reveal what ARC is actually doing. Objective-C is a 30-year-old language, and it’s evolving faster than ever. We’ll look at the basics of the language, how properties and categories work, and cover advanced topics like creating classes at runtime, memory management and ARC, and tagged pointers. At the end of the talk, you’ll be able to diagnose issues with your app more quickly, dive into system components more readily, and have a better sense of why your code works.

Jeff Kelley

September 27, 2013
Tweet

More Decks by Jeff Kelley

Other Decks in Programming

Transcript

  1. Understanding Objective- C Inside and Out CocoaConf Columbus, September 27th,

    2013 Jeff Kelley @SlaunchaMan Saturday, September 28, 13
  2. Objective-C’s Beginning • Developed by Brad Cox and Tom Love

    in the early 1980s • Originally OOPC, or Object Oriented Pre- Compiler • Was originally a C precompiler! • Acquired by NeXT in 1995 • Apple now owns Objective-C rights Saturday, September 28, 13
  3. Why Objective-C? • Compatibility with existing C code • Originally

    developed to work alongside telecom C code • Brings in the object-oriented nature of Smalltalk Saturday, September 28, 13
  4. Early Objective-C • Tim Berners-Lee wrote the first web browser,

    WorldWideWeb, on a NeXT Cube in Objective-C in 1989/1990 • Let’s look at some source code! Saturday, September 28, 13
  5. Early Objective-C - readPrintInfo /* * Sets the margin fields

    from the Application-wide PrintInfo. */ { id pi; float conversion, dummy; NXCoord left, right, top, bottom; [super readPrintInfo]; pi = [NXApp printInfo]; [self convertOldFactor:&conversion newFactor:&dummy]; [pi getMarginLeft:&left right:&right top:&top bottom:&bottom]; [leftMargin setFloatValue:left * conversion]; [rightMargin setFloatValue:right * conversion]; [topMargin setFloatValue:top * conversion]; [bottomMargin setFloatValue:bottom * conversion]; return self; } Saturday, September 28, 13
  6. Why do we still use it? • C is powerful

    • C is fast • Apple owns it • See recently-developed features: properties, ARC, blocks, literals • What else would we use? Saturday, September 28, 13
  7. Today’s Agenda • Why do I get all these weird

    linker errors? • objc_msgSend() keeps crashing! • Why can’t I add an instance variable in a category? • How can Apple still be using Objective-C? Saturday, September 28, 13
  8. Weird Linker Errors • Can freeze a new Objective-C programmer

    in his or her tracks • Simple, but cryptic error messages • Cross-library troubles Saturday, September 28, 13
  9. What happens when you send a message? •objc_msgSend() • Hey,

    I see that in my crash logs all the time! They should work on that! • Found in <objc/message.h> • If you want to pass an argument, you need to cast it • Useful for calling private APIs Saturday, September 28, 13
  10. Calling Private API Let’s call -recursiveDescription on a view. NSString

    *recursiveDescription = objc_msgSend(self.window, @selector(recursiveDescription)); Saturday, September 28, 13
  11. Calling Private API • OK, that was easy. But what

    if something takes an argument? Or returns a C primitive? • You get to cast objc_msgSend() to the appropriate function type Saturday, September 28, 13
  12. Casting objc_msgSend() NSArray *myArray = @[ @1, @2, @3 ];

    NSUInteger (*unsignedIntegerMessage)(id obj, SEL message) = (NSUInteger (*)(id, SEL))objc_msgSend; NSUInteger count = unsignedIntegerMessage(myArray, @selector(count)); Saturday, September 28, 13
  13. Casting objc_msgSend() • Hey, that looks like block syntax! •

    C function pointer syntax was here first • Don’t do this with other functions—this one is expecting it • Lots of hand-written assembly code per platform in objc_msgSend() Saturday, September 28, 13
  14. objc_msgSend() Family • id objc_msgSend(id self, SEL op, ...); •

    id objc_msgSendSuper(id self, SEL op, ...); • long double objc_msgSend_fpret(id self, SEL op, ...); • void objc_msgSend_stret(id obj, SEL op, ...); Saturday, September 28, 13
  15. So what does objc_msgSend() do? • Every Objective-C method compiles

    to a C implementation, or IMP • These are looked up by name (the selector) and cached for performance • “Objective-C is slow” • objc_msgSend() finds the IMP and calls it • So if the stack explodes, you see it Saturday, September 28, 13
  16. Why is this useful? • Implement two Objective-C methods with

    one underlying C function • Override - (IMP)methodForSelector: (SEL)aSelector; • Or graft existing C functionality to Objective-C • Method Swizzling Saturday, September 28, 13
  17. Method Swizzling • If you need to modify behavior of

    a class you don’t own (like UIView), you can replace a method implementation with one of your own • A combination of class_addMethod() and method_setImplementation() Saturday, September 28, 13
  18. Objects and Instance Variables • Why can’t I add an

    instance variable in a category? • Where am I supposed to add instance variables, anyway? Saturday, September 28, 13
  19. What is an Object? • From <objc/objc.h>: typedef struct objc_object

    { Class isa; } *id; • It’s just a C struct! • Well, not recently. • You can do object->instanceVariable Saturday, September 28, 13
  20. Memory Layout • Objective-C objects are laid out like structs

    • First member is always the isa pointer • Points to class • So to know where an instance variable is, we need to know the entire hierarchy • Therefore, we can’t add anything in a category Saturday, September 28, 13
  21. Why do we always use pointers? • With a complex

    object hierarchy, hundreds of instance variables might be on one class • Can’t make all these objects on the stack • Would use too much memory • Would die with the stack • All objects are created on the heap, we use pointers to find them Saturday, September 28, 13
  22. Reference Counting • How do we know when to free

    the memory an object is using? • Despite how we talk about it, the system is not as complicated as you might think • Objects don’t really know about each other, they just store pointers to them • Retain/release used based on simple rules Saturday, September 28, 13
  23. ARC • ARC is a simple system of rules •

    Placing an object’s address in a pointer variable? Retain it. • A pointer variable containing an object’s address falls out of scope or is getting a new value? Release it. • The method starts with alloc, new, copy, or create? Autorelease it instead. Saturday, September 28, 13
  24. ARC • ARC adds an optimization layer on top of

    these simple rules to get really fast • objc_retain() faster than [object retain]; Saturday, September 28, 13
  25. What if my category needs storage? • Objective-C is really

    good at looking up values in storage • Honed through the message dispatch system • Associated objects API allows arbitrary storage of object associations • objc_setAssociatedObject() and friends Saturday, September 28, 13
  26. Places to Add Instance Variables • The public header @interface

    MyClass : NSObject { id obj; } • Encourages direct instance variable access by others, should be avoided • Use a property instead Saturday, September 28, 13
  27. Places to Add Instance Variables • The implementation block @implementation

    MyClass { id obj; } • Limits access to implementation file • Property access is still safer, but with ARC this is reasonably safe Saturday, September 28, 13
  28. Places to Add Instance Variables • The class extension @interface

    MyClass() { id obj; } • If you’re also adding methods in the class extension, it’s only logical to place these here as well Saturday, September 28, 13
  29. Dynamic Classes • No, really, I need to modify the

    behavior of an object at runtime. • Apple already does this with KVO • So how do they do it? Saturday, September 28, 13
  30. KVO in Action • First, the API creates a subclass

    of your class at runtime: Class mySubclass = objc_allocateClassPair([NSObject class], "MySubclass", 0); Saturday, September 28, 13
  31. KVO in Action • Second, the API sets your object

    to this new class object_setClass(object, mySubclass); • DO NOT set the isa pointer directly •object->isa = mySubclass; Saturday, September 28, 13
  32. KVO in Action • Third, the API implements the method(s)

    it wants to override: - (void)setFoo:(id)foo { NSLog(@"About to set foo!"); [super setFoo:foo]; NSLog(@"Set foo!"); } Saturday, September 28, 13
  33. KVO in Action • Finally, the API overrides -class to

    hide what it’s done • So -isMemberOfClass: still works for the original class Saturday, September 28, 13
  34. Why would I need to do this? • Here’s an

    actual, real-life example of dynamic subclassing: Kiwi mocks • In Kiwi, the BDD test framework, you can stub a method like this: •[viewController stub:@selector(view) andReturn:thisMockView]; Saturday, September 28, 13
  35. Why would I need to do this? • In production

    applications, you won’t need to do this too often • But in testing, it’s invaluable Saturday, September 28, 13
  36. So How is Apple Still Using Objective-C? • As KVO

    illustrates, Objective-C is extremely capable of runtime hackery • As the sole owner, Apple is free to make insane improvements to the language • Tagged Pointers • The isa pointer in 64-bit iOS • Excellent tool development in LLVM/Clang Saturday, September 28, 13
  37. Tagged Pointers • A pointer to an object will, due

    to alignment, have some extra bits • The last bit is always 0… or is it? • Apple sets the last bit to 1 and uses the remaining bits to store integers • Looks and acts just like an NSNumber, but needs no additional storage Saturday, September 28, 13
  38. Tagged Pointers • Don’t try to set the isa pointer.

    Bad things will happen. • Best-case scenario: compiler error. • 64-bit iOS means we get tagged pointers • Performance/memory gains for free! Saturday, September 28, 13
  39. LLVM/Clang Optimizations • Link-Time Optimization • Applies compiler optimizations across

    source files, leading to huge potential gains • New optimization level -Ofast • Not suitable for scientific applications that need precise floating-point values Saturday, September 28, 13
  40. Where is Objective-C Going? • Recent Objective-C developments have made

    developing faster • Auto-synthesized property accessors • ARC • Packages • Object Literals Saturday, September 28, 13
  41. Where is Objective-C Going? • Recent Objective-C developments have reduced

    the cognitive load of the language • ARC • Packages • Properties Saturday, September 28, 13
  42. Where is Objective-C Going? • Future developments will likely continue

    down these paths • My suggestion: get rid of pointers • “What? Are you crazy?” • The compiler is smart enough to re- insert them where needed Saturday, September 28, 13
  43. References • http://en.wikipedia.org/wiki/Objective-C • http://en.wikipedia.org/wiki/Tim_Berners-Lee • http://www.mikeash.com/pyblog/friday-qa-2013-09-27- arm64-and-you.html • http://www.sealiesoftware.com/blog/archive/2013/09/24/

    objc_explain_Non-pointer_isa.html • https://twitter.com/marcoarment/status/ 383083874676641792 • http://llvm.org/docs/LinkTimeOptimization.html Saturday, September 28, 13