Skip to content

Instantly share code, notes, and snippets.

@rickytan
Last active September 28, 2018 07:25
Show Gist options
  • Save rickytan/fcc9c823985a5e71555feab170059ed7 to your computer and use it in GitHub Desktop.
Save rickytan/fcc9c823985a5e71555feab170059ed7 to your computer and use it in GitHub Desktop.
Safe key path, compile time key-path checking and auto-completion
#define __mz_macro_concat(A, B) __mz_macro_concat_(A, B)
#define __mz_macro_argcount(...) __mz_macro_at(6, __VA_ARGS__, 6, 5, 4, 3, 2, 1)
#define __mz_macro_head(...) __mz_macro_head_(__VA_ARGS__, 0)
#define __mz_macro_at(N, ...) __mz_macro_concat(__mz_macro_at, N)(__VA_ARGS__)
#define __mz_macro_at0(...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at1(_0, ...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at2(_0, _1, ...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at3(_0, _1, _2, ...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at4(_0, _1, _2, _3, ...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at5(_0, _1, _2, _3, _4, ...) __mz_macro_head(__VA_ARGS__)
#define __mz_macro_at6(_0, _1, _2, _3, _4, _5, ...) __mz_macro_head(__VA_ARGS__)
#define MZKVOKeyPath(Class, ...) __mz_macro_concat(MZKVOKeyPath, __mz_macro_argcount(__VA_ARGS__))(Class, __VA_ARGS__)
#define MZKVOKeyPath0(Class) ((Class *)nil)
#define MZKVOKeyPath1(Class, path0) ((void)(NO && ((void)(((Class *)nil).path0), NO)), @#path0)
#define MZKVOKeyPath2(Class, path0, path1) ((void)(NO && ((void)((((Class *)nil).path0).path1), NO)), @#path0 "." #path1)
#define MZKVOKeyPath3(Class, path0, path1, path2) ((void)(NO && ((void)(((((Class *)nil).path0).path1).path2), NO)), @#path0 "." #path1 "." #path2)
#define MZKVOKeyPath4(Class, path0, path1, path2, path3) ((void)(NO && ((void)((((((Class *)nil).path0).path1).path2).path3), NO)), @#path0 "." #path1 "." #path2 "." #path3)
#define MZKVOKeyPath5(Class, path0, path1, path2, path3, path4) ((void)(NO && ((void)(((((((Class *)nil).path0).path1).path2).path3).path4), NO)), @#path0 "." #path1 "." #path2 "." #path3 "." #path4)
#define MZKVOKeyPath6(Class, path0, path1, path2, path3, path4, path5) ((void)(NO && ((void)((((((((Class *)nil).path0).path1).path2).path3).path4).path5), NO)), @#path0 "." #path1 "." #path2 "." #path3 "." #path4 "." #path5)
#define __mz_macro_concat_(A, B) A ## B
#define __mz_macro_head_(FIRST, ...) FIRST
@rickytan
Copy link
Author

rickytan commented Oct 27, 2017

Introduction

Automatically check keypath at compile time, with auto-completion.

Usage

NSString *keypath1 = MZKVOKeyPath(NSString, lowercaseString, pathComponents, count);

@interface Author : NSObject
@property (nonatomic) NSString *name;
@end
@interface Book : NSObject
@property (nonatomic) Author *author;
@end

NSString *keypath2 = MZKVOKeyPath(Book, author, name);

Example:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSLog(@"%@", MZKVOKeyPath(ViewController, view));
    NSLog(@"%@", MZKVOKeyPath(ViewController, view, frame));
    NSLog(@"%@", MZKVOKeyPath(ViewController, view, hidden));
    NSLog(@"%@", MZKVOKeyPath(ViewController, view, nextResponder));
    NSLog(@"%@", MZKVOKeyPath(ViewController, view, frame, origin));
    NSLog(@"%@", MZKVOKeyPath(ViewController, view, frame, size));
    NSLog(@"%@", MZKVOKeyPath(UIViewController, view, frame, size, height));
    NSLog(@"%@", MZKVOKeyPath(ViewController, navigationController, navigationBar, titleTextAttributes, description));
    NSLog(@"%@", MZKVOKeyPath(ViewController, navigationController, navigationBar, nextResponder, canBecomeFirstResponder));
}

2017-12-10 13:54:09.715758+0800 tmp[22042:4656991] view
2017-12-10 13:54:09.715933+0800 tmp[22042:4656991] view.frame
2017-12-10 13:54:09.716045+0800 tmp[22042:4656991] view.hidden
2017-12-10 13:54:09.716150+0800 tmp[22042:4656991] view.nextResponder
2017-12-10 13:54:09.716337+0800 tmp[22042:4656991] view.frame.origin
2017-12-10 13:54:09.716436+0800 tmp[22042:4656991] view.frame.size
2017-12-10 13:54:09.716526+0800 tmp[22042:4656991] view.frame.size.height
2017-12-10 13:54:09.716631+0800 tmp[22042:4656991] navigationController.navigationBar.titleTextAttributes.description
2017-12-10 13:54:09.716722+0800 tmp[22042:4656991] navigationController.navigationBar.nextResponder.canBecomeFirstResponder

Limitation

  1. Maximum 6 key-path supported, but you can add your own.

@iOSleep
Copy link

iOSleep commented Dec 2, 2017

使用[]的形式进行调用会导致使用getter方式的property变量获取不掉,kvo监听的根据的属性名,不是getter的方法名

#define MZKVOKeyPath0(Class) ((Class *)nil)
#define MZKVOKeyPath1(Class, path0) ((void)(NO && ((Class *)nil).path0), @#path0)
#define MZKVOKeyPath2(Class, path0, path1) ((void)(NO && ((Class *)nil).path0.path1), @#path0 "." #path1)
#define MZKVOKeyPath3(Class, path0, path1, path2) ((void)(NO && ((Class *)nil).path0.path1.path2), @#path0 "." #path1 "." #path2)
#define MZKVOKeyPath4(Class, path0, path1, path2, path3) ((void)(NO && ((Class *)nil).path0.path1.path2.path3), @#path0 "." #path1 "." #path2 "." #path3)
#define MZKVOKeyPath5(Class, path0, path1, path2, path3, path4) ((void)(NO && ((Class *)nil).path0.path1.path2.path3.path4), @#path0 "." #path1 "." #path2 "." #path3 "." #path4)
#define MZKVOKeyPath6(Class, path0, path1, path2, path3, path4, path5) ((void)(NO && ((Class *)nil).path0.path1.path2.path3.path4.path5), @#path0 "." #path1 "." #path2 "." #path3 "." #path4 "." #path5)

我进行了调整,您检查下,没有问题就变过来吧。

@rickytan
Copy link
Author

@iOSleep 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment