02.实战技术 父子控制器,常量Const,开发中常见问题

@(iOS Study)[实战技术]


目录

  • 02.实战技术 父子控制器,常量Const,开发中常见问题
  • 开发中常见问题/常量Const/父子控制器
  • 1.super,superClass,class关键字
    • super,superClass,class介绍
    • super关键字
  • 2.项目奇怪的bug
    • bug1. 已添加文件到项目中,却提示未定义
    • bug2. 重复添加文件到项目中
    • bug3. 导入.m文件
  • 3.const的使用
    • const与宏的区别
    • const的简单使用
    • const开发中使用场景
    • static和extern使用
    • static和const联合使用
    • extern和const联合使用
  • 4.父子控制器
    • 父子控制器简介
    • 父子控制器相关知识点
    • 父子控制器的简单使用
  • 5.开发中常见问题
    • UIScrollView的自动布局
    • UIScrollView自动布局步骤(重要)
    • size和center设置注意点
    • bounds和frame简介
    • bounds和frame介绍

开发中常见问题/常量Const/父子控制器


1.super,superClass,class关键字

super,superClass,class介绍

  • 关键字在方法中的意义
    • self: 方法调用者
    • class: 获取方法调用者的类对象
    • superclass:获取方法调用者的父类对象

super关键字

  • super关键字的介绍

    • super:不是一个指针,编译指示器(标识符)
    • super的本质:其实还是当前对象去调用,只不过让当前对象去调用父类方法
    • super不是父类对象,指的是父类方法
  • 注意:[super class]表示用self调用父类方法,[super superclass]表示用self调用父类的方法(获取父类方法)

  • super的案例说明

@implementation SubPerson

- (void)test
{
    // [super class]表示用self调用父类方法,[super superclass]表示用self调用父类的方法(获取父类方法)
    NSLog(@"%@ %@ %@ %@",[self class], [self superclass], [super class], [super superclass]);
    // 正确打印结果: SubPerson Person SubPerson Person √
    // 错误打印结果: SubPerson Person Person NSObject X
}

@end

2.项目奇怪的bug

bug1. 已添加文件到项目中,却提示未定义

  • 已添加文件到项目中,却提示未定义bug
02.实战技术 父子控制器,常量Const,开发中常见问题_第1张图片
1.bug1.已添加文件到项目中,却提示未定义.png
  • 导致该bug的原因
    • 在创建文件/从其他地方拖进工程的时候,没有勾选以下选项
      创建的时候未勾选该选项,导致没添加到编译列表
02.实战技术 父子控制器,常量Const,开发中常见问题_第2张图片
2.导致该bug的原因.png

从其他地方拖进工程的时候,没有勾选Targets项,导致没添加到编译列表

02.实战技术 父子控制器,常量Const,开发中常见问题_第3张图片
3.没有勾选Targets项.png
  • 解决方案: 在项目文件/Build Phases/Complie Sources将文件(.m文件)添加到编译列表
02.实战技术 父子控制器,常量Const,开发中常见问题_第4张图片
4.解决方案.png

bug2. 重复添加文件到项目中

  • 重复添加文件,提示错误
02.实战技术 父子控制器,常量Const,开发中常见问题_第5张图片
5.bug2. 重复添加文件到项目中.png
  • 解决方案: 找出重复文件,将文件移除.

bug3. 导入.m文件

  • 不小心导入了.m文件
02.实战技术 父子控制器,常量Const,开发中常见问题_第6张图片
6.bug3. 导入.m文件.png

3.const的使用

const与宏的区别

  • const与宏的区别

    • 1.编译时刻: const:编译时期 宏:预编译时期
    • 2.编译检测: const有编译检测,宏没有编译检测.如#define WXKey @"123"488 ,这样宏不会提示错误.
    • 3.宏可以替换方法和函数,const不行
    • 4.大量使用宏,容易造成预编译时期过长.
    • 有博客提到:大量使用宏,会导致内存暴增,有异议.
  • 苹果使用const为了迎合swift

  • '#'号是预编译指令

  • 预编译时期:项目一打开的时候,有个自动读条时期,这个时期就是在做预编译操作,如果定义了大量的宏,可能会导致自动读条的时间变长,是开发效率变低,苹果官方建议尽量少使用宏.

const的简单使用

  • const的作用

    • 1.用来修饰右边变量(基本变量,指针变量,对象等)
    • 2.只要被const修饰的变量,只读
  • const使用原则

    • 放在const右边被修饰的变量为只读.
  • const笔试题

    // 1.原因:const修饰的是p,所以p为只读;const在*之后,const没修饰*p,所以*p是变量.
    int * const p; // p:只读   *p:变量
    // 2.原因: const修饰的是*p1,所以*p1为只读;const没修饰p1,所以p1是变量.
    int const *p1; // p1:变量  *p1:只读 
    // 3.原因: const修饰*p2,所以*p2为只读;const没修饰p2,所以p2为变量
    const int *p2; // p2:变量  *p2:只读
    // 4.原因: 第一个const修饰*p3,所以*p3为只读;第二个const修饰p3,所以p3为只读
    int const * const p3; // p3:只读 *p3:只读
    // 5.原因: 第一个const修饰*p4,所以*p4为只读;第二个const修饰p4,所以p4为只读
    const int * const p4; // p4:只读  *p4:只读

