天天看點

黑馬OC聽課筆記(第一天)

視訊連結:iOS開發基礎班+就業班(100天完整版)之基礎班2:Objective-C學習(10天)

目錄

寫在前面

本OC教程學習路線圖

Objective-C概述

OC介紹及發展背景

OC對比C語言的差異化

OC-Hello World

OC中的資料類型

運算符、控制語句、關鍵字

面向對象

類和對象

對象

類和對象的關系

設計一個類

定義并使用類

方法調用

其他注意

寫在前面

本教程需要掌握一定的C語言知識,如果沒有C基礎,請點選這裡,或者先學習我的偷學C語言系列部落格。

本OC教程學習路線圖

黑馬OC聽課筆記(第一天)

Objective-C概述

OC介紹及發展背景

Objective-C簡稱Obj-C或者OC,關于OC的更多内容可以參見這篇博文:Objective-C

OC對比C語言的差異化

<1>在C的基礎上增加了面向對象的文法;

<2>将C中複雜繁瑣的内容進行了很好地封裝,更便于開發人員編碼;

<3>OC完全相容C語言,但在C中不能編寫OC新增的文法。

OC-Hello World

我們在MAC上開發OC程式使用Xcode,打開Xcode,點選Create a new Xcode project選項,

黑馬OC聽課筆記(第一天)

出現如下界面,依次選擇macOS,選擇Command Line Tool:

黑馬OC聽課筆記(第一天)

在如下界面輸入相應資訊,然後點Next:

黑馬OC聽課筆記(第一天)

語言選擇Objective-C,然後點Next,選擇存放項目的目錄,自動會生成"Hello,World!"程式:

//
//  main.m
//  OCStudyDemo
//
//  Created by Luffy on 2021/1/13.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
           

下面我們來分析這個OC程式:

<1>首先,OC的源檔案名字尾是.m,m代表message,是OC中的一個重要機制——消息機制。

<2>main函數仍然是OC程式的入口和出口,int表示傳回值類型為整型,return 0表示函數的傳回狀态;main函數中的參數是為了程式在運作時接收使用者傳遞的資料,也可以v把參數删掉。

<3>#import指令是#include的增強版,是一個預編譯處理指令(會在預編譯期将指定的頭檔案拷貝到寫指令的地方)。

a.之是以稱之為include的增強版,是因為在一個OC檔案中,不管 import多少次同一檔案,這個檔案隻會被拷貝一次,而include會拷貝多次。

b.import底層實作簡要原理:#import指令在包含檔案時,首先會判斷此檔案是否已經被包含,如果被包含了則不做任何處理,否則才會包含該檔案。

<4>Foundation是一個架構,所謂架構其實就是一個功能集或者叫做工具集,蘋果或者第三方将常用的功能事先寫好,封裝到一個個函數或者類中,然後将這些函數和類打包成一個工具集,和C

的函數庫比較相似。

a.Foundation意為基礎、基本,這個架構裡面封裝了最基礎的功能,比如輸入輸出、一些資料類型。

b.Foundation.h的路徑(不用刻意去記):

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/Foundation.h

我們打開Foundation.h,發現裡面幾乎都是#import xxxxxx,其實Foundation.h這個頭檔案裡面包含了Foundation架構中所有的頭檔案,是以我們隻需要導入 Foundation.h,就可以使用該架構的所有函數和類。

<5>@autoreleasepool是自動釋放池,我們先有這麼個概念就好,先不用管它是幹嘛用的。我們可以把代碼寫到自動釋放池裡面,也可以把它删掉。

<6>NSLog函數

(1)作用:是 printf函數的增強版,向控制台輸出資訊。

(2)文法:NSLog(@"格式控制字元串",變量清單);  或者 NSLog(@"格式控制字元串");

(3)增強的特點:

  • 輸出一些調試相關資訊:
    黑馬OC聽課筆記(第一天)
  • 可以自動換行,而不需要在末尾輸入'\n'換行符。
  • NSLog不僅可以輸出C語言中的資料類型變量的值,還可以輸出OC新增的資料類型的變量的值。

(4)用法與printf函數類似,一樣可以輸出變量的值,并且占位符和用法一樣。

(5)使用注意:

  • NSLog函數第一個參數是NSString,必須在字元串前面加“@”符号;(下面會講NSString)
  • 如果在字元串末尾加了'\n',那麼函數自身的自動換行功能就會失效。(我測試的結果是'\n'加在字元串末尾并不會使得NSLog的自動換行功能失效,可能跟Xcode版本有關。我的環境:MAC版本10.15.4 (19E287)、Xcode 12.3、Build version 12C33)

<7>字元串

(1)C語言中字元串的存儲方式:

  • 字元數組存儲
  • 使用字元指針

(2)OC設計了一種更為好用的用來存儲字元串的類型:NSString

