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
1.bug1.已添加文件到項目中,卻提示未定義.png
  • 導致該bug的原因
    • 在創建文件/從其他地方拖進工程的時候,沒有勾選以下選項
      創建的時候未勾選該選項,導致沒添加到編譯列表
2.導致該bug的原因.png

從其他地方拖進工程的時候,沒有勾選Targets項,導致沒添加到編譯列表

3.沒有勾選Targets項.png
  • 解決方案: 在項目文件/Build Phases/Complie Sources將文件(.m文件)添加到編譯列表
4.解決方案.png

bug2. 重復添加文件到項目中

  • 重復添加文件,提示錯誤
5.bug2. 重復添加文件到項目中.png
  • 解決方案: 找出重復文件,將文件移除.

bug3. 導入.m文件

  • 不小心導入了.m文件
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:相對于內容,就會改變
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容