const开发中使用场景

  • 1.当代码多处(不同方法中)共用同一个只读(无需修改)的字符串,比如多处(不同方法中)用到setValue:forKey:时都需要用到同一个key时,可以使用以下方法定义const字符串常量.
NSString * const key = @"key";

static和extern使用

  • static和extern的作用

    • static作用:

    1.描述局部变量,局部变量被static修饰,生命周期延长(整个app运行过程中都在),作用域不变.
    2.局部变量被static修饰,只会分配一次内存,程序一启动就会分配.
    3.修饰全局变量,全局变量被static修饰,生命周期不变,作用域会变,只能在当前文件下使用.

    • extern作用:声明一个全局变量,不能定义变量.

static和const联合使用

  • static和const联合使用场景
// 用于KVC的key时,可以static和const联合使用
static NSString * const ageKey = @"age";

extern和const联合使用

  • 开发规范:全局变量不能在自己的文件下定义,搞一个专门文件,去管理所有全局变量

  • extern和const联合使用使用场景(声明全局常量的时候)

    • 创建Const.h和Const.m文件用来存放全局常量
    • 在Const.h中使用extern和const声明 NSString * const nameKey;注意:不能在.h文件中赋值.
    • 在Const.m中定义NSString * const nameKey = @"name";
  • 开发中存放常量的文件中,常量可以按功能模块划分.

// Const.m
#import "Const.h"
/*********我的********/
NSString * const nameKey = @"nameKey";

/*********发现********/
NSString * const discoveryKey = @"discoveryKey";


// Const.h
/*********我的********/
extern NSString * const nameKey;

/*********发现********/
extern NSString * const discoveryKey;
  • 模仿系统实现extern
// Const.h
// WXKIT_EXTERN相当于extern
#define WXKIT_EXTERN    extern __attribute__((visibility ("default")))
WXKIT_EXTERN NSString * const nameKey;

4.父子控制器

父子控制器简介

父子控制器应用场景-多控制器管理者:导航控制器,tabBarController,内部原理就是采取父子控制器去管理.
任何控制器都可以成为父控制器
父子控制器设计原则: 把A控制器的view添加到B控制器的view上,那么A控制器必须要成为B控制器子控制器.

父子控制器相关知识点

  • 调试时已知问题可以先列出来,以防忘记,遗漏处理已经存在的问题
/*
    问题:
    1.每次都创建控制器
    2.每次都添加view
    3.控制器被销毁
 */
  • 调用dismissViewControllerAnimated:completion:方法时,dismiss实现内部实现

    • 1.判断下当前控制器是不是modal,如果是,就会dismiss
    • 2.如果不是,判断下父控制器是不是modal,如果是,就会dismiss
    • 3.继续判断,直到没有
  • 只有导航控制器子控制器才能获取self.navigationController.

    • 使用self.navigationController时,会判断自己是不是导航控制器子控制器,如果不是,判断自己的父控制器是不是导航控制器子控制器,继续判断,直到没有父控制器.

父子控制器的简单使用

  • 父子控制的简单使用
#import "ViewController.h"
#import "SocietyViewController.h"
#import "TopLineViewController.h"
#import "HotViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *topView;
@property (nonatomic, weak) UIButton *selectedBtn;
@property (weak, nonatomic) IBOutlet UIView *contentView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.添加所有子控制器
    [self setUpAllChildViewController];
    
    // 2.通过控制器标题,设置按钮显示的文字
    [self setupTopButton];
    
}

// ----------------------------------------------------------------------------------
// 初始化
#pragma mark - 初始化

