使用xib开发界面
使用xib开发界面
纯代码写界面有时候会降低开发效率,对于一些通用简单的界面,例如程序设置界面,可以使用xib进行开发。
一、关于xib
1. xib和nib
xib文件可以被Xcode编译成nib文件,xib文件本质上是一个xml文件,而nib文件就是编译后的二进制文件,该文件将视图等控件对象封装了起来,而在程序运行起来后,这些对象会被激活。
xib文件本质上是一个xml文件,可以用vim或cat命令查看,例如:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | $ cat ~/Desktop/JLN-1_xib/JLN-1_xib/GrayViewController.xib<!--?xml version="1.0"encoding="UTF-8"standalone="no"?--><document type="com.apple.interfacebuilder3.cocoatouch.xib"version="3.0"toolsversion="6254"systemversion="14b25"targetruntime="ios.cocoatouch"propertyaccesscontrol="none"useautolayout="yes"usetraitcollections="yes">    <dependencies>        <plugin identifier="com.apple.interfacebuilder.ibcocoatouchplugin"version="6247">    </plugin identifier="com.apple.interfacebuilder.ibcocoatouchplugin"version="6247"></dependencies>    <objects>        <placeholder placeholderidentifier="ibfilesowner"id="-1"userlabel="file's owner"customclass="grayviewcontroller">            <connections>                <outlet property="actionbutton"destination="edu-ds-gip"id="qav-o1-ta6">                <outlet property="titlelabel"destination="ycj-fh-rdg"id="xj4-yo-zzp">                <outlet property="view"destination="i5m-pr-fkt"id="sfx-zr-jgt">            </outlet property="view"destination="i5m-pr-fkt"id="sfx-zr-jgt"></outlet property="titlelabel"destination="ycj-fh-rdg"id="xj4-yo-zzp"></outlet property="actionbutton"destination="edu-ds-gip"id="qav-o1-ta6"></connections>                <placeholder placeholderidentifier="ibfirstresponder"id="-2"customclass="uiresponder">        <view clearscontextbeforedrawing="no"contentmode="scaletofill"id="i5m-pr-fkt">            <rect key="frame"x="0.0"y="0.0"width="300"height="44">            <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="edu-ds-gip">                    <rect key="frame"x="246"y="7"width="46"height="30">                    <state key="normal"title="button">                        <color key="titleshadowcolor"white="0.5"alpha="1"colorspace="calibratedwhite">                                        <connections>                        <action selector="action:"destination="-1"eventtype="touchupinside"id="svp-jp-gk9">                    </action selector="action:"destination="-1"eventtype="touchupinside"id="svp-jp-gk9"></connections>                                <label opaque="no"userinteractionenabled="no"contentmode="left"horizontalhuggingpriority="251"verticalhuggingpriority="251"fixedframe="yes"text="label"linebreakmode="tailtruncation"baselineadjustment="alignbaselines"adjustsfontsizetofit="no"translatesautoresizingmaskintoconstraints="no"id="ycj-fh-rdg">                    <rect key="frame"x="129"y="11"width="42"height="21">                    <fontdescription key="fontdescription"type="system"pointsize="17">                    <color key="textcolor"cocoatouchsystemcolor="darktextcolor">                    <nil key="highlightedcolor">                            </nil key="highlightedcolor"></color key="textcolor"cocoatouchsystemcolor="darktextcolor"></fontdescription key="fontdescription"type="system"pointsize="17"></rect key="frame"x="129"y="11"width="42"height="21"></label opaque="no"userinteractionenabled="no"contentmode="left"horizontalhuggingpriority="251"verticalhuggingpriority="251"fixedframe="yes"text="label"linebreakmode="tailtruncation"baselineadjustment="alignbaselines"adjustsfontsizetofit="no"translatesautoresizingmaskintoconstraints="no"id="ycj-fh-rdg"></color key="titleshadowcolor"white="0.5"alpha="1"colorspace="calibratedwhite"></state key="normal"title="button"></rect key="frame"x="246"y="7"width="46"height="30"></button opaque="no"contentmode="scaletofill"fixedframe="yes"contenthorizontalalignment="center"contentverticalalignment="center"buttontype="roundedrect"linebreakmode="middletruncation"translatesautoresizingmaskintoconstraints="no"id="edu-ds-gip"></subviews>            <color key="backgroundcolor"white="1"alpha="1"colorspace="custom"customcolorspace="calibratedwhite">            <nil key="simulatedstatusbarmetrics">            <nil key="simulatedtopbarmetrics">            <nil key="simulatedbottombarmetrics">            <freeformsimulatedsizemetrics key="simulateddestinationmetrics">            <point key="canvaslocation"x="382"y="285">            </point key="canvaslocation"x="382"y="285"></freeformsimulatedsizemetrics key="simulateddestinationmetrics"></nil key="simulatedbottombarmetrics"></nil key="simulatedtopbarmetrics"></nil key="simulatedstatusbarmetrics"></color key="backgroundcolor"white="1"alpha="1"colorspace="custom"customcolorspace="calibratedwhite"></autoresizingmask key="autoresizingmask"widthsizable="yes"heightsizable="yes"></rect key="frame"x="0.0"y="0.0"width="300"height="44"></view clearscontextbeforedrawing="no"contentmode="scaletofill"id="i5m-pr-fkt"></placeholder placeholderidentifier="ibfirstresponder"id="-2"customclass="uiresponder"></placeholder placeholderidentifier="ibfilesowner"id="-1"userlabel="file's owner"customclass="grayviewcontroller"></objects></document type="com.apple.interfacebuilder3.cocoatouch.xib"version="3.0"toolsversion="6254"systemversion="14b25"targetruntime="ios.cocoatouch"propertyaccesscontrol="none"useautolayout="yes"usetraitcollections="yes"> | 
nib文件可以在程序的Build目录下找到。
2. xib文件的若干属性
xib文件有以下几个重要的属性:
- xib文件名 
- File’s Owner 
- xib文件中的视图的Class 
- xib文件中的视图的Outlet指向 
从哪里加载xib,加载xib中的什么视图,都可以根据这几个属性得出。
二、Demo实践
1. 加载xib中File’s Owner为nil的视图
BlueView.xib