(3)NSString字元串常量必須使用“@”作為字首。(上面說NSLog第一個參數必須加“@”就是因為這個原因)

(4)NSString類型的指針變量隻能存儲OC字元串。代碼示例:NSString *str = @"古今多少事,都付笑談中。"。

PS:如果需要使用NSLog函數輸出 NSString,需要使用占位符“%@”:

//
//  main.m
//  OCStudyDemo
//
//  Created by Luffy on 2021/1/13.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *saying = @"是非成敗轉頭空,幾度夕陽紅。";
        NSLog(@"人生的道理都在這句詩裡面:%@",saying);
        
    }
    return 0;
}
           

<8>NS字首:NextStep ---> Cocoa --->Foundation架構之中。有NS字首,說明是NextStep裡面的内容。比如NSLog、NSString。

<9>@符号:

  • 将C字元串轉換為OC字元串。
  • OC中的絕大部分關鍵字都是以@符号開頭。

<10>注釋:與C語言注釋一毛一樣,分為單行注釋和多行注釋。

<11>函數的定義和調用:還是和C語言一樣:

//
// 示範函數調用
//  Created by Luffy on 2021/1/13.
//

#import <Foundation/Foundation.h>
/*
  1.先聲明函數
 **/
void test(void);
/*
 2.再實作函數
 **/
void test(){
    NSString *saying = @"是非成敗轉頭空,幾度夕陽紅。";
    NSLog(@"人生的道理都在這句詩裡面:%@",saying);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        /*
         3.再調用函數
         **/
        test();
    }
    return 0;
}

           

<12>OC程式的編譯、連結、執行:

  1. 在.m檔案中編寫符合OC文法規範的源代碼;
  2. 使用編譯器将源代碼編譯為目标檔案:cc -c xx.m  這行指令會依次執行編譯預處理、檢查文法、編譯;
  3. 連結:如果程式中使用到了架構的函數或類,那麼連結的時候需要告訴編譯器去哪個架構中找這個函數或者類。指令是:cc xx.o -framework 架構名稱;
  4. 連結成功以後,就會生成“a.out”可執行檔案。
黑馬OC聽課筆記(第一天)

使用“&&”可以把兩個指令連起來執行。

學到這裡我有一個疑問,C預編譯處理會把包含的頭檔案都拷貝到目前檔案中,這句話沒錯吧,那為啥我用指令行編譯的時候還要連結架構呢?關于這個問題,問了很多人,百度了一圈,發現這篇博文講得不錯。

<13>OC程式和C程式各個階段的字尾名對比

程式設計語言 源檔案 目标檔案 可執行檔案
C .c .o .out
OC .m .o .out

OC中的資料類型

<1>OC支援C語言中的所有資料類型:

  • 基本資料類型:

    a.整型:short、int、long、long long int;

    b.浮點型:float、double、long double;

    c.字元型:char;

    d.枚舉:enum;

    e.布爾:_Bool.

  • 指針類型:int *p1;
  • 構造類型:數組、結構體;
  • 空類型:void.

<2>BOOL類型:存儲"YES"或"NO"中的任意資料,一般用來存儲條件表達式的結果。

BOOL的本質:實際上BOOL是一個有符号的char變量:typedef signed char BOOL;(滑鼠右鍵BOOL,選擇"Jump to Definition"跳轉到BOOL實作的地方,我的Xcode 12.3不能跳轉過去,難受)

黑馬OC聽課筆記(第一天)

YES實際就是 1:#define YES ((BOOL)1)  

NO實際就是 0:#define NO ((BOOL)0)

<3>Boolean:和BOOL幾乎一樣,之是以定義兩個功能相同的類型關鍵字,是為了吸引其他程式設計語言的程式員。

本質:無符号的char類型:typedef unsigned char Boolean;

#define true 1

#define false 0

ps:一般使用 BOOL

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 6;
        int b = 8;
        BOOL isMax = a > b;
        Boolean isMax2 = a > b;
        //C和OC中都沒有布爾變量的占位符,以此方式解決對布爾變量的值的列印
        NSLog(@"a比b大嗎?%@",isMax ? @"YES" : @"NO");
        NSLog(@"a比b大嗎?%@",isMax2 ? @"YES" : @"NO");
    }
    return 0;
}           

<4>class類型

<5>id類型:萬能指針

<6>nil:與NULL差不多

<7>SEL:方法選擇器

<8>block:代碼段

運算符、控制語句、關鍵字

OC支援C語言中的所有運算符、控制語句、關鍵字,并且效果是一樣的。OC新增的關鍵字絕大多數都是以“@”符号開頭,比如:@interface、@implementation、@public等。所有還是那句話,OC語言完全相容C語言。

面向對象

實作需求:把大象放進冰箱:

面向過程的思維方式:第一步:打開冰箱;第二步:把大象放進去;第三步:把冰箱門關上。

面向對象的思維方式:找一個冰箱具備以下功能:自己開門、自己把大象拉進去、自己關門。

是以說面向過程強調的是步驟,按部就班;面向對象強調的是對象,基于對象實作你要做的事情。關于面向對象的更多内容參見百度百科:面向對象。

類和對象

物以類聚人以群分,類是對一群具有相同特征或者行為的事物的統稱,抽象的,不能直接使用。

對象

對象是現實世界中一個具體的存在,是一個看得見摸得着的東西,拿過來就可以直接使用。

類和對象的關系

類是模闆,類的對象是根據這個模闆建立出來的,類模闆中有什麼,對象中就有什麼,絕不可能多,也不可能少。

生活中的案例:做月餅。類是做月餅的模子,模子不能吃,所有類不能直接使用,對象是根據模子制造出來的月餅,模子上有什麼花紋,月餅上就有什麼花紋,不會多也不會少,月餅可以吃,是以對象可以直接拿來使用。

設計一個類

-->類的名字:你要描述的這類事物叫什麼;

-->這類事物有什麼特征;

定義并使用類

/**
 1.先有類還是先有對象?
    從現實的角度講,先有對象再有類;從代碼的角度講,先有類再有對象。
 2.如何定義類:
   <1>文法:
        a.位置:直接寫在源檔案中,main函數之外。
        b.類的定義分為兩部分:
            類的聲明
            @interface 類名 : NSObject
            {
                //類的特征,以變量的形式描述。
            }
            //在此定義類的行為
            @end
            類的實作
            @implementation 類名
            //講類的行為在這裡實作,行為就是功能
            @end
    <2>注意點:
        a.類必須要有聲明和實作;
        b.類名命名需見名知意,首字母大寫;
        c.NSObject是什麼意思暫時不用管;
        d.用來描述這類事物特征的變量必須定義在@interface的大括号裡面,這些變量被稱為屬性、成員變量、執行個體變量、字段...
        e.屬性必須以下劃線開頭。
 3.類是無法直接使用的,必須先根據這個類建立一個關于這個類的對象,那麼如何建立一個對象?
    文法:類名 *對象名 = [類名 new]
        Person *p = [Person new];a
 4.如何使用對象?
    如何通路對象的屬性?
        (1)預設情況下,對象的屬性不允許被外界直接通路。如果需要通路,隻需要在屬性前加一個“@public”關鍵字聲明一下即可;
        (2)通路對象的方式:
         對象名->屬性名 = 值;
         對象名->屬性名;
 5.課堂練習:
    寫一個學生類。屬性:姓名、年齡、國文成績、數學成績、英語成績
 
 */

#import <Foundation/Foundation.h>

@interface Student : NSObject{
    @public
    NSString *_name;
    int _age;
    float _ChineseScore;
    float _mathScore;
    float _EnglishScore;
}

@end

@implementation Student

@end

@interface Person : NSObject{
    @public
    NSString *_name;
    int _age;
    float _height;
}

@end

@implementation Person
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //練習題
        Student *stu = [Student new];
        stu->_name = @"小花";
        stu->_age = 17;
        stu->_ChineseScore = 89.5f;
        stu->_mathScore = 98.0f;
        stu->_EnglishScore = 87.0f;
        
        NSLog(@"%@今年年齡%d,本次考試國文成績%.1f,數學成績%.1f,英語成績%.1f。",
              stu->_name,
              stu->_age,
              stu->_ChineseScore,
              stu->_mathScore,
              stu->_EnglishScore);
        
        
        Person *p1 = [Person new];
        p1->_name = @"Luffy";
        p1->_age = 27;
        p1->_height = 173.5f;
        NSLog(@"%@的年齡是%d歲,身高是%.1f厘米。",p1->_name,p1->_age,p1->_height);
    }
    return 0;
}

           

方法調用

