$30 off During Our Annual Pro Sale. View Details »

Empower your Objective-C

Empower your Objective-C

Krzysztof Zabłocki

October 23, 2014
Tweet

More Decks by Krzysztof Zabłocki

Other Decks in Programming

Transcript

  1. Empower your Objective C
    Krzysztof Zabłocki @merowing_

    View Slide

  2. Swift?

    View Slide

  3. Objective-C techniques for
    • Less bugs
    • Faster development
    • Higher quality of code
    • More flexibility

    View Slide

  4. Technical Bits
    • Key Value Coding
    • Runtime
    • DSL
    • Macros
    • LLVM Attributes

    View Slide

  5. Key Value Coding
    • KVC gives us:
    • automatic boxing / unboxing of primitive types
    • collection operators like sum/avg/max, removing need
    to write them
    • more complex operators like unionOfObjects
    • can be applied to sub-objects eg. @sum.price
    • extracting only interesting attributes

    View Slide

  6. Key Value Coding
    - (CGFloat)before:(NSArray *)charts
    {
    CGFloat maxValue = CGFLOAT_MIN;
    for(HRBBarGraphChartDescriptor *chart in charts) {
    maxValue = fmaxf(chart.value.floatValue, maxValue);
    }
    return maxValue;
    }
    Turns into:
    - (CGFloat)after:(NSArray *)charts
    {
    return [[charts valueForKeyPath:@"@max.value"] floatValue];
    }

    View Slide

  7. Key Value Coding
    - (id )uniqueElementsBefore
    {
    NSMutableArray *allElements = [NSMutableArray new];
    for (ShapeGroup *group in _shapeGroups) {
    for(CCSprite *element in group.elements) {
    if(![allElements containsObject: element]) {
    [allElements addObject:element];
    }
    }
    }
    return [allElements copy];
    }
    Turns into:
    - (id )uniqueElementsAfter
    {
    return [_shapeGroups valueForKeyPath:@"@distinctUnionOfArrays.elements"];
    }

    View Slide

  8. Runtime
    • Dynamically create / override functions and classes:
    • Automatically pick up classes implementing specific
    protocol.
    • Intercept methods to log or modify behavior eg. Aspects.
    • Implement similar functions without repetition:
    • eg. findBy{Field}
    • Store context data per instance.

    View Slide

  9. Runtime - examples
    • Adding guards around Apple API's misuses:
    • accessing UIKit methods from background threads
    • accessing CoreData context from wrong thread
    • Adding UIGestureRecognizer support to Cocos2D
    • Higher order messaging:
    [[windowsArray do] setHidesOnDeactivate:YES];

    View Slide

  10. DSL - Domain Specific Language
    • Simplify code by making it more readable or avoiding
    clutter, eg.
    • KZAsserts automatic NSError generation, assertions and
    graceful error handling with same line of code.
    • KZPropertyMapper implements compile time error
    checking, generates custom bindings, removing need for
    breakable code.
    • Testing/Mocking frameworks

    View Slide

  11. DSL - Domain Specific Language
    - (NSError*)method {
    NSParameterAssert([dataFromServer isKindOfClass:[NSDictionary class]]);
    if ([dataFromServer isKindOfClass:[NSDictionary class]]) {
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Unable to satisfy condition [dataFromServer isKindOfClass:[NSDictionary class]]"};
    return [NSError errorWithDomain:ErrorDomain code:InternalErrorCode userInfo:userInfo];
    }
    NSParameterAssert([something isKindOfClass:[NSString class]]);
    if ([something isKindOfClass:[NSString class]]) {
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Unable to satisfy condition [something isKindOfClass:[NSString class]]"};
    return [NSError errorWithDomain:ErrorDomain code:InternalErrorCode userInfo:userInfo];
    }
    ...
    }
    Into this:
    - (NSError*)method {
    AssertTrueOrReturnError([dataFromServer isKindOfClass:[NSDictionary class]]);
    AssertTrueOrReturnError([something isKindOfClass:[NSString class]]);
    ...
    }

    View Slide

  12. DSL - Domain Specific Language
    This is all it takes to map values with type conversion and
    validation:
    [KZPropertyMapper mapValuesFrom:dictionary toInstance:self usingMapping:@{
    @"content_url" : KZBox(URL, contentURL).isRequired(),
    @"full_name" : KZProperty(fullName).lengthRange(5, 12),
    @"videoType" : KZProperty(type),
    @"sub_object" : @{
    @"title" : KZProperty(uniqueID),
    },
    }];

    View Slide

  13. DSL - Macros
    #define Macro(param)
    • Generate NSString - @#param turns into @"name"
    • Generate unique symbols by joining:
    NSString *local_##param = #@param; turns into
    NSString *local_name = @"name";
    • Use gcc expression extension to execute few statements:
    ({ result = doSomething(param); result; })

    View Slide

  14. DSL - Macros
    #define Macro(param)
    • Generate compilation errors on misspelling of keyPaths/
    properties:
    ({if(NO){ [self param]; }; #@param;})
    np. Macro(name) will return NSString* for a keyPath but only if it
    exists, compile error otherwise.

    View Slide

  15. LLVM Attributes - Top 4
    • constructor - similar to +load.
    • NS_REQUIRE_SUPER
    • nonnull - compiler error when trying to explicitly pass null
    • overloadable - multiple functions with same API prototype.
    void __attribute__((overloadable)) KZPShow(CGImageRef image);
    void __attribute__((overloadable)) KZPShow(UIImage *image);
    void __attribute__((overloadable)) KZPShow(NSString *format, ...);

    View Slide

  16. Playgrounds?

    View Slide

  17. View Slide

  18. Summary
    • Advanced techniques can save you hours of work
    • Cleaner and more robust code
    • Easier to evolve
    • Fun!
    • With great power comes great responsibility:
    • Design with care
    • Any programming technique can be misused

    View Slide

  19. Questions?

    View Slide

  20. Thank you!
    Krzysztof Zabłocki
    Follow @merowing_

    View Slide