Generics worked pretty well for Swift, and in retrospect I sometimes want to use this technique in OC as well. OC may be considered generic for iOS development, but Apple introduced generics in OC 15 years ago. Although the purpose of introducing generics is to make Swift and OC compatible (Apple calls it Lightweight generics).

Many people have written code like this:

@property NSArray<Model *> *dataSource;
Copy the code

This line of code declares an array property called dataSource in a class. The addition of

restricts the elements in this array to be Model objects. In the following code self.datasource [0] is directly a Model object. The corresponding Swift code should look like this:

var dataSource: [Model]
Copy the code

Now consider this scenario. Fruit shop, butcher shop, vegetable shop… . This situation:

class Mall<T> {
    private var stock: [T] = []
    
    func buy(_ product: T) {
        stock.append(product)
    }
    func sell(a) -> T? {
        let res = stock.last
        stock.removeLast()
        return res
    }
}
Copy the code

So this code declares a generic class called Mall that has both stock and buy and sell methods. When using this class, we can first declare several protocols or enumerations representing various goods:

enum Fruits {
    case apple, banana
}
protocol Meats {}
class Vegetables {}
Copy the code

We then initialize the store generic class by passing in the corresponding type, enumeration, and protocol:

let mall = Mall<Fruits> ()let mall2 = Mall<Meats> ()let mall3 = Mall<Vegetables> ()Copy the code

Using a stereotype lets us circumvent the need to declare FruitMall MeatsMall, etc., and then write pretty much the same code in each object. You might want to use inheritance or protocols at this point, but even if it does save a lot of code, maintaining classes is a pain in the neck.

How do you implement the same functionality in OC?

.h

@interface Mall<T> : NSObject

- (void)buy:(T)product;
- (T)sell;

@end
Copy the code

.m

@interface Mall()

@property (nonatomic, strong) NSMutableArray<id> *stock;

@end
    
@implementation Mall

- (instancetype)init {
    self = [super init];
    if (self) {
        _stock = [NSMutableArray array];
    }
    return self;
}

- (void)buy:(id)product {
    [_stock addObject:product];
}

- (id)sell {
    id res = _stock.lastObject;
    [_stock removeLastObject];
    return res;
}

@end
Copy the code

This code does the same thing. In use:

You still need a protocol or class, but enumerations seem to be out of the question.

Mall<Meats *> *mall = [[Mall alloc] init]; // Class Mall<id< hotpot >> *mall2 = [[Mall alloc] init]; / / agreementCopy the code

We can even add an Extension to Mall (category for OC):

extension Mall {
    func sellAll(a)- > [T] {
        let res = stock
        stock.removeAll()
        return res
    }
}
Copy the code

Another thing to watch out for in OC is type conversions. Suppose you have two classes, one of which is a subclass of the other. As follows, there is a drink shop and a mineral water shop. Drinks is the parent of Water.

Mall<Water *> *waterMall = [[Mall alloc] init];
Mall<Drinks *> *drinkMall = waterMall;
//Warning: Incompatible pointer types initializing 'Mall<Drinks *> *' with an expression of type 'Mall<Water *> *'
Copy the code

If such a conversion occurs, the compiler throws a warning ⚠️. If you want to convert a generic object containing subclasses to a generic object containing a parent class, use the __covariant keyword. Let’s look at the.h file again.

@interface Mall<__covariant T> : NSObject

- (void)buy:(T)product;
- (T)sell;

@end
Copy the code

Conversely, there is the __contravariant keyword, which allows a generic object containing a parent class to be converted into a generic object containing subclasses. There’s only one problem: two keywords can’t exist at the same time.