Objective-C:诡计!变相完成Protocol中的默认实现|技术点评

694 阅读3分钟

从Swift中的Protocol特性聊到的面试题

有一次面试上,和面试官讨论起Swift的特性的时候,我们聊到了Swift的Protocol。


Swift的中的Protocol可以写其Extension方法。

如果遵守该Protocol的类或者结构体针对协议方法的不重写的话,那么就会调用Protocol中Extension的默认方法。


上面这段话用代码写非常容易理解,上一段代码就知道了:

protocol Chef {
    func makeFood()
}

extension Chef {
    func makeFood() {
        print("make food")
    }
}

class A: Chef {
    func makeFood() {
        print("A food")
    }
}

class B: Chef {
    
}

let a = A()
let b = B()

a.makeFood()
b.makeFood()

打印的日志如下

A food
make food

“如果我想OC中的Protocol也能够有类似Swift的Protocol中Extension的特性,该如何实现呢?”

这个就是当时面试官提给我的问题。

OC中Protocol的灵魂拷问

这个问题是确实是问到我了,OC的中Protocol可以用@request和@optional在遵循的类中的去做强制实现和可选实现,但是要想要一个类去调用遵守协议的默认实现,这个该怎么办呢?

想了很久,在偶然的一次编码中,发现了。

我们先来创建一个协议,创建文件ClassNameConvertible.h

@protocol ClassNameConvertible <NSObject>

+ (NSString *)className;

- (NSString *)className;

@end

明眼人一眼就看出来这个协议想干嘛,无非就是获取类名的字符串嘛,我不是经常写OC了,所以总会忽略掉一些OC很重要的细节。


问:OC中什么类型可以遵守协议?

答:对象!


看看定义一个OC协议的格式

@protocol 协议名 <NSObject>

一些方法......

@end

创建的时候已经了然。


也就是说,遵守协议的类型必然都是对象,而对象的基类是什么?

是NSObject!

如果NSObject去遵守定义的协议A,并实现协议的默认方法,其他的任何类都会遵守协议A,一旦调用协议A的方法,其他类如果不重写协议A的方法,那么就会调用NSObject中协议A的默认方法。

如何让NSObject去遵守定义的协议A?

可以创建一个NSObject的分类去遵守协议A!


这就是解决了上述的问题。

OC中Protocol中实现默认Extension的方式

来,代码创建NSObject+ClassName的分类:

.h文件

#import <Foundation/Foundation.h>

#import "ClassNameConvertible.h"

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (ClassName)<ClassNameConvertible>

@end

NS_ASSUME_NONNULL_END

.m文件

#import "NSObject+ClassName.h"

@implementation NSObject (ClassName)

+ (NSString *)className {
    return NSStringFromClass(self);
}

- (NSString *)className {
    return [[self class] className];
}

@end

然后我们任意创建了一个SomeView类继承自UIView

.h和.m文件

NS_ASSUME_NONNULL_BEGIN

@interface SomeView : UIView

@end

NS_ASSUME_NONNULL_END
@implementation SomeView

@end

最后我们来一段测试代码:

SomeView *someView = [SomeView new];

// 调用函数
NSString *instaceName = [someView className];
NSString *className = [SomeView className];

NSLog(@"instaceName:%@", instaceName);
NSLog(@"className:%@", className);

// 是否遵循协议
if ([someView conformsToProtocol:@protocol(ClassNameConvertible)]) {
    NSLog(@"someView 遵守ClassNameConvertible 协议");
}

打印日志:

instaceName:SomeView
className:SomeView
someView 遵守ClassNameConvertible 协议

有人会说,对NSObject的分类添加了一个ClassNameConvertible协议,实质上是扩展了整个系统的方法,这样代价也太大了吧。

不一定要在NSObject的分类去遵守协议A,可以在你定义的专用类的分类中去遵守协议A。

这样的话就既实现了Protocol的Extension的默认实现,也将影响力度控制在自己手里。并且你拥有了一个遵守协议的类。

一旦将这个思路扩展下去,其实OC中类泛型的思路也就更加明朗了。

嗯,这也许是一个伪命题。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看活动详情