MainViewController.m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ...@property (strong, nonatomic) UIView *blueView;...- (void)loadBlueViewFromXIB {    // BlueView.xib的File's Owner为nil    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"BlueView"owner:nil options:nil];    self.blueView = views[0];        // 从xib加载进来的View大小是确定的,但是该视图在父视图中的位置是不确定的    // 此外,视图中的子视图也是原封不动地Load进来的    CGRect rect = _blueView.frame;    rect.origin.x += 37.5f;    rect.origin.y += 80.0f;    _blueView.frame = rect;    [self.view addSubview:_blueView];} | 
运行结果:

结论:
- File’s Owner为nil的xib文件中的视图属于通用视图,在工程中可以复用 
- 从xib加载进来的View大小是确定的,但是该视图在父视图中的位置是不确定的,因此需要开发者自行指定 
- 视图中的所有子视图会被原封不动地Load进来 
2. 加载xib中File’s Owner为self的视图

MainViewController.m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ...@property (weak, nonatomic) IBOutlet UIView *greenView;...- (void)loadGreenViewFromXIB {    // GreenView.xib的File's Owner设为self,并建立了一个从该xib的View到self的IBOutlet greenView    [[NSBundle mainBundle] loadNibNamed:@"GreenView"owner:self options:nil];        // 只要self主动调用Load XIB的方法,self持有的IBOutlet指向的视图就会被初始化    // 这里不需要通过views[0]的方式存取视图    CGRect rect = _greenView.frame;    rect.origin.x = _blueView.frame.origin.x;    rect.origin.y = _blueView.frame.origin.y + 80.0f;    _greenView.frame = rect;    [self.view addSubview:_greenView];} | 
运行结果:

结论:
- File’s Owner不为nil的xib文件中的视图属于专用视图,在工程中不应该被复用 
- 只要self主动调用loadNibNamed:owner:options:方法,self持有的IBOutlet指向的视图就会被初始化 
- 存取xib中的视图不用views[0]的方式,而是通过IBOutlet类型的property进行存取 
3. 加载xib中File’s Owner为特定类的视图
RedView.xib

RedViewOwner.h
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @interface RedViewOwner : NSObject@property (strong, nonatomic) IBOutlet UIView *redView;@endMainViewController.m...@property (strong, nonatomic) RedViewOwner *redViewOwner;...- (void)loadRedViewFromXIB {    // RedView.xib的File's Owner是RedViewOwner类的实例,并建立了一个从该xib的View到RedViewOwner实例的IBOutlet    // 只要通过_redViewOwner主动调用Load XIB的方法,该IBOutlet指向的视图就会被初始化    self.redViewOwner = [RedViewOwner new];    [[NSBundle mainBundle] loadNibNamed:@"RedView"owner:_redViewOwner options:nil];        UIView *redView = _redViewOwner.redView;    CGRect rect = redView.frame;    rect.origin.x = _greenView.frame.origin.x;    rect.origin.y = _greenView.frame.origin.y + 80.0f;    redView.frame = rect;    [self.view addSubview:redView];} | 
运行结果:

结论:
- File’s Owner类可以封装视图中的各种逻辑,而不仅仅是提供视图内容 
- 只要通过File’s Owner类主动调用loadNibNamed:owner:options:方法,该IBOutlet指向的视图就会被初始化 
4. 加载xib中文件名和视图类名一致的视图(File’s Owner为nil)
YellowView.xib

YellowView.h/m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @interface YellowView : UIView+ (instancetype)viewFromNIB;@property (weak, nonatomic) IBOutlet UILabel *titleLabel;@end@implementation YellowView// Convenience Method+ (instancetype)viewFromNIB {    // 加载xib中的视图,其中xib文件名和本类类名必须一致    // 这个xib文件的File's Owner必须为空    // 这个xib文件必须只拥有一个视图,并且该视图的class为本类    NSArray *views = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil];    returnviews[0];}- (void)awakeFromNib {    // 视图内容布局    self.backgroundColor = [UIColor yellowColor];    self.titleLabel.textColor = [UIColor whiteColor];}@endMainViewController.m...@property (strong, nonatomic) YellowView *yellowView;...- (void)loadYellowViewFromXIB {    // 说明见YellowView.m的viewFromNIB方法    self.yellowView = [YellowView viewFromNIB];        CGRect rect = _yellowView.frame;    UIView *redView = _redViewOwner.redView;    rect.origin.x = redView.frame.origin.x;    rect.origin.y = redView.frame.origin.y + 80.0f;    _yellowView.frame = rect;    [self.view addSubview:_yellowView];} | 
运行结果:

结论:
这里的viewFromNib方法只是对loadNibNamed:owner:options:方法的一个简单封装,要求的条件包括: - xib文件名和本类类名必须一致 - 这个xib文件的File’s Owner必须为空 - 这个xib文件必须只拥有一个视图,并且该视图的class为本类
5. 通过UIViewController的initWithNibName:bundle:方法加载xib文件中的视图
BlackView.xib

如果BlackViewController类希望self.view就是xib文件中的View,可以在Connections页中建立view -> File’s Owner的Outlet,如下:

BlackViewController.h/m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @interface BlackViewController : UIViewController@property (weak, nonatomic) IBOutlet UILabel *titleLabel;// Convenience Method+ (instancetype)viewControllerFromNIB;@end@implementation BlackViewController- (void)viewDidLoad {    [superviewDidLoad];        self.view.backgroundColor = [UIColor blackColor];    self.titleLabel.textColor = [UIColor whiteColor];}+ (instancetype)viewControllerFromNIB {    return[[BlackViewController alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];}- (void)didReceiveMemoryWarning {    [superdidReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}@end | 
MainViewController.m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ...@property (strong, nonatomic) BlackViewController *blackViewController;...- (void)loadBlackViewFromXIB {    self.blackViewController = [[BlackViewController alloc] initWithNibName:@"BlackViewController"bundle:[NSBundle mainBundle]];        // 或使用Conveniece Method,但要求xib文件名和View Controller类名一致    // self.blackViewController = [BlackViewController viewControllerFromNIB];        UIView *blackView = _blackViewController.view;    CGRect rect = blackView.frame;    rect.origin.x = _yellowView.frame.origin.x;    rect.origin.y = _yellowView.frame.origin.y + 80.0f;    blackView.frame = rect;    [self.view addSubview:blackView];} | 
运行结果:

结论:
- 将xib的File’s Owner设成一个UIViewController子类,可以将这个xib文件的视图展示和外部响应事件(例如点击一个按钮触发的点击事件,该视图的手势事件等)全部封装在一个View Controller中,如果把按钮的点击事件封装在一个UIView类中,貌似破坏了MVC模式,因此最好将xib的File’s Owner设成一个UIViewController子类,该类可以通过addChildViewController方法将其添加到现有的View Controller上。如果只是希望加载视图,可以通过viewcontroller.view存取。 
如果希望ViewControllerA加载并响应aXIBView中的按钮点击事件,这时必须建立一个aXIBView到ViewControllerA的IBAction,如果ViewControllerA需要拥有多个这样的XIB,那么ViewControllerA会变得非常的庞大,此时可以通过为每一个XIB设置一个ViewController,再让ViewControllerA加载这些Child View Controllers,这样可以将这些事件的响应职责和视图的描绘工作分派给专门的Child View Controller,在减小ViewControllerA体积的同时,也可以提高各个xib的可复用性。
- 这里的viewControllerFromNIB方法其实就是initWithNibName:bundle:方法的一个简单封装,要求:xib的File’s Owner设为本类。 
6. 通过UIViewController+NIB加载xib文件中的View Controller类和其视图
GrayView.xib

UIViewController+NIB.h/m
| 1 2 3 4 5 6 7 8 9 10 11 12 | @interface UIViewController (NIB)// 要求xib文件名和View Controller类名一致+ (instancetype)loadFromNib;@end@implementation UIViewController (NIB)+ (instancetype)loadFromNib {    // [self class]会由调用的类决定    Class controllerClass = [self class];    NSLog(@"class = %@", controllerClass);    return[[controllerClass alloc] initWithNibName:NSStringFromClass(controllerClass) bundle:[NSBundle mainBundle]];}@end | 
GrayViewController.h/m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @interface GrayViewController : UIViewController@property (weak, nonatomic) IBOutlet UILabel *titleLabel;@property (weak, nonatomic) IBOutlet UIButton *actionButton;@end@implementation GrayViewController- (void)viewDidLoad {    [superviewDidLoad];        self.view.backgroundColor = [UIColor grayColor];        self.titleLabel.text = @"Gray View";    self.titleLabel.textColor = [UIColor whiteColor];    self.titleLabel.textAlignment = NSTextAlignmentCenter;    self.titleLabel.font = [UIFont systemFontOfSize:8.5f];        [self.actionButton setTitle:@"action"forState:UIControlStateNormal];    [self.actionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];}- (void)didReceiveMemoryWarning {    [superdidReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}// 推荐从XIB文件中加载View Controller的方法,这种方法可以将XIB文件中的视图和其按钮响应事件全部封装在GrayViewController// 如果GrayViewController的按钮响应事件由MainViewController作出响应,那么二者的耦合度就过高// 建议:// 单纯的通用View展示,使用从xib文件加载视图的方法,File's Owner设为nil// 特定拥有者的View展示,从xib文件加载视图时,File's Owner设为拥有者// 如果视图中有按钮响应事件,或其它可以和用户交互的事件,建议采用从XIB文件中加载View Controller的方法,这样可以封装UI展示和交互事件- (IBAction)action:(id)sender {    NSLog(@"action");}@end | 
MainViewController.m
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ...@property (strong, nonatomic) GrayViewController *grayViewController;...- (void)loadGrayViewFromXIB {    self.grayViewController = [GrayViewController loadFromNib];        UIView *grayView = _grayViewController.view;    UIView *blackView = _blackViewController.view;    CGRect rect = grayView.frame;    rect.origin.x = blackView.frame.origin.x;    rect.origin.y = blackView.frame.origin.y + 80.0f;    grayView.frame = rect;    [self.view addSubview:grayView];} | 
运行结果:

结论:
这里我专门写了一个UIViewController+NIB的category,只需要调用loadFromNib类方法就可以加载xib中的视图。要求: - xib文件的File’s Owner必须设置为对应的View Controller类。
三、总结
在写界面时同时混用xib和代码可以提高效率,而对xib的使用主要体现在其专用性和通用性上。
- 对于一些专门的界面,例如App中的设置界面,纯代码写难免会浪费时间,此时可以通过xib文件的拖控件方法来定制。这个xib是专用于某一个界面的,目的是提高效率。 
- 对于一些通用的控件甚至界面,例如一个很漂亮但实现起来非常复杂的按钮,此时可以通过load xib文件中的视图来快速添加。这个xib对于所有视图是共用的,目的是提高可复用性。 
对于通用的xib:
- 如果xib只是单纯的界面展示,那么File’s Owner可以随意。 
- 如果xib中包含了按钮、手势等用户输入事件,那么File’s Owner最好设置为UIViewController类的子类。 
四、自问自答
以前使用xib时一直都有点疑问,xib中可以有多个视图控件,但是从xib中load出来的是一个数组,那么怎么确定哪个对象对应的是哪个控件呢?
可以实践一下:
PurpleView.xib

随便在xib文件中加了几个视图。
接下来将其load出来看看:
MainViewController.m
| 1 2 3 4 5 6 7 8 9 | - (void)logViewsFromXIB {    NSLog(@"%s begin", __func__);    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"PurpleView"owner:nil options:nil];    for(int i = 0; i < views.count; i++) {        id obj = views[i];        NSLog(@"%d : %@", i, [obj class]);    }    NSLog(@"%s end", __func__);} | 
控制台输出如下:
| 1 2 3 4 5 6 | 2015-01-09 15:03:06.629 JLN-1_xib[3139:121677] -[MainViewController logViewsFromXIB] begin2015-01-09 15:03:06.635 JLN-1_xib[3139:121677] 0 : UIView2015-01-09 15:03:06.635 JLN-1_xib[3139:121677] 1 : UIButton2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] 2 : UITableView2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] 3 : UILabel2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] -[MainViewController logViewsFromXIB] end | 
结论:
从xib中load出来的views数组中视图对象的排列顺序和xib scene中的对象排列顺序一致(其实就是xml文件中元素的排序而已)。如下:

可以将其打乱并重新运行程序查看结果。
使用xib开发界面的更多相关文章
- Unity iOS混合开发界面切换思路
		Unity iOS混合开发界面切换思路 最近有很多博友QQ 私信 或则 留言联系我,请教iOS和Unity界面之前相互切换的问题,源代码就不私下发你们了,界面跳转功能的代码我直接贴到下面好了,顺带说i ... 
- IOS xib生成界面和代码生成界面两种方式混合
		应用程序代理类 WKAppDelegate.m // // WKAppDelegate.m // HelloWorld // // Created by easy5 on 13-9-18. // Co ... 
- Lakeshore 中文开发界面,示例项目,飞机大战 等 Lakeshore Chinese development interface, sample project, aircraft war, etc
		Lakeshore 中文开发界面,示例项目,飞机大战 等 Lakeshore Chinese development interface, sample project, aircraft war, ... 
- IOS 使用Interface Builder开发界面入门与技巧
		引言: 通过Interface Builder(简称IB)来制作界面一直是iOS开发界饱受争议的方式.主要争议的话题是不太适合团队协作开发,再就是对IB的使用比较生疏,觉得IB只能完成一些很简单的功能 ... 
- WinForm开发-界面控件到实体,实体到界面控件自动绑定
		在WinForm开发中,我们是不是为绑定界面控件的数据而每个控件每个控件的赋值?在保存修改时是不是也是每个控件每个控件的赋值到实体中?字段一多,那简直就是噩梦.有没有像Web中那样方便的方法直接就自动 ... 
- 第二十三篇:在SOUI中使用LUA脚本开发界面
		像写网页一样做客户端界面可能是很多客户端开发的理想. 做好一个可以实现和用户交互的动态网页应该包含两个部分:使用html做网页的布局,使用脚本如vbscript,javascript做用户交互的逻辑. ... 
- 树莓派入门教程——使用Qt开发界面程序
		前言 Qt是一个1991年由奇趣科技开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器.Qt是面向对象的框架,使用特 ... 
- java springmvc +spring+ mybaits  模块化开发框架 HTML5+css3.0+bootstrap响应式开发界面
		需要源码,请加QQ:858-048-581 系统模块 1. 权限管理:点开二级菜单进入三级菜单显示 角色(基础权限)和按钮权限 角色(基础权限): 分角色组和角色,独立分配菜单权限和增 ... 
- Swift下使用Xib设计界面
		虽然Swift可以纯代码设计界面,不过不利用现有的可视化工具有时候有点效率低.下面是使用xib设计方法,部分代码来自网上. (1)新建View 2.新建View class 3.DemoView.sw ... 
随机推荐
- 一文解析总结Java虚拟机内存区域模型
			最近抽空看了一点<深入理解Java虚拟机>,本篇文章主要来总结一下Java虚拟机内存的各个区域,以及这些区域的作用.服务对象以及其中可能产生的问题,作为大家的面试宝典. 首先我们来看一下J ... 
- physics(2018.10.27)
			这道题可以推出\(O(1)\)的算法,但是实际上暴力模拟就可以过了. 代码(暴力模拟): #include<cstdio> #include<algorithm> #inclu ... 
- 买票案例 1.synchronize关键字 2.lock锁
- AngularJS - 入门小Demo
			AngularJS四大特效 MVC模式.模块化设计.自动化双向数据绑定.依赖注入 如果了解了后端开发知识,想必对这些词汇不会陌生,AngularJS融合了后端开发的一些思想,虽然身为前端框架,但与jQ ... 
- JAVA 操作远程mysql数据库实现单表增删改查操作
			package MysqlTest; import java.sql.DriverManager; import java.sql.ResultSet; import com.mysql.jdbc.C ... 
- flask_之参数传递
			参数篇 request接收数据 request对象 method:当前请求方法(POST,GET等) url:当前链接地址 path:当前链接的路径 environ:潜在的WSGI环境 headers ... 
- laravel之null替换空字符串中间件
			在laravel写接口的时候免不了数据库中保存null,可用诸如设置ORM的访问器或以下方法处理 $goods->name?$goods->name:''; 其实可以利用路由中间件,在需要 ... 
- 使用Hexo 搭建自己的博客
			使用Hexo 搭建自己的博客 最近一直都想着如何去搭建属于自己的博客,有空的时候就写写文章什么的. 本人对该博客系统的要求是: 博文的编写要采用现在流行的MarkDown来进行编写. 本人还不想去注册 ... 
- python入门之实例-商品选择
			需求: 显示一系列商品,根据序号选择商品 li = ["手机","电脑","电视"] #函数enumerate在for循环遍历的时候,会默认 ... 
- mysql写存储过程并定时调用
			设置一个定时任务:运行以下SQL -- 创建一个表test:字段endtime CREATE TABLE test (endtime DATETIME); -- 创建函数 :向test插入endt ... 
