Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.

This paper also participates inProject DigginTo win the creative gift package and challenge the creative incentive money.

preface

When you talk about generics in iOS, you usually think of generics in Swift, but there are generics in Objective-C as well, but we tend to forget about generics in OC.

Today’s lesson is what I understand generics to be in OC.

Generics in NSArray

Because of my work, I have to keep track of old projects, and I often see code like this in old projects:

@property (nonatomic, strong) NSMutableArray *dataSource;

NSMutableArray *mutableArray = [NSMutableArray array];
Copy the code

It is written normally, using strong to decorate mutable arrays, and using factory methods to initialize arrays.

In fact, the above writing can be understood as:

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

NSMutableArray<id> *mutableArray = [NSMutableArray array];

Copy the code

Any object type can be stuffed into an array.

This is not a good thing for our development. Normally we only have one data type in the array, and it is better to be able to specify the type of the array:

@property (nonatomic, strong) NSMutableArray<ResultItem *> *dataSource; NSMutableArray<NSString *> *mutableArray = [NSMutableArray array];Copy the code

So I can get a better understanding of the data types that are stored in the array, even though the amount of code is increasing, but for the person who’s maintaining the code, and the person who’s assigning to the array, locking the data type of the array tells the compiler that you can’t just randomly assign to the array, you need to match the type.

Let’s take a look at one of the methods of NSArray, and see if this generic looks a little bit like the one defined in Swift.

@interface NSArray<ObjectType> (NSExtendedArray)

- (NSArray<ObjectType> *)arrayByAddingObject:(ObjectType)anObject;

@end
Copy the code

Generics in NSDictionary

The generic definition in NSDictionary is very similar to that in NSArray, and I’ll show you a little bit more here. Just look at this example:

NSDictionary<NSString *, NSValue *>
Copy the code

Remember, the OC language should not be discouraged from writing more code, although long code is not a good thing, but clearly written code is necessary.

The generic type used when defining the proxy

When we define an agent in OC, we usually use generics, such as the following code:

#import <UIKit/UIKit.h>

@class CommentModel;

@protocol CommentCellDelegate <NSObject>

- (void)deleteButtonAction:(UIButton *)button commentModel:(CommentModel *)model;

@end

@interface CommentCell : UITableViewCell

@property (nonatomic, strong) CommentModel *model;

@property (nonatomic, weak) id<CommentCellDelegate> delegate;

@end
Copy the code

When we declare the delegate property, we use the id

, which says what:

First, delegate is an object type, and second, this object type must comply with the CommentDelegate protocol.

So we see that types that adhere to the CommentDelegate protocol are generally written like this:

@interface CommentListController ()<CommentCellDelegate, UITableViewDataSource, UITableViewDelegate>
Copy the code

Try using generics to model JSON in OC

In OC, I tried to write a base class for network request response via generics.

.h files:

#import <Foundation/Foundation.h>

#import <YYModel.h>

NS_ASSUME_NONNULL_BEGIN

@interface ListItem: NSObject<YYModel>

@property (nonatomic , copy) NSString *topicTittle;
@property (nonatomic , copy) NSString *upTime;
@property (nonatomic , copy) NSString *topicDesc;
@property (nonatomic , assign) NSInteger numId;
@property (nonatomic , copy) NSString *topicImageUrl;
@property (nonatomic , assign) NSInteger topicStatus;
@property (nonatomic , assign) NSInteger topicOrder;

@end


@interface BaseResponse<T: NSObject<YYModel> *> : NSObject <YYModel>

@property (nonatomic , assign) NSInteger code;
@property (nonatomic, strong) NSArray<T>  *list;

@end

NS_ASSUME_NONNULL_END
Copy the code

.m files:

@implementation ListItem

+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper {

    return @{
             @"numId": @"id"
             };

}

@end


@implementation BaseResponse

+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass {

    return @{
             @"list": [NSObject<YYModel> class]
             };
}

@end
Copy the code

Anyone can see what I’m trying to do.

The BaseResponse type and its array elements of type list comply with the YYModel protocol through generic constraints.

BaseResponse is used to receive each request, and then the YYModel protocol is converted in one step.

Any ideas for Codable protocols?

So I went on to write the Internet request:

- (void)request {

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    [manager POST:@"" parameters:nil headers:nil progress:^(NSProgress * _Nonnull uploadProgress) {

    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

        BaseResponse<ListItem *>* response = [BaseResponse<ListItem *> yy_modelWithJSON:responseObject];

        NSLog(@"response: %@", response);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

    }];
}
Copy the code

Now is the exciting time to convert to BaseResponse ?

The answer is a pity:

List is converted to NSDictionary instead of ListItem.

And it’s pretty obvious what went wrong, right here:

+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass {

    return @{
             @"list": [NSObject<YYModel> class]
             };
}
Copy the code

I’m mapping the list here to [NSObject

class], when in fact I want to map it to @interface BaseResponse

*> : NSObject

declares

*>, but you can’t write this in.m:



An undefined T type is used

At this point, generic model transformations through generics break down.

This is why OC is always a request, a return model for network requests, and the outer layers of the model are always the same, because you can’t be unified by generics. Generics in OC are generally treated as placeholders.

OC is more about using its dynamics

We’ve seen a taxonomy of NSArray extension map functions implemented as follows

@implementation NSArray (Map) - (NSArray *)_map:(id(^)(id))handle { if (! handle || ! self) return self; NSMutableArray *arr = NSMutableArray.array; for (id obj in self) { id new = handle(obj); [arr addObject:new]; } return arr.copy; } @endCopy the code

The block after map is id(^)(id obj), the element of the array is id, and the return type is ID, not because it doesn’t want to use generic constraints, but because there is no way to use generic constraints.

In a map function that uses this NSArra, we can only define the specific type of an array by the specific definition of its type at runtime.

Id type

When I write these examples, I often use the ID type of OC

Id is a flexible pointer to an Object, and a pointer to any Object that inherits the Object (or NSObject) class. In cocoa, NSObject is the root of all classes. So the ID can point to any legitimate object in Cocoa.

It has similarities and differences with NSObject:

  • Id is a generic object type that can be used to store objects belonging to any class. It can be understood as a universal pointer

  • In the definition of ID, the * is wrapped, and the ID pointer can only point to OS objects

  • Both NSObject and ID can point to any object

  • NSObject wakes up compile-time checks (casts required)

  • Id does not need to cast, id can be used directly

  • When the compiler sees the ID, it assumes it is dynamically typed and does not check the type

Reference documentation

IOS OC implements higher-order functions such as Map, Filter, and Reduce

Id type in iOS

conclusion

It was hard for me to write a technical article about OC, mainly because I was trying to backtrack on generic usage and attempts in OC through Swift when I was writing Swift.

OC generics are weak on the whole, but for NSArray and NSDictionary, by declaring generics, we can have a clearer understanding of data structures and types.

The use of generics in a Delegate, on the other hand, can be thought of as constraints on types.

The generic system is often used in Swift and writes base class response bodies, but OC is not friendly to this support because OC is more of a language dynamic feature.

If you’re interested in generics in OC code, check out this Demo, since I wrote it arbitrarily, and switch to the Fastlane_auto branch to see the code.

We’ll see you next time.