Objective-CiOS开发技术基础知识

iOS:Protocol详解

2019-05-17  本文已影响0人  码小菜

目录
一,基本概念
二,如何使用
三,特点
四,使用场景
五,系统常见协议
六,底层实现

一,基本概念

继承树.png

二,如何使用

@protocol PersonProtocol <NSObject>

@required
- (void)eat;

@optional
- (void)run;

@end
@protocol PersonProtocol <NSObject>
@property (nonatomic, copy) NSString *name;
@end

@interface Person : NSObject <PersonProtocol>
@end
属性.png 成员变量.png 单独文件.png

三,特点

四,使用场景

// CustomViewProtocol
@protocol CustomViewProtocol <NSObject>
- (void)setModel:(id)model;
@end

// CustomView
@interface CustomView : UIView <CustomViewProtocol>
@end

@implementation CustomView
- (void)setModel:(id)model { // 实现协议方法
    NSLog(@"view---%@", model);
}
@end

// CustomTableView
@interface CustomTableView : UITableView <CustomViewProtocol>
@end

@implementation CustomTableView
- (void)setModel:(id)model { // 实现协议方法
    NSLog(@"tableView---%@", model);
}
@end

// 传递数据
NSArray *views = @[[CustomView new], [CustomTableView new]];
NSArray *models = @[@"111", @"222"];
[views enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    // 判断是否遵守此协议
    if ([obj conformsToProtocol:@protocol(CustomViewProtocol)]) {
        [obj setModel:models[idx]];
    }
}];

// 打印结果
view---111
tableView---222
面向接口编程.png

1,bridge:关联接口和实现

// .h文件
@interface ServerBridge : NSObject
+ (void)bindServer:(id)server andProtocol:(Protocol *)protocol;
+ (id)serverForProtocol:(Protocol *)protocol;
@end

// .m文件
@interface ServerBridge ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *serverStore;
@end

@implementation ServerBridge
- (NSMutableDictionary<NSString *,id> *)serverStore {
    if (!_serverStore) {
        _serverStore = [NSMutableDictionary new];
    }
    return _serverStore;
}
+ (instancetype)shared {
    static id _bridge = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _bridge = [[self alloc] init];
    });
    return _bridge;
}
+ (void)bindServer:(id)server andProtocol:(Protocol *)protocol {
    if ([server conformsToProtocol:protocol]) {
        [[ServerBridge shared].serverStore setValue:server
                                             forKey:NSStringFromProtocol(protocol)];
    }
}
+ (id)serverForProtocol:(Protocol *)protocol {
    return [[ServerBridge shared].serverStore valueForKey:NSStringFromProtocol(protocol)];
}
@end

2,protocol:对外暴露的接口

@protocol ServerProtocol <NSObject>
@property (nonatomic, copy) NSString *provideData;
- (void)doSomething;
@end

3,server:接口的具体实现

@interface Server () <ServerProtocol>
@end

@implementation Server
@synthesize provideData;
+ (void)load {
    [ServerBridge bindServer:[self new]
                 andProtocol:@protocol(ServerProtocol)];
}
- (NSString *)provideData {
    return @"server provide data";
}
- (void)doSomething {
    NSLog(@"server do something");
}
@end

4,business:使用接口的业务

id<ServerProtocol> server = [ServerBridge serverForProtocol:@protocol(ServerProtocol)];
NSLog(@"%@", server.provideData);
[server doSomething];

// 打印结果
server provide data
server do something

五,系统常见协议

@protocol NSObject
- (id)performSelector:(SEL)aSelector;
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)respondsToSelector:(SEL)aSelector;
...
@end
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end

1,继承关系

// Man继承自Person,Person遵守了NSCopying协议

@implementation Person
- (id)copyWithZone:(NSZone *)zone {
    Person *p = [[self.class allocWithZone:zone] init];
    return p;
}
@end

@implementation Man
- (id)copyWithZone:(NSZone *)zone {
    // person没有实现copyWithZone方法
    //Man *m = [[self.class allocWithZone:zone] init];
    // person实现了copyWithZone方法
    Man *m = [super copyWithZone:zone];
    return m;
}
@end

/*
为什么用self.class?
答:为了保证创建正确的对象,父类调用就创建父类对象,子类调用就创建子类对象
*/

2,初始化方法