/**
 1.一類事物不僅具有相同的特征還具有相同的行為。
 行為就是一個功能,C語言中用函數形式表示一個功能,OC使用方法的形式表示。
 2.無參數的方法:
 方法聲明的位置都是在@interface的大括号的外面,實作的位置都是在@implementation之中實作。
 (1)聲明:
    文法:-(傳回值類型)方法名稱;
 (2)實作
    實作的文法:将方法的聲明拷貝到@implementation之中,去掉分号,追加大括号,
    将方法的實作寫在大括号中。
 (3)調用
    a.調用方法必須通過對象;
    b.文法:[對象名 方法名];
 3.帶一個參數的方法:
 (1)聲明:
    文法:-(傳回值類型)方法名稱 : (參數類型) 形參名稱;
 (2)實作
    實作的文法:将方法的聲明拷貝到@implementation之中,去掉分号,追加大括号,
    将方法的實作寫在大括号中。
 (3)調用
    a.調用方法必須通過對象;
    b.文法:[對象名 方法名];
 4.帶多個參數的方法:
 (1)聲明:
 文法:- (傳回值類型) 方法名 : (參數1類型) 形參1名稱 : (參數2類型) 形參2名稱 : ... : (參數N類型) 參數N名稱;
 (2)實作:将方法的聲明拷貝到@implementation之中,去掉分号,追加大括号,
 将方法的實作寫在大括号中。
 (3)調用:同樣需要通過對象調用,文法:[對象名 方法名 : 參數1 : 參數2 ... : 參數N]
 5.帶參數的方法聲明規範:
 (1)如果方法隻有一個參數,要求最好方法名叫做 xxxWith 或者 xxxWithxxx;(其實就是為了見名知意,可讀性更高)
 (2)如果方法有多個參數,建議方面名稱這樣命名:
 方法名With : (參數1類型) 參數1名稱 and : (參數2類型) 參數2名稱 ... and : (參數N類型) 參數N名稱
 
 可讀性更高的:
 方法名With參數1名稱:(參數1類型) 參數1名稱 and參數2名稱:(參數2類型) 參數2名稱... and參數N名稱 : (參數N類型) 參數N名稱
 */

#import <Foundation/Foundation.h>

@interface Person : NSObject{
    
}
- (void) run;
- (void) eat : (NSString *) food;
- (int) sum : (int) num1 : (int) num2;
- (int) sumWith : (int) num1 and : (int) num2;
- (int) sumWithNum1 : (int) num1 andNum2 : (int) num2;
@end

@implementation Person
-(void)run{
    NSLog(@"剛睡醒,現在起來跑步。");
}

- (void) eat:(NSString *)food{
    NSLog(@"旺财說:“主人,你賞我的%@賊好吃!”",food);
}
- (int) sum:(int)num1 :(int)num2{
    int sum = num1 + num2;
    return sum;
}
- (int) sumWith : (int) num1 and : (int) num2{
    int sum = num1 + num2;
    return sum;
}
- (int) sumWithNum1 : (int) num1 andNum2 : (int) num2{
    int sum = num1 + num2;
    return sum;
}
@end

int main(){
    @autoreleasepool {
        Person *p1 = [Person new];
        [p1 run];
        [p1 eat:@"紅燒排骨"];
        
        int sum = [p1 sum:6 :2];
        NSLog(@"sum的值是:%d。",sum);
        
        int sumWith = [p1 sumWith:2 and:6];
        NSLog(@"sumWith的值是:%d。",sumWith);
        
        int sumWithNum1 = [p1 sumWithNum1:2 andNum2:6];
        NSLog(@"sumWithNum1的值是:%d。",sumWithNum1);
    }
    return 0;
}
           

其他注意

  • 同一個類可以建立無數的對象,且這些對象之間沒有任何關系;
  • 建立對象時,寫右中括号,Xcode會自動補全左括号;
  • 在方法的實作中可以直接通路屬性;
  • 這個方法通過哪個對象調用,通路的就是哪個對象的屬性。
/**
練習題:
 要求:類名、屬性名、屬性類型、方法名、方法參數,方法傳回值自拟,
 自己寫main函數測試設計的程式是否合理。
 
 設計一個“狗”類:
 <1>屬性:顔色、奔跑的速度(機關:m/s)、性别、體重(機關是kg);
 <2>行為:
 a.吃:每吃一次,體重增加0.5kg,輸出吃完後的體重;
 b.吠(叫):輸出所有的屬性;
 c.跑:每跑一次,體重減少0.5kg,輸出速度和跑完後的體重。
 */

#import <Foundation/Foundation.h>

@interface Dog : NSObject{
    @public
    NSString *_color;
    float _speed;
    NSString *_sex;
    float _weight;
}
-(void) eat;
-(void) barking;
-(void) run;

@end

@implementation Dog

_weight = 99.9f;

-(void) eat{
    _weight += 0.5f;
    NSLog(@"狗子啃完骨頭後體重是:%.2f kg。",_weight);
}
-(void) barking{
    NSLog(@"我是一隻小%@狗,我奔跑的速度可以達到%.2f m/s,跑得比兔子快多了,我的性别是%@,目前體重是%.2f kg",_color,_speed,_sex,_weight);
}
-(void) run{
    _weight -= 0.5f;
    NSLog(@"我這次跑步的速度是%.2f m/s,跑完之後體重變成了%.2f kg。",_speed,_weight);
}
@end
int main(){
    @autoreleasepool {
        Dog *dog1 = [Dog new];
        dog1->_color = @"黃";
        dog1->_speed = 16.8f;
        dog1->_sex = @"公";
        dog1->_weight = 36.6f;
        
        [dog1 run];
        [dog1 barking];
        [dog1 eat];
    }
    return 0;
}
           

繼續閱讀