看了objc.io
中的《Behaviors in iOS Apps》(objccn上也有中文翻译版)后,终于如梦初醒了IB中的这个低调的Object
存在的意义:html
再加上一样被轻视的Runtime Attributes:ios
有了这些,IB才算完整和强大。
最近看了一些文章,加上工程中也遇到的坑,矛头都指向了MVC(Massive View Controller):臃肿的ViewController所引起的不爽,objc.io
也是以一个《Lighter View Controllers》开篇。从把DataSource和Delegte从VC中分离,到把Model逻辑整个分离的MVVM
,VC一步步瘦身,再到这篇Behavior模式巧妙的组件式的分离了功能。但有没有想过,为啥往一个页面写点啥东西就必定要子类化一个VC呢,使用上面两个IB的功能,咱们能够激进地实验一次0代码ViewController
git
首先必须说明Top Level Objects这个概念,根据apple文档:github
The top-level objects are the subset of these objects that do not have a parent object. The top-level objects typically include only the windows, menubars, and custom controller objects that you add to the nib file. (Objects such as File’s Owner, First Responder, and Application are placeholder objects and not considered top-level objects.)windows
因此,IB里面的Object
控件其实就是向Controller中添加Custom Top Level Object
,在storyboard中被摆在下面的位置:数组
事实上任何Object均可以添加,这里出现了一个LoginViewModel对象、一个菊花、一个Tap手势。app
- init
消息- initWithCoder:
消息- awakeFromNib
消息- awakeFromNib
消息- initWithCoder:
消息- awakeFromNib
消息可见自定义的Object的建立时间是早于VC的,至于为何- awakeFromNib
收到的晚于VC,是由于建立出来的Object须要被VC强引用ide
建立出来的Object必须保证不被释放,这个强引用由VC实现,虽然说没有显示的API,但从UIViewController.h
中能够看到马脚:工具
1 2 3 4 5 6 7 |
@interface UIViewController : UIResponder {
@package
// ...
NSDictionary *_externalObjectsTableForViewLoading;
NSArray *_topLevelObjectsToKeepAliveFromStoryboard;
// ... } |
经过KVC能够很轻松的取出来:布局
1 2 |
NSArray *objs = [vc valueForKey:@"_topLevelObjectsToKeepAliveFromStoryboard"]; NSDictionary *dict = [vc valueForKey:@"_externalObjectsTableForViewLoading"]; |
这些objects就是被这个数组强引用的,感兴趣的能够打印下看看结果。
注:只在storyboard下生效,在xib下,被建立的Object由于没有被强引用而随后被释放。
storyboard中拉出个VC,随便摆摆:
建立一个ViewModel类(也就是Behavior类),里面写须要的IBOutlet和IBAction
1 2 3 4 5 6 7 |
@interface XXLoginViewModel : NSObject @property (nonatomic, weak) IBOutlet UIViewController *ownerViewController; @property (nonatomic, weak) IBOutlet UITextField *usernameTextField; @property (nonatomic, weak) IBOutlet UITextField *passwordTextField; @property (nonatomic, weak) IBOutlet UIActivityIndicatorView *spinner; - (IBAction)loginAction:(id)sender; @end |
storyboard中拖出来一个Object到左边,设置类为这个XXLoginViewModel
, 将全部IBOutlet和IBAction链接好
这样,就能够在自定义Object中随心所欲了,须要什么就从storyboard里面Outlet出来就行了,好比点击以后的跳转:
1 2 3 4 |
- (IBAction)loginAction:(id)sender { [self.ownerViewController performSegueWithIdentifier:@"LoginSegue" sender:nil]; } |
具体的代码不show了,Demo的效果以下:
固然,TableView的delegate和data source也都是能够托管到自定义Object中,同时,Object之间也能够有Outlet关系哦,剩下的就纯靠想象力了。
这个简单的demo从->这里下载<-
模板类
,继承+重载无可非议(但像UITableView这种被设计成配置类
的类,咱们更应该去配置它,而非继承它,更别说NSArray,NSString这种类簇的工具类了)https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html
http://www.objc.io/issue-13/behaviors.html
原创文章,转载请注明源地址,blog.sunnyxx.com