@implementation Person
- (id)copyWithZone:(NSZone *)zone {
    // 无自定义初始化方法
    Person *p = [[self.class allocWithZone:zone] init];
    // 有自定义初始化方法
    Man *p = [[self.class allocWithZone:zone] initWithName:self.name];
    return p;
}

3,属性赋值

@implementation Person
- (id)copyWithZone:(NSZone *)zone {
    Person *p = [[self.class allocWithZone:zone] init];
    // 基本数据类型
    p.age = self.age;
    // 对象类型
    p.name = [self.name copyWithZone:zone];
    // 私有属性
    p -> _height = _height;
    return p;
}
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
@implementation Person
- (id)mutableCopyWithZone:(NSZone *)zone {
    Person *p = [[self.class allocWithZone:zone] init];
    p.name = [self.name mutableCopyWithZone:zone];
    return p;
}
@end
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
@end
// Person
@interface Person : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@end

@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder { // 实现协议方法
    [aCoder encodeObject:self.name forKey:@"name"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder { // 实现协议方法
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
    }
    return self;
}
@end

// 存储Person对象
- (void)viewDidLoad {
    [super viewDidLoad];

    [self save];
    [self read];
}
- (NSString *)filePath {
    NSString *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    return [document stringByAppendingPathComponent:@"person.data"];
}
- (void)save {
    Person *p = [Person new];
    p.name = @"zhangsan";
    [NSKeyedArchiver archiveRootObject:p toFile:self.filePath];
}
- (void)read {
    Person *p = [NSKeyedUnarchiver unarchiveObjectWithFile:self.filePath];
    NSLog(@"%@", p.name);
}

// 打印结果
zhangsan
@protocol NSSecureCoding <NSCoding>
@property (class, readonly) BOOL supportsSecureCoding;
@end
@interface Person : NSObject <NSSecureCoding>
@property (nonatomic, copy) NSString *name;
@end

@implementation Person
+ (BOOL)supportsSecureCoding { // 对编码解码进行加密
    return YES; 
}
- (void)encodeWithCoder:(NSCoder *)aCoder { // 编码
    [aCoder encodeObject:self.name forKey:@"name"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder { // 解码
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
    }
    return self;
}
@end

六,底层实现

struct protocol_t : objc_object {
    const char *mangledName; // 重整的名称
    const char *_demangledName; // 没有重整的名称
    struct protocol_list_t *protocols; // 遵守的协议
    method_list_t *classMethods; // 类方法
    method_list_t *optionalClassMethods; // 可选的类方法
    method_list_t *instanceMethods; // 实例方法
    method_list_t *optionalInstanceMethods; // 可选的实例方法
    property_list_t *_classProperties; // 类属性(用class修饰的)
    property_list_t *instanceProperties; // 实例属性
    ...
}

1,类方法和实例方法调用的都是class_conformsToProtocol方法

+ (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

- (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = self.class; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

2,从class中取出protocols(类遵守的所有协议),然后逐个跟传入的protocol进行比较,判断是否相等或者调用protocol_conformsToProtocol_nolock方法

BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen) {
    protocol_t *proto = newprotocol(proto_gen);

    if (!cls) return NO;
    if (!proto_gen) return NO;

    rwlock_reader_t lock(runtimeLock);
    assert(cls->isRealized());

    for (const auto& proto_ref : cls->data()->protocols) {
        protocol_t *p = remapProtocol(proto_ref);
        if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) {
            return YES;
        }
    }

    return NO;
}

3,从当前协议中取出protocols(当前协议遵守的所有协议),然后逐个跟传入的protocol进行比较,判断mangledName是否相等,如果不相等就递归调用自身继续比较

static bool protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other) {
    runtimeLock.assertLocked();

    if (!self || !other) {
        return NO;
    }

    if (0 == strcmp(self->mangledName, other->mangledName)) {
        return YES;
    }

    if (self->protocols) {
        uintptr_t i;
        for (i = 0; i < self->protocols->count; i++) {
            protocol_t *proto = remapProtocol(self->protocols->list[i]);
            if (0 == strcmp(other->mangledName, proto->mangledName)) {
                return YES;
            }
            if (protocol_conformsToProtocol_nolock(proto, other)) {
                return YES;
            }
        }
    }

    return NO;
}
上一篇 下一篇

猜你喜欢

热点阅读