ई.जेम्स की पोस्ट ने अधिकांश विचारों के लिए एक उत्कृष्ट समाधान दिया। लेकिन यूआईटीएक्स्टफिल्ड और यूआईटीएक्स्टव्यू जैसे कीबोर्ड निर्भर विचारों के लिए, यह अक्सर अनंत लूप की स्थिति में परिणाम देता है। इससे छुटकारा पाने के लिए, मैंने इसे कुछ अतिरिक्त कोड के साथ तय किया है जो जांचता है कि चयनकर्ता विशिष्ट प्रोटोकॉल में है या नहीं।
WZProtocolInterceptor.h
#import <Foundation/Foundation.h>
@interface WZProtocolInterceptor : NSObject
@property (nonatomic, readonly, copy) NSArray * interceptedProtocols;
@property (nonatomic, weak) id receiver;
@property (nonatomic, weak) id middleMan;
- (instancetype)initWithInterceptedProtocol:(Protocol *)interceptedProtocol;
- (instancetype)initWithInterceptedProtocols:(Protocol *)firstInterceptedProtocol, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithArrayOfInterceptedProtocols:(NSArray *)arrayOfInterceptedProtocols;
@end
WZProtocolInterceptor.m
#import <objc/runtime.h>
#import "WZProtocolInterceptor.h"
static inline BOOL selector_belongsToProtocol(SEL selector, Protocol * protocol);
@implementation WZProtocolInterceptor
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self.middleMan respondsToSelector:aSelector] &&
[self isSelectorContainedInInterceptedProtocols:aSelector])
return self.middleMan;
if ([self.receiver respondsToSelector:aSelector])
return self.receiver;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([self.middleMan respondsToSelector:aSelector] &&
[self isSelectorContainedInInterceptedProtocols:aSelector])
return YES;
if ([self.receiver respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
- (instancetype)initWithInterceptedProtocol:(Protocol *)interceptedProtocol
{
self = [super init];
if (self) {
_interceptedProtocols = @[interceptedProtocol];
}
return self;
}
- (instancetype)initWithInterceptedProtocols:(Protocol *)firstInterceptedProtocol, ...;
{
self = [super init];
if (self) {
NSMutableArray * mutableProtocols = [NSMutableArray array];
Protocol * eachInterceptedProtocol;
va_list argumentList;
if (firstInterceptedProtocol)
{
[mutableProtocols addObject:firstInterceptedProtocol];
va_start(argumentList, firstInterceptedProtocol);
while ((eachInterceptedProtocol = va_arg(argumentList, id))) {
[mutableProtocols addObject:eachInterceptedProtocol];
}
va_end(argumentList);
}
_interceptedProtocols = [mutableProtocols copy];
}
return self;
}
- (instancetype)initWithArrayOfInterceptedProtocols:(NSArray *)arrayOfInterceptedProtocols
{
self = [super init];
if (self) {
_interceptedProtocols = [arrayOfInterceptedProtocols copy];
}
return self;
}
- (void)dealloc
{
_interceptedProtocols = nil;
}
- (BOOL)isSelectorContainedInInterceptedProtocols:(SEL)aSelector
{
__block BOOL isSelectorContainedInInterceptedProtocols = NO;
[self.interceptedProtocols enumerateObjectsUsingBlock:^(Protocol * protocol, NSUInteger idx, BOOL *stop) {
isSelectorContainedInInterceptedProtocols = selector_belongsToProtocol(aSelector, protocol);
* stop = isSelectorContainedInInterceptedProtocols;
}];
return isSelectorContainedInInterceptedProtocols;
}
@end
BOOL selector_belongsToProtocol(SEL selector, Protocol * protocol)
{
// Reference: https://gist.github.com/numist/3838169
for (int optionbits = 0; optionbits < (1 << 2); optionbits++) {
BOOL required = optionbits & 1;
BOOL instance = !(optionbits & (1 << 1));
struct objc_method_description hasMethod = protocol_getMethodDescription(protocol, selector, required, instance);
if (hasMethod.name || hasMethod.types) {
return YES;
}
}
return NO;
}
और यहाँ स्विफ्ट 2 संस्करण है:
//
// NSProtocolInterpreter.swift
// Nest
//
// Created by Manfred Lau on 11/28/14.
// Copyright (c) 2014 WeZZard. All rights reserved.
//
import Foundation
/**
`NSProtocolInterceptor` is a proxy which intercepts messages to the middle man
which originally intended to send to the receiver.
- Discussion: `NSProtocolInterceptor` is a class cluster which dynamically
subclasses itself to conform to the intercepted protocols at the runtime.
*/
public final class NSProtocolInterceptor: NSObject {
/// Returns the intercepted protocols
public var interceptedProtocols: [Protocol] { return _interceptedProtocols }
private var _interceptedProtocols: [Protocol] = []
/// The receiver receives messages
public weak var receiver: NSObjectProtocol?
/// The middle man intercepts messages
public weak var middleMan: NSObjectProtocol?
private func doesSelectorBelongToAnyInterceptedProtocol(
aSelector: Selector) -> Bool
{
for aProtocol in _interceptedProtocols
where sel_belongsToProtocol(aSelector, aProtocol)
{
return true
}
return false
}
/// Returns the object to which unrecognized messages should first be
/// directed.
public override func forwardingTargetForSelector(aSelector: Selector)
-> AnyObject?
{
if middleMan?.respondsToSelector(aSelector) == true &&
doesSelectorBelongToAnyInterceptedProtocol(aSelector)
{
return middleMan
}
if receiver?.respondsToSelector(aSelector) == true {
return receiver
}
return super.forwardingTargetForSelector(aSelector)
}
/// Returns a Boolean value that indicates whether the receiver implements
/// or inherits a method that can respond to a specified message.
public override func respondsToSelector(aSelector: Selector) -> Bool {
if middleMan?.respondsToSelector(aSelector) == true &&
doesSelectorBelongToAnyInterceptedProtocol(aSelector)
{
return true
}
if receiver?.respondsToSelector(aSelector) == true {
return true
}
return super.respondsToSelector(aSelector)
}
/**
Create a protocol interceptor which intercepts a single Objecitve-C
protocol.
- Parameter protocols: An Objective-C protocol, such as
UITableViewDelegate.self.
*/
public class func forProtocol(aProtocol: Protocol)
-> NSProtocolInterceptor
{
return forProtocols([aProtocol])
}
/**
Create a protocol interceptor which intercepts a variable-length sort of
Objecitve-C protocols.
- Parameter protocols: A variable length sort of Objective-C protocol,
such as UITableViewDelegate.self.
*/
public class func forProtocols(protocols: Protocol ...)
-> NSProtocolInterceptor
{
return forProtocols(protocols)
}
/**
Create a protocol interceptor which intercepts an array of Objecitve-C
protocols.
- Parameter protocols: An array of Objective-C protocols, such as
[UITableViewDelegate.self].
*/
public class func forProtocols(protocols: [Protocol])
-> NSProtocolInterceptor
{
let protocolNames = protocols.map { NSStringFromProtocol($0) }
let sortedProtocolNames = protocolNames.sort()
let concatenatedName = sortedProtocolNames.joinWithSeparator(",")
let theConcreteClass = concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nil)
let protocolInterceptor = theConcreteClass.init()
as! NSProtocolInterceptor
protocolInterceptor._interceptedProtocols = protocols
return protocolInterceptor
}
/**
Return a subclass of `NSProtocolInterceptor` which conforms to specified
protocols.
- Parameter protocols: An array of Objective-C protocols. The
subclass returned from this function will conform to these protocols.
- Parameter concatenatedName: A string which came from concatenating
names of `protocols`.
- Parameter salt: A UInt number appended to the class name
which used for distinguishing the class name itself from the duplicated.
- Discussion: The return value type of this function can only be
`NSObject.Type`, because if you return with `NSProtocolInterceptor.Type`,
you can only init the returned class to be a `NSProtocolInterceptor` but not
its subclass.
*/
private class func concreteClassWithProtocols(protocols: [Protocol],
concatenatedName: String,
salt: UInt?)
-> NSObject.Type
{
let className: String = {
let basicClassName = "_" +
NSStringFromClass(NSProtocolInterceptor.self) +
"_" + concatenatedName
if let salt = salt { return basicClassName + "_\(salt)" }
else { return basicClassName }
}()
let nextSalt = salt.map {$0 + 1}
if let theClass = NSClassFromString(className) {
switch theClass {
case let anInterceptorClass as NSProtocolInterceptor.Type:
let isClassConformsToAllProtocols: Bool = {
// Check if the found class conforms to the protocols
for eachProtocol in protocols
where !class_conformsToProtocol(anInterceptorClass,
eachProtocol)
{
return false
}
return true
}()
if isClassConformsToAllProtocols {
return anInterceptorClass
} else {
return concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nextSalt)
}
default:
return concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nextSalt)
}
} else {
let subclass = objc_allocateClassPair(NSProtocolInterceptor.self,
className,
0)
as! NSObject.Type
for eachProtocol in protocols {
class_addProtocol(subclass, eachProtocol)
}
objc_registerClassPair(subclass)
return subclass
}
}
}
/**
Returns true when the given selector belongs to the given protocol.
*/
public func sel_belongsToProtocol(aSelector: Selector,
_ aProtocol: Protocol) -> Bool
{
for optionBits: UInt in 0..<(1 << 2) {
let isRequired = optionBits & 1 != 0
let isInstance = !(optionBits & (1 << 1) != 0)
let methodDescription = protocol_getMethodDescription(aProtocol,
aSelector, isRequired, isInstance)
if !objc_method_description_isEmpty(methodDescription)
{
return true
}
}
return false
}
public func objc_method_description_isEmpty(
var methodDescription: objc_method_description)
-> Bool
{
let ptr = withUnsafePointer(&methodDescription) { UnsafePointer<Int8>($0) }
for offset in 0..<sizeof(objc_method_description) {
if ptr[offset] != 0 {
return false
}
}
return true
}
Uglyyyyyyyyy, लेकिन दुर्भाग्य से आवश्यक:/ –
हाँ, एकमात्र अन्य विकल्प nsnotifications –
हैलो का उपयोग करना है ... मैंने इसे अपने स्वयं के कोड पर लागू किया है, कुछ प्रतिनिधि तरीकों के साथ यूआईटीएक्स्टफिल्ड के लिए यह काम करता है, लेकिन दूसरों के साथ परिणामस्वरूप अज्ञात चयनकर्ता उदाहरण 0x4b21800 ' – speeder