Skip to content

Commit

Permalink
Merge pull request #2 from HaloWang/master - perfect message forward
Browse files Browse the repository at this point in the history
  • Loading branch information
HaloWang authored and Tuccuay committed Apr 29, 2016
1 parent 5e8fba9 commit 021428f
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 30 deletions.
43 changes: 40 additions & 3 deletions ForwardMessage/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="jKV-aG-N9G">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L2J-GH-GVP">
<rect key="frame" x="20" y="72" width="60" height="30"/>
<state key="normal" title="实例方法"/>
<connections>
<action selector="instanceMethodCalling" destination="BYZ-38-t0r" eventType="touchUpInside" id="alc-pL-IYP"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qn3-Fc-vBe">
<rect key="frame" x="21" y="110" width="45" height="30"/>
<state key="normal" title="类方法"/>
<connections>
<action selector="classMethodCalling" destination="BYZ-38-t0r" eventType="touchUpInside" id="52K-o0-lvH"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="1yZ-bv-73P"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="816" y="-370"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="yrP-D4-I2P">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="jKV-aG-N9G" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="09b-gs-mYv">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="JMK-d5-cAu"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="LaK-zr-J3m" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="129" y="-370"/>
</scene>
</scenes>
</document>
2 changes: 2 additions & 0 deletions ForwardMessage/Cat.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@

+ (void)stoke;

- (void)stoke;

@end
76 changes: 56 additions & 20 deletions ForwardMessage/Cat.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,89 @@

@implementation Cat

// 在没有找到方法时,会先调用此方法,可用于动态添加方法
/*
#pragma mark - 实例方法

// 第一步
// 在没有找到方法时,会先调用此方法,可用于动态添加方法
// 返回 YES 表示相应 selector 的实现已经被找到并添加到了类中,否则返回 NO

+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
return YES;
}
*/

// 如果上面返回 NO,就会进入这一步,用于指定备选响应此 SEL 的对象
// 如果返回 self 就会死循环
/*
// 第二步
// 如果第一步的返回 NO 或者直接返回了 YES 而没有添加方法,该方法被调用
// 在这个方法中,我们可以指定一个可以返回一个可以响应该方法的对象
// 如果返回 self 就会死循环

- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}
*/

// 指定方法签名,若返回 nil,则不会进入下一步,而是无法处理消息
// 第三步
// 如果 `forwardingTargetForSelector:` 返回了 nil,则该方法会被调用,系统会询问我们要一个合法的『类型编码(Type Encoding)』
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
// 若返回 nil,则不会进入下一步,而是无法处理消息

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"stoke"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

return [super methodSignatureForSelector:aSelector];
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

// 当实现了此方法后,-doesNotRecognizeSelector: 将不会被调用
// 如果要测试找不到方法,可以注释掉这一个方法
// 在这里进行消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {

// 我们还可以改变方法选择器
[anInvocation setSelector:@selector(touch)];
// 改变方法选择器后,还需要指定是哪个对象的方法
[anInvocation invokeWithTarget:self];
}

- (void)touch {
NSLog(@"Cat 没有实现 -stoke 方法,并且成功的转成了 -touch 方法");
}

- (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"无法处理消息:%@", NSStringFromSelector(aSelector));
}

+ (void)touch {
NSLog(@"Cat 没有实现 stoke 方法,并且成功的转成了 touch 方法");
#pragma mark - 类方法

// 类方法也会直接消息转发机制
// 不过有些东西可能会比较有趣:
// 下述方法中:
// forwardingTargetForSelector
// methodSignatureForSelector
// forwardInvocation
// doesNotRecognizeSelector
// 我们将前面的修饰符由 "-" 替换为了 "+",
// 但当我们在上述各个方法中的实现用调用 [super method] 时,method 的定义还是 NSObject 的实例方法
// 这可能就和元类(MetaClass)相关了

// 与 resolveInstanceMethod 相对应
+ (BOOL)resolveClassMethod:(SEL)sel {
return NO;
}

+ (void)stoke {
Cat *carson = [[Cat alloc] init];
[Cat performSelector:@selector(touch) withObject:nil afterDelay:0];
+ (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation setSelector:@selector(touch)];
[anInvocation invokeWithTarget:self];
}

+ (void)touch {
NSLog(@"Cat 没有实现 +stoke 方法,并且成功的转成了 +touch 方法");
}

+ (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"无法处理消息:%@", NSStringFromSelector(aSelector));
}

@end
12 changes: 5 additions & 7 deletions ForwardMessage/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// ForwardMessage / 消息转发

#import "ViewController.h"

#import "Cat.h"

@interface ViewController ()
Expand All @@ -13,13 +12,12 @@ @interface ViewController ()

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

- (IBAction)instanceMethodCalling {
[[Cat new] stoke];
}

- (IBAction)classMethodCalling {
[Cat stoke];

// 输出:
// 2016-04-24 00:27:12.823 ForwardMessage[50298:3350384] Cat 没有实现 stoke 方法,并且成功的转成了 touch 方法
}

@end

0 comments on commit 021428f

Please sign in to comment.