Skip to content

Instantly share code, notes, and snippets.

@linjunpop
Last active November 28, 2018 12:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save linjunpop/5667422 to your computer and use it in GitHub Desktop.
Save linjunpop/5667422 to your computer and use it in GitHub Desktop.
Objective-C for Rubyist.

Objective-C for Rubyist

Basic Syntax

Message

[you say:@"Hello."];

Ruby:

you.say('Hello')

In particular, Objective-C’s:

[apple performSelector:@selector(eat:)
                        withObject:camembert]; // Equivalent to [apple eat:camembert];

Would be equivalent to Ruby’s:

apple.send(:eat, camembert)

Sadly, we CANNOT use

[apple performSelector:@selector(@"eat:")
                        withObject:camembert];

Another way:

id objc_msgSend(id theReceiver, SEL theSelector, ...)

Protocal

@protocal Talking

- (void)say:(NSString *)word;

@end

// Miao.h
@interface Miao<Talking>
@end

// Miao.m
@implement Miao
- (void)say:(NSString *)word {
  NSLog(@"Hi");
}
@end

Property

// Interface
#import <Foundation/Foundation.h>

@interface UserModel : NSObject

@property (retain) NSString* name;

@end

// Implementation
#import "UserModel.h"

@implementation UserModel

@synthesize name;

@end

Set age to be available with read and write access (getter and setter) we want the argument to be assigned to the property.

Ruby:

attr_accessor, attr_reader, attr_writer

Initialize

[[Foo alloc] init]

Camembert *camembert = [[Camembert alloc] initWithAge:3 smell:someSmell];

Ruby:

class Object
  def self.new(*args)
    self.alloc.initialize(*args)
  end

  def initialize
  end
end

Duck typing

id you = [[Superman alloc] init];

id point to any object.

Ruby:

"foo".respond_to?(:to_s)
#=> true

1.respond_to?(:to_s)
#=> true

Interface

Header file: UserModel.h

#import <Foundation/Foundation.h>

@interface UserModel : NSObject

+ (NSMutableArray *)all;

@end

Implementation file: UserModel.m

#import "UserModel.h"

@implementation UserModel

+ (NSMutableArray *)all {
    // ...
}

@end

Monkey patching (categories)

"A category allows you to add methods to an existing class—even to one for which you do not have the source."

#import <Foundation/Foundation.h>
 
@interface AClass (ACategory)
 
@end

File: NSDate+Formatting.h

#import <Foundation/Foundation.h>
 
@interface NSDate (Formatting)
 
- (NSString*) timeAgoInWords;
 
@end

File: 'NSDate+Formatting.m'

#import "NSDate+Formatting.h"
 
@implementation NSDate (Formatting)
 
- (NSString *)timeAgoInWords {
    // ...
}

@end

Usage:

NSDate* oldDate = [NSDate dateWithTimeIntervalSinceNow:180];
NSLog(@"Date was %@", oldDate.timeAgoInWords); // => 3 minutes ago

Ruby:

class String
  def blank?
    true
  end
end

"foo".blank? #=> true

Block

Basic

^{
    NSLog(@"This is a block");
}

Ruby:

lambda {
  puts "This is a lambda"
}

Declare a block variable simpleBlock accept no argument and return void:

void (^simpleBlock)(void);

Ruby:

foo = Proc.new {}

Assign a block to the variable:

simpleBlock = ^{
    NSLog(@"This is a block");
};

Ruby:

foo = Proc.new {
  puts "This is a proc"
}

Combine declaration and assignment:

void (^simpleBlock)(NSString *s) = ^{
    NSLog(@"This is a block");
};

Ruby:

foo = lambda { |a|
  puts "foobar - #{a}"
}

Blocks Can Capture Values from the Enclosing Scope

- (void)testMethod {
    int anInteger = 42;
 
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
    
    anInteger = 84;
 
    testBlock();
}
// => Integer is: 42

Ruby:

Use __block Variables to Share Storage

__block int anInteger = 42;

void (^testBlock)(void) = ^{
    NSLog(@"Integer is: %i", anInteger);
};

anInteger = 84;

testBlock();
// => Integer is: 84

Side effect:

Any Objective-C objects declared in this way will not be sent a -retain message. Another side-effect is that the variable will be shared by all blocks which access it, so modifications made by one block will be seen by another— they will not have their own mutable copies of the variable’s initial value.

Ruby:

a = 42
foo = lambda {
  puts "Integer is #{a}"
}
a = 64
foo.call()

#=> Integer is: 64

Nil

Every message send to nil will be IGNORED

Ruby:

NoMethodError: undefined method `a' for nil:NilClass

Ref:

http://www.slideshare.net/abdels/my-adventuresinobjc http://blog.carbonfive.com/2012/01/23/monkey-patching-ios-with-objective-c-categories-part-1-simple-extensions-and-overrides/ http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html http://blog.phusion.nl/2010/03/24/objective-c-for-ruby-developers-un-not-so-petit-interlude-1/ http://paulsturgess.co.uk/blog/2013/04/25/objective-c-for-a-rubyist/ https://speakerdeck.com/eddie/my-way-to-objective-c

@phensalves
Copy link

Nice! I develop in Ruby on Rails for 2,5 years and 5 years with another languages. I just started studying iOS languages (first Swift) this year and I can't wait for my first program!

Thanks for this short article!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment