Classes, part 2

In this tutorial we cover the following topics


Selectors

Selectors are Objective-C’s internal representation of a method name. A selector by itself doesn’t do anything. It simply identifies a method. They let us treat a method as an independent entity, enabling to separate an action from the object that needs to perform it.They let dynamically “select” one of an object’s methods, which can be used to refer to a method at run time, pass a method to another function, and figure out whether an object has a particular method. It’s important to understand that a selector only represents the method name not a specific method implementation. In other words, a bar method defined by the Foo1 class has the same selector as a bar method defined by the Foo2 class (see experiment 1).

Selectors only work with method names not signatures. As a result, a method name (selector) may match a signature with different data types (see experiment 2).

Getting a selector

Compiled selectors are of type SEL. There are two common ways to get a selector:

  • At compile time, we use the compiler directive @selector.
    SEL aSelector = @selector(methodName);
    
  • At runtime, we use the NSSelectorFromString function, where the string is the name of the method:
    SEL aSelector = NSSelectorFromString(@"methodName");
    

    We use a selector created from a string when we want our code to send a message whose name we may not know until runtime.

Using a selector

We can invoke a method using a selector with performSelector: (or other similar methods)

SEL aSelector = @selector(methodName);
[objectName performSelector:aSelector];

This is the equivalent of calling methodName directly on objectName:

[objectName methodName];

For methods with one or more parameters, we can use the related performSelector:withObject: methods.
For example the equivalent of passing the parameter directly to the method:

[objectName methodName:parameter1];

is

[objectName performSelector:@selector(methodName:) withObject:parameter1];

(see experiment 2). The one thing we have to remember is that all parameters and the return value of the method must be objects — we can not use primitive C data types like int, float, etc.

Experiment 1


In this experiment we want to show that two methods (with the same name) with two different classes has exactly the same selector.

#import <Foundation/Foundation.h>

@interface Foo1 : NSObject

- (void) test;

@end
#import "Foo1.h"

@implementation Foo1

- (void) test{
    NSLog(@"Test method from Foo1 class\n");
}

@end
#import <Foundation/Foundation.h>

@interface Foo2 : NSObject

- (void) test;

@end
#import "Foo2.h"

@implementation Foo2

- (void) test{
    NSLog(@"Test method from Foo2 class\n");
}

@end
#import <Foundation/Foundation.h>
#import "Foo1.h"
#import "Foo2.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo1 *foo1 = [[Foo1 alloc] init];
        Foo2 *foo2 = [[Foo2 alloc] init];
        
        [foo1 test];
        [foo2 test];
        
        // Notice that we select simply "test" method
        // without specifying class name
        SEL selectorTestMethod = @selector(test);
        // or
        // SEL selectorTestMethod = NSSelectorFromString(@"test");
        
        [foo1 performSelector:selectorTestMethod];
        [foo2 performSelector:selectorTestMethod];
        
    }
    return 0;
}

This code should works
[fulmanp type=”terminal”]
2016-03-02 11:01:21.332 app_003_selectors_ex1[8027:904841] Test method from Foo1 class
2016-03-02 11:01:21.333 app_003_selectors_ex1[8027:904841] Test method from Foo2 class
2016-03-02 11:01:21.333 app_003_selectors_ex1[8027:904841] Test method from Foo1 class
2016-03-02 11:01:21.333 app_003_selectors_ex1[8027:904841] Test method from Foo2 class
Program ended with exit code: 0
[/fulmanp]
but notice that editing it we may obtain a warning

PerformSelector may cause a leak because its selector is unknown

selectors_ex1_001
In some sense it’s normal. Please read about this in other articles like



Experiment 2


In this experiment we want to show

  • that one selector match to different methods (methods with the same name but different signatures);
  • how to write selector for a method with more than one argument;
  • how to check if an object can respond to a selector at run time using the respondsToSelector: method.
#import <Foundation/Foundation.h>

@interface Foo1 : NSObject

- (void) printMessage: (NSString*) message withValue:(NSString*) string;

@end
#import "Foo1.h"

@implementation Foo1

- (void) printMessage: (NSString*) message withValue:(NSString*) string {
    NSLog(@"%@ %@", message, string);
}

@end
#import <Foundation/Foundation.h>

@interface Foo2 : NSObject

- (void) printMessage: (NSString*) message withValue:(NSNumber*) integer;

@end
#import "Foo2.h"

@implementation Foo2

// Note:
// NSInteger is a primitive type, which means it can be stored locally on the stack.
// We don't need to use a pointer to access it.
// So if we would like to use 'printMessage' as usual method we could write this as
//
// - (void) printMessage: (NSString*) message withValue:(NSInteger) integer {
//     NSLog(@"%@ %ld", message, (long)integer);
// }
//
// Because we want to use selector, we have to use NSNumber which is not a primitive type -
// remember that in this case all parameters and the return value of the method must be objects.

- (void) printMessage: (NSString*) message withValue:(NSNumber*) integer {
    int intValue = [integer intValue];
    NSLog(@"%@ %d", message, intValue);
}

@end
#import <Foundation/Foundation.h>
#import "Foo1.h"
#import "Foo2.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo1 *foo1 = [[Foo1 alloc] init];
        Foo2 *foo2 = [[Foo2 alloc] init];
        
        NSString *msg1 = @"Print something...";
        NSString *msg2 = @"NSString";
        NSNumber *integer = [NSNumber numberWithInt:123];
        // or the same with
        //NSNumber *integer = @123;

        SEL selectorPrintMessage = @selector(printMessage:withValue:);
        
        if ([foo1 respondsToSelector:selectorPrintMessage]) {
            [foo1 performSelector:selectorPrintMessage withObject:msg1 withObject:msg2];
        }
        if ([foo2 respondsToSelector:selectorPrintMessage]) {
            [foo2 performSelector:selectorPrintMessage withObject:msg1 withObject:integer];
        }
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 09:55:59.519 app_003_selectors_ex2[8830:946576] Print something… NSString
2016-03-03 09:55:59.520 app_003_selectors_ex2[8830:946576] Print something… 123
Program ended with exit code: 0
[/fulmanp]



Experiment 3: usage example


In this experiment we want to show a very simple usage example.

#import <Foundation/Foundation.h>

@interface WallDestroyer : NSObject

@property SEL action;

// Set of actions
- (void)useHead;
- (void)useHammer;

// Utils methods
- (void)performAnAction;

@end
#import "WallDestroyer.h"

@implementation WallDestroyer

// Set of actions
- (void)useHead{
    NSLog(@"I'm destroying the wall with my head");
}

- (void)useHammer{
    NSLog(@"I'm destroying the wall with a hammer");
}

// Utils methods
- (void)performAnAction{
    [self performSelector:_action];
}

@end
#import <Foundation/Foundation.h>
#include "WallDestroyer.h"
#define NUMBER_OF_ACTIONS 2

NSString *actionSelector() {
    NSString *action[NUMBER_OF_ACTIONS] = {@"useHead", @"useHammer"};
    int decision = 0;
    
    NSLog (@"Which method you want me to use to destroy a wall?");
    for (int i=0; i<NUMBER_OF_ACTIONS; i++) {
        NSLog(@"%2d %@", i, action[i]);
    }
    
    scanf("%d", &decision);
    
    return action[decision];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        WallDestroyer *person = [[WallDestroyer alloc] init];
        person.action = NSSelectorFromString(actionSelector());
        [person performAnAction];
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 10:24:35.603 app_003_selectors_ex3[9119:957793] Which method you want me to use to destroy a wall?
2016-03-03 10:24:35.604 app_003_selectors_ex3[9119:957793] 0 useHead
2016-03-03 10:24:35.605 app_003_selectors_ex3[9119:957793] 1 useHammer
1
2016-03-03 10:25:01.581 app_003_selectors_ex3[9119:957793] I’m destroying the wall with a hammer
Program ended with exit code: 0
[/fulmanp]




Protocols


A protocol is a group of related properties and methods that can be implemented by any class. With protocols we can define a one single API and use it (after implementation) in unrelated classes. This way we can represent horizontal relationships on top of an existing tree-like class hierarchy (see graph below).

In this sense Objective-C protocols are like Java interfaces.

A syntax of protocol is shown below.

protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end

The methods under keyword @required must be implemented in the classes that conforms to the protocol and the methods under @optional keyword are optional to implement.

Here is the syntax for class conforming to protocol ProtocolName1 as well as ProtocolName2

@interface ClassName : NSObject <ProtocolName1, ProtocolName2>
...
@end

Above means that any instance of ClassName will respond not only to the methods declared in the interface, but that this class also provides implementations for all the required methods in ProtocolName1 and ProtocolName2. There’s no need to redeclare the protocol methods in the class interface — the adoption of the protocol is sufficient.

The protocol’s code should be placed in the file with *.h extension.


Experiment 4: creating and adopting protocols

#ifndef Protocol1_h
#define Protocol1_h

@protocol Protocol1

@required
- (void) requiredMethodProt1;

@optional
- (void) optionalMethodProt1;

@end

#endif /* Protocol1_h */
#ifndef Protocol2_h
#define Protocol2_h

@protocol Protocol2

@required
- (void) requiredMethodProt2;

@optional
- (void) optionalMethodProt2;

@end

#endif /* Protocol2_h */
#import <Foundation/Foundation.h>
#include "Protocol1.h"
#include "Protocol2.h"

@interface Foo : NSObject <Protocol1, Protocol2>

- (void) classMethod;

@end
#import "Foo.h"

@implementation Foo

- (void) requiredMethodProt1{
    NSLog(@"requiredMethodProt1");
}

- (void) optionalMethodProt1{
    NSLog(@"optionalMethodProt1");
}

- (void) requiredMethodProt2{
    NSLog(@"requiredMethodProt2");
}

- (void) classMethod{
    NSLog(@"classMethod{");
}

@end
#import <Foundation/Foundation.h>
#import "Foo.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo *foo = [[Foo alloc] init];
        [foo requiredMethodProt1];
        [foo optionalMethodProt1];
        [foo requiredMethodProt2];
        [foo classMethod];
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 11:26:35.881 app_003_selectors_ex4[9463:977228] requiredMethodProt1
2016-03-03 11:26:35.882 app_003_selectors_ex4[9463:977228] optionalMethodProt1
2016-03-03 11:26:35.882 app_003_selectors_ex4[9463:977228] requiredMethodProt2
2016-03-03 11:26:35.882 app_003_selectors_ex4[9463:977228] classMethod{
Program ended with exit code: 0
[/fulmanp]



Experiment 5: type checking with protocols


In this example we want to show how to make sure an object adopts a protocol. Simply, put the protocol name after the data type in the variable declaration.

#ifndef Protocol_h
#define Protocol_h

@protocol Protocol

- (void) protocolMethod;

@end

#endif /* Protocol_h */
#import <Foundation/Foundation.h>
#include "Protocol.h"

@interface Foo1 : NSObject <Protocol>

- (void) foo1Method;

@end
#import "Foo1.h"

@implementation Foo1

- (void) protocolMethod{
    NSLog(@"foo1ProtocolMethod");
}


- (void) foo1Method{
    NSLog(@"foo1Method");
}

@end
#import <Foundation/Foundation.h>
#include "Protocol.h"

@interface Foo2 : NSObject <Protocol>

- (void) foo2Method;

@end
#import "Foo2.h"

@implementation Foo2

- (void) protocolMethod{
    NSLog(@"foo2ProtocolMethod");
}


- (void) foo2Method{
    NSLog(@"foo1Method");
}

@end
#import <Foundation/Foundation.h>
#import "Foo1.h"
#import "Foo2.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo1 * foo1 = [[Foo1 alloc] init];
        Foo2 * foo2 = [[Foo2 alloc] init];
        // 'something' is a pointer to any class which
        // implements protocol 'Protocol'
        id <Protocol> something = foo1;
        [something protocolMethod];
        
        something = foo2;
        [something protocolMethod];
        
        if ([foo1 conformsToProtocol:@protocol(Protocol)]) {
            [foo1 protocolMethod];
        }
        
        if ([foo2 conformsToProtocol:@protocol(Protocol)]) {
            [foo2 protocolMethod];
        }
    }

    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 21:19:59.381 app_003_selectors_ex5[9896:1025359] foo1ProtocolMethod
2016-03-03 21:19:59.383 app_003_selectors_ex5[9896:1025359] foo2ProtocolMethod
2016-03-03 21:19:59.384 app_003_selectors_ex5[9896:1025359] foo1ProtocolMethod
2016-03-03 21:19:59.384 app_003_selectors_ex5[9896:1025359] foo2ProtocolMethod
Program ended with exit code: 0
[/fulmanp]




Categories


It’s not very common situation, but sometimes we may wish to extend an existing class by adding behavior that is useful only in certain situations. What’s more, we want to extend “built-in” class — a class for which we don’t have a source code. This is when categories can help. Other case is when our code is to large and it wouldn’t be a bad idea to divide it into smaller chunks.

The syntax to declare a category uses the @interface keyword, just like a standard Objective-C class description, but does not indicate any inheritance from a subclass. Instead, it specifies the name of the category in parentheses, like this:

@interface ClassNameWeWantToExtend (OurCategoryName)

@end

It’s good to remember that

  • A category can be declared for any class, even if we don’t have the original implementation source code.
  • Any methods declared in a category will be available to all instances of the original class, as well as any subclasses of the original class.
  • At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.
  • The only restriction on category names is that they don’t conflict with other categories on the same class. The canonical file naming convention is to use the class name and the category name separated by a plus sign, for example ClassNameWeWantToExtend+OurCategoryName.h


Experiment 6: extend existing “built-in” class

#import <Foundation/Foundation.h>

@interface NSString (CategoryForNSString)

+ (NSString *) newClassMethod;
- (NSString *) addPrefix: (NSString *) prefix;

@end
#import "NSString+CategoryForNSString.h"

@implementation NSString (CategoryForNSString)

+ (NSString *) newClassMethod{
    return @"newClassMethod";
}

- (NSString *) addPrefix: (NSString *) prefix {
    NSString *combine = [NSString stringWithFormat:@"%@%@", prefix, self];
    //[prefix stringByAppendingFormat:@",%@", string];
    return combine;
}

@end
#import <Foundation/Foundation.h>
#include "NSString+CategoryForNSString.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString * string = @"String";
        NSString * prefix = @"prefix";
        
        NSLog(@"%@", [NSString newClassMethod]);
        NSLog(@"%@", [string addPrefix:prefix]);
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 21:53:12.288 app_003_selectors_ex6[10170:1039292] newClassMethod
2016-03-03 21:53:12.290 app_003_selectors_ex6[10170:1039292] prefixString
Program ended with exit code: 0
[/fulmanp]




Excercise 3.1: split a single class definition into multiple files


Write a class and split its code into multiple files. You can work in gropus, so one person can implement one aspect of this class. For example you can write a class GeometricFigures where different figures are implemened by different person as a different cateory of GeometricFigures.




Extensions


Extensions are similar to categories, but it can only be added to a class for which we have the source code at compile time.

The methods declared by a class extension are implemented in the implementation block for the original class, so we can’t, for example, declare a class extension on a framework class, such as NSString. Extensions are actually categories without the category name and that is why it’s sometimes referred as anonymous categories.

The syntax to declare a extension uses the @interface keyword, just like a standard Objective-C class description, but does not indicate any inheritance from a subclass. Instead, it just adds an empty parentheses (remeber: anonymous categories), as shown below

@interface ClassNameWeWantToExtend ()

@end

It’s good to remember that

  • An extension can be declared only for the classes that we have original implementation of source code.
  • Any method or variable declared inside the extensions is not accessible even to the inherited classes. In consequence with extension we can add private methods and private variables that are only specific to the class.
    Notice that private methods can be also emulated by adding them to the implementation but not the interface.


Experiment 8: extension; compare emulated private methods with real private methods


For example, if we want to formally add a private privateMethodFromExtension method to the ClassWithExtension class defined below, we could include an extension in ClassWithExtension.m; yes, in *.m file!

#import <Foundation/Foundation.h>

@interface ClassWithExtension : NSObject

- (void) publicMethod;

@end
#import "ClassWithExtension.h"

@interface ClassWithExtension ()

- (void) privateMethodFromExtension;

@end

@implementation ClassWithExtension


- (void) publicMethod{
    NSLog(@"publicMethod");
    [self privateMethod];
    [self privateMethodFromExtension];
}

- (void) privateMethodFromExtension{
    NSLog(@"privateMethodFromExtension");
}

- (void) privateMethod{
    NSLog(@"privateMethod");
}

@end
#import <Foundation/Foundation.h>
#include "ClassWithExtension.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ClassWithExtension *cwe = [[ClassWithExtension alloc] init];
        [cwe publicMethod];
        // We can't do the folowing
        // [cwe privateMethodFromExtension];
        // [cwe privateMethod];
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-03 22:47:34.132 app_003_extensions_ex8[10380:1055235] publicMethod
2016-03-03 22:47:34.134 app_003_extensions_ex8[10380:1055235] privateMethod
2016-03-03 22:47:34.134 app_003_extensions_ex8[10380:1055235] privateMethodFromExtension
Program ended with exit code: 0
[/fulmanp]




Blocks


Blocks are Objective-C’s anonymous functions. They let us pass distinct segments of code between objects as we do with data. To understand how it works we should know something about closures, so we start with this topic.


Closures


A little bit of theory. In programming languages, closures are a technique for implementing lexically scoped name binding. We can think of it as a record storing a function together with an environment. Each variable that is used locally, but defined in an enclosing scope is associate with the value or storage location to which the name was bound when the closure was created. What is important, a closure, unlike a plain function, allows the function to access those captured variables through the closure’s reference to them, even when the function is invoked outside their scope.

To understand how it works, let’s analyze the following example (this code is in JavaScript, so you can test it in browser’s developer tools)

var f;

function foo() {
  var x=1;
  f = function() { return ++x; };
  var result = f();
  console.log('inside foo, call to f(): ' + result);
}

foo();
console.log('call to f(): ' + f());

In line

  f = function() { return ++x; };

an anonymous function is defined. Variable x is defined outside of this function but is used inside. So, we can say, that this functon needs x to work corectly. And there is nothing surprising
that call

foo();

and in consequence

var result = f();

works and returns correct values
[fulmanp type=”terminal”]
inside foo, call to f(): 2
call to f(): 3
[/fulmanp]
The queestion is how it is possible that code

console.log('call to f(): ' + f());

also works?! Now we are outside of foo function and we try to call f(). How it could be that f() knows value of the x variable? This is when closures comes into play.
The assignment statement

  f = function() { return ++x; };

,,saves” function but also all the outer environment needed to execute this function (in this case, x variable).

Now consider a second example (again in JavaScript).

function referenceValue(x) {
  return function(y) {
    return y + x;
  };
}

var moveFrom3By = referenceValue(3);
var moveFrom5By = referenceValue(5);

console.log(moveFrom3By(7));
console.log(moveFrom5By(9));

This code should print 10 and 14 as a result.

The above code defines a function referenceValue with a parameter x and a nested anonymous function. The nested function has access to x, because is in the lexical scope of x (note that x is not local to this anonymous function). The function referenceValue returns a closure containing

  • the inner anonymous function, which adds the y value to the x (reference) value,
  • and a reference to the variable x from this invocation of referenceValue, so inner function will know where to find it when invoked.

Note that, as referenceValue returns a function. This means that both moveFrom3By and moveFrom5By are of function type, so we can invoke moveFrom3By(7) and moveFrom5By(9). INteresting is that while moveFrom3By and moveFrom5By refer to the same anonymous function, the associated environments differ, and invoking the closures will bind the name x to two distinct variables with different values (3 and 5) in the two invocations, thus evaluating the function to different results (10 and 14).



Experiment 9: simple block usage


We can think of a block as a normal function. We can declare a block variable just like we would declare a function, define the block as though we would implement a function, and then call the block as if it were a function. In the following code please find

  • Simple block declaration of the form
    returnType (^blockName)(argumentType);
    
  • Simple block implementation of the form
    returnType (^blockName)(argumentType)= ^{
    };
    
  • Simple block invocation of the form
    blockName(argumentType);
    
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Declare the block variable
        int (^blockVariable1)(int x, int y);
        int (^blockVariable2)(int x, int y);
        int (^blockVariable3)(void);
        int (^blockVariable4)(void);
        
        // Create and assign the block
        blockVariable1 = ^int(int x, int y) {
            return x + y;
        };
        blockVariable2 = ^(int x, int y) {
            return x + y;
        };
        blockVariable3 = ^int {
            return 2;
        };
        blockVariable4 = ^{
            return 2;
        };
        
        // Call the blocks
        NSLog(@"%d", blockVariable1(3,5));
        NSLog(@"%d", blockVariable2(3,5));
        NSLog(@"%d", blockVariable3());
        NSLog(@"%d", blockVariable4());
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-04 21:57:49.119 app_003_blocks_ex9[10763:1122352] 8
2016-03-04 21:57:49.120 app_003_blocks_ex9[10763:1122352] 8
2016-03-04 21:57:49.120 app_003_blocks_ex9[10763:1122352] 2
2016-03-04 21:57:49.120 app_003_blocks_ex9[10763:1122352] 2
Program ended with exit code: 0
[/fulmanp]
Notice that

  • in signature begining block return type can be omitted if desired (compare methods blockVariable1 and blockVariable2);
  • if a block doesn’t take any parameters, we can omit the argument list in its entirety (compare methods blockVariable3 and blockVariable4)

Having above code we may think that blocks are just a complicated way of defining functions. In some sense it’s true, but we have to remember that block are implemented as closures and this gives us a lot of new options.



Experiment 10: use closures


Below there is a simple block example where closure is used. Analyzing this code, please note for __block modifier. By default non-local variables are saved in closure as constant values — this is safe as it prevents us from accidentally changing them from within the block. If, for some reason, we want to change this behaviour we can declare a non-local variable with the __block storage modifier.

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int immutable = 0;
        int (^block1)(void) = ^ {
            // We can't do this
            // immutable += 1;
            // Xcode reports: Variable is not assignable (missing __block type specifier)
            return immutable;
        };
        NSLog(@"%d", block1());
        NSLog(@"%d", block1());
        NSLog(@"%d", block1());
        
        __block int mutable = 0;
        int (^block2)(void) = ^ {
            mutable += 1;
            return mutable;
        };
        NSLog(@"%d", block2());
        NSLog(@"%d", block2());
        NSLog(@"%d", block2());
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-04 22:18:15.475 app_003_blocks_ex10[10868:1128737] 0
2016-03-04 22:18:15.482 app_003_blocks_ex10[10868:1128737] 0
2016-03-04 22:18:15.483 app_003_blocks_ex10[10868:1128737] 0
2016-03-04 22:18:15.483 app_003_blocks_ex10[10868:1128737] 1
2016-03-04 22:18:15.483 app_003_blocks_ex10[10868:1128737] 2
2016-03-04 22:18:15.483 app_003_blocks_ex10[10868:1128737] 3
Program ended with exit code: 0
[/fulmanp]


Experiment 11: use blocks as method parameters


Storing blocks in variables is occasionally useful, but in the real world, they’re more likely to be used as method parameters. They solve the same problem as function pointers, but the fact that they can be defined inline makes the resulting code much easier to read.

#import <Foundation/Foundation.h>

@interface Foo : NSObject

@property int x, y, z;

- (void)modifyWithMethod:(int (^)(int x, int y))method
            useArgumentX:(int)x
            useArgumentY:(int)y;

@end
#import "Foo.h"

@implementation Foo

- (void)modifyWithMethod:(int (^)(int x, int y))method
            useArgumentX:(int)x
            useArgumentY:(int)y{
    _z = method(x, y);
}

@end
#import <Foundation/Foundation.h>
#import "Foo.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Foo *foo = [[Foo alloc] init];
        
        [foo modifyWithMethod:^(int x, int y) {
            return x + y;
        } useArgumentX:3 useArgumentY:5];
        NSLog(@"%d", foo.z);
        
        [foo modifyWithMethod:^(int x, int y) {
            return x * y;
        } useArgumentX:3 useArgumentY:5];
        NSLog(@"%d", foo.z);
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-03-04 22:55:24.839 app_003_blocks_ex11[11117:1142558] 8
2016-03-04 22:55:24.840 app_003_blocks_ex11[11117:1142558] 15
Program ended with exit code: 0
[/fulmanp]




Delegates

Simply speaking, delegates are a design pattern; there is no special syntax or language support for it.

A delegate is just an object that another (delegating) object sends messages to when certain things happen, so that the delegate can handle app-specific details the original object wasn’t designed for. It’s a way of customizing behavior without subclassing.

In this pattern one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object — the delegate — and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by making many different things: updating the appearance or state of itself, return some value etc. The main value of delegation is that it allows us to easily customize the behavior of several objects in one central object.

In short

  • The class that is delegating = The class that is sending message
  • The class that is the delegate = The class that is receiving message
  • delegat = do something on behalf of another object

#ifndef ProtocolForDelegate_h
#define ProtocolForDelegate_h

@protocol ProtocolForDelegate

@required
- (void) notifyDelegate:(NSString*)message;

@end

#endif /* ProtocolForDelegate_h */
#import <Foundation/Foundation.h>
#import "ProtocolForDelegate.h"

@interface DelegatingClass : NSObject 
@property (weak) id  delegate;
- (void) sendMessageToDelegate:(NSString*)message;
@end
#import "DelegatingClass.h"

@implementation DelegatingClass

- (void) sendMessageToDelegate:(NSString*)message;
{
    NSString *delegateResponse;
    if ([self.delegate respondsToSelector:@selector(notifyDelegate:)]) {
        delegateResponse = [self.delegate performSelector:@selector(notifyDelegate:) withObject:message];
    }
    NSLog(@"Response from delegate: >%@<", delegateResponse);
}
@end
#import <Foundation/Foundation.h>
#import "ProtocolForDelegate.h"

@interface Delegate : NSObject <ProtocolForDelegate>
- (NSString *) notifyDelegate:(NSString*)message;
@end
#import "Delegate.h"

@implementation Delegate
- (NSString *) notifyDelegate:(NSString*)message{
    NSLog(@"Delegate notified with message >%@<", message);
    return @"Response from delegate";
}
@end
#import <Foundation/Foundation.h>
#import "DelegatingClass.h"
#import "Delegate.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        DelegatingClass *delegating = [[DelegatingClass alloc] init];
        Delegate *delegate = [[Delegate alloc] init];
        
        // Set delegate for delegating object
        delegating.delegate = delegate;
        
        // Use method from DelegatingClass
        [delegating sendMessageToDelegate:@"Message for delegate"];
    }
    return 0;
}

[fulmanp type=”terminal”]
2016-12-16 23:58:44.550236 delegates_001[9835:1477757] Delegate notified with message >Message for delegate< 2016-12-16 23:58:44.550576 delegates_001[9835:1477757] Response from delegate: >Response from delegate< Program ended with exit code: 0 [/fulmanp]



Excercise 3.0


Create a project according to the following rules


Leave a Reply

Your email address will not be published. Required fields are marked *