Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

改的有点多,主要对消息转发的自己思考 #2

Merged
merged 3 commits into from
Apr 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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