/** 初始化顶部button */
- (void)setupTopButton
{
    NSInteger count = self.topView.subviews.count;
    for (NSInteger i = 0; i < count; i++) {
        // 取出button
        UIButton *btn = self.topView.subviews[i];
        
        // 取出子控制器
        UIViewController *vc1 = self.childViewControllers[i];
        
        // 设置btn显示的文字
        [btn setTitle:vc1.title forState:UIControlStateNormal];
        
        // 设置btn选中状态的文字颜色,需在storyboard中将按钮Type修改为Custom,否则系统默认会把按钮渲染成蓝色
        [btn setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
        
        // 设置默认选中
        if (i == 0) {
            [self btnClick:btn];
        }
    }
}

/** 添加所有子控制器 */
- (void)setUpAllChildViewController
{
    // 1.添加社会控制器
    SocietyViewController *vc1 = [[SocietyViewController alloc] init];
    vc1.title = @"社会";
    [self addChildViewController:vc1];
    // 2.添加头条控制器
    TopLineViewController *vc2 = [[TopLineViewController alloc] init];
    vc2.title = @"头条";
    [self addChildViewController:vc2];
    // 3.添加热点控制器
    HotViewController *vc3 = [[HotViewController alloc] init];
    vc3.title = @"热点";
    [self addChildViewController:vc3];
    
}

// ----------------------------------------------------------------------------------
// 添加顶部三个按钮的监听
#pragma mark - 事件监听

/** 监听顶部三个button的点击 */
- (IBAction)btnClick:(UIButton *)btn {
    // 1.切换选中状态
    [self changeSelectedBtn:btn];
    
    // 2.设置子控制器view的属性
    // 2.1 根据button的tag值,取出对应的控制器
    UIViewController *vc = self.childViewControllers[btn.tag];
    // 2.2 设置控制器view的背景颜色和button的背景颜色一样
    vc.view.backgroundColor = btn.backgroundColor;
    
    // 2.3 设置子控制器的view的frame
    vc.view.frame = self.contentView.bounds;
    
    // 3.将子控制器的view添加到contentView
    // 3.1 先移除当前控制器view的所有子控件
    [self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    // 3.2 添加子控件的view到当前控制器的contentView上
    [self.contentView addSubview:vc.view];
}

/** 切换按钮选中状态 */
- (void)changeSelectedBtn:(UIButton *)btn
{
    self.selectedBtn.selected = NO;
    self.selectedBtn = btn;
    self.selectedBtn.selected = YES;
}

@end
  • NSArray类的makeObjectsPerformSelector方法的使用
// 数组调用该方法时,表示数组里每个元素会执行test方法
[arr makeObjectsPerformSelector:@selector(test)];
  • UIButon的Type如果是System,系统默认会把按钮渲染成蓝色

5.开发中常见问题

UIScrollView的自动布局

  • UIScrollView的自动布局思考

    • 1.确定好UIScrollView的滚动范围contentSize
    • 2.怎么确定?搞一个view添加到UIScrollView上,这个view用来确定UIScrollView的滚动范围
    • 3.通过设置这个view约束.来告诉UIScrollView滚动范围
    • 4.如何计算contentSize呢?要计算contentSize必须清楚每个subviews的frame,而subviews的frame居然又要依赖scrollView
  • UIScrollView的自动布局原则

    • 1.UIScrollView的contentSize依赖于subviews.
    • 2.UIScrollView的contentSize必须根据其子控件如UIView的4条边来确定.
    • 3.注意:如果只设置UIScrollView子控件的上下左右间距为0,是不能确定contentSize的,必须设置其子控件UIView的尺寸才能确定4条边.

UIScrollView自动布局步骤(重要)

  • UIScrollView自动布局实现步骤
    • 1.给UIScrollView添加一个UIView,作为UIScrollView的contentView.之后要添加其他子控件可以往contentView上添加,这样UIScrollView就只有contentView一个子控件,便于布局
    • 2.设置contentView的尺寸(宽,高),因为UIScrollView的contentSize是由它的子控件的frame决定的,contentView是UIScrollView的子控件,所以必须设置contentView的size才能确定contentSize.
    • 3.设置contentView的约束,比如设置约束上下左右间距都为0.

size和center设置注意点

  • size和center如果一起使用时,如果设置的是frame的size,必须先设置尺寸frame.size,再设置center.如果设置的是bounds的size,就无需考虑先后顺序.因为center就是根据bounds中的size决定的.

  • 设置frame的size属性,先设置尺寸frame.size,再设置center.

- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建redView
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    // 先设置尺寸
    CGRect frame = redView.frame;
    frame.size = CGSizeMake(200, 200);
    redView.frame = frame;
    
    // 再设置center
    redView.center = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
}
  • 设置bounds的size属性,无需先后顺序.
- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建redView
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    // 设置center
    redView.center = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
    
    // 设置尺寸
    CGRect bounds = redView.bounds;
    bounds.size = CGSizeMake(200, 200);
    redView.bounds = bounds;
}

bounds和frame简介

bounds和frame介绍

  • frame和bounds的区别

    • frame:以父控件的左上角为原点
    • bounds:以自己左上角为原点,x,y = 0,(X)
  • frame和bounds描述一个矩形

    • frame:可视范围
    • bounds:描述 可视范围 在 内容范围 区域
  • bounds本质:修改内容原点位置

    • 子控件是相对于控件内容范围
    • frame:相对于父控件,位置永远不变
    • frame:相对于内容,就会改变

你可能感兴趣的:(02.实战技术 父子控制器,常量Const,开发中常见问题)