看了objc.io
中的《Behaviors in iOS Apps》(objccn上也有中文翻译版)后,终于如梦初醒了IB中的这个低调的Object
存在的意义:
再加上同样被轻视的Runtime Attributes:
有了这些,IB才算完整和强大。
最近看了一些文章,加上工程中也遇到的坑,矛头都指向了MVC(Massive View Controller):臃肿的ViewController所引发的不爽,objc.io
也是以一个《Lighter View Controllers》开篇。从把DataSource和Delegte从VC中分离,到把Model逻辑整个分离的MVVM
,VC一步步瘦身,再到这篇Behavior模式巧妙的组件式的分离了功能。但有没有想过,为啥往一个页面写点啥东西就一定要子类化一个VC呢,使用上面两个IB的功能,我们可以激进地实验一次0代码ViewController
首先必须说明Top Level Objects这个概念,根据apple文档:
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.)
所以,IB里面的Object
控件其实就是向Controller中添加Custom Top Level Object
,在storyboard中被摆在下面的位置:
事实上任何Object都可以添加,这里出现了一个LoginViewModel对象、一个菊花、一个Tap手势。
- init
消息- initWithCoder:
消息- awakeFromNib
消息- awakeFromNib
消息- initWithCoder:
消息- awakeFromNib
消息可见自定义的Object的创建时间是早于VC的,至于为什么- awakeFromNib
收到的晚于VC,是因为创建出来的Object需要被VC强引用
创建出来的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因为没有被强引用而随后被释放。
创建一个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