Objective-C 語法簡介

Objective-C

OC 是 C 的超集,因此在OC中既有C語言的語法特性,也有擴展出來的語法特性。有時候能夠互通,大部分時候不能互通。例如 C語言中的 char 類型,在OC中極少用到,C語言中的CFImageRef徹底不能在擴展語法部分使用,必須使用bridge。面試

從語言層面上,iOS SDK 的實現分爲幾層,大體是 CGFoudation -> Foundation -> Cocoa Touch (大部分時候是UIKit)。CGFoudnation部分是C語言編寫,另兩部分則是OC開發。objective-c

OC在編譯階段被CLang翻譯成C語言。C 到 OC 的實現大量使用了 struct 類型。編程

數據類型

基本數據類型

  • 從C語言過分來的: int long double
  • Foundation擴展的:NSInterger
  • UIKit 擴展的:CGFloat 等

對象類型

  • NSObject
  • NSNumber
  • NSString
  • NSArray
  • NSDictionary
  • NSNull

還有不少SDK內置的類型,和自定義的class類型。多線程

對象類型的基類有兩個,NSObject, NSProxy。其中NSObject是最多見最經常使用的。閉包

變量

double width = 100;
CGFloat width = 100;
NSString *str = @"123";
複製代碼

須要注意的:函數

  1. OC中沒有類型推導,所以沒有 let a = 1;這種定義形式,定義變量必定要聲明類型。
  2. C和C++都是經典的C系語言,最讓人望而卻步的就是指針類型。所以,在聲明對象類型時,請使用 * 表示後面的變量是一個指針類型。請和基礎數據類型作區別。
  3. ''"" 在C語言中表示char 和 char * 類型,所以,NSString類型的字面量要使用 @ 修飾,表示這是OC中的對象類型。同理,NSArray 和 NSDictionary 都是這樣。

函數

做爲C的超集,便可以使用C函數的定義,也可使用擴展部分的定義。ui

須要注意的是,這裏討論擴展的函數定義,必定是定義在class 中,做爲實例方法或者靜態方法呈現,對於使用了OC對象類型的C函數定義依然認爲是C函數。atom

無參數的函數定義

int main() {
    // some code ...
}
複製代碼
- (int)main {
    
}
複製代碼

單參數的函數定義

int length(NSString *str) {
    // some code ...
}
複製代碼
- (int)length:(NSString *)str {
    // some code ...
}
複製代碼

多參數的函數定義

int sum(int a, int b) {
    // some code ...
}
複製代碼
- (int)sumWithA:(int)a withB:(int)b {
    // some code ...
}
複製代碼

多參數方法中,習慣性使用with做爲每段函數簽名的開始,嫌醜的話能夠本身發揮,保持優良的可讀性便可。spa

函數的調用

C 方法的調用和 Dart 語言十分類似,再也不贅述。線程

已上面三個方法爲例:

// main
int value = [someObjectInstance main];

// length
int length = [someObjectInstance length:@"angryli"];

// sum
int sum = [someObjectInstance sumWithA:1 withB:2];
複製代碼

C語言沒有class這一說。OC則在其基礎上擴展了OOP功能,這裏會生成一個面試題:怎麼使用C語言實現面向對象?

OC基於C,同事也帶來了頭文件和實現文件的概念。在OC中,頭文件以.h 後綴,實現文件已.m 後綴。理論上其餘類只能訪問頭文件中聲明的屬性和方法,可是利用運行時(相似於映射)方法,能夠訪問到.m文件中的私有方法,固然你這是有風險的,一旦.m移除該方法,外部調用是無從得知的。

先看一個例子:

// Person.h
@interface Person : NSObject

@property (strong, nonatomic) NSString *name;

- (void)sayHello;

+ (void)cry;

@end

// Person.m
@interface Person () {
    int _a;
}

@property(assign, nonatomic) int age;

@end

@implementation Person

- (void)sayHello {
    [self _printHello];
}

- (void)_printHello {
    NSLog(@"Hello %@, my age is %d", self.name, self.age);
}

+ (void)cry:(int)a b:(int)b {
    NSLog(@"Crazy!");
}

- (NSString *)description {
    return @"I am Person class";
}

@end
複製代碼

擴展與分類

@interface Person ()
@end

@interface Person (Run)

- (void)run;

@end

@implementation Person (Run)

- (void)run {
    NSLog(@"我跑了 3 公里");
}

@end
複製代碼

這裏有幾個概念:實例變量,實例屬性,實例方法,類方法,class定義,class實現,class擴展,class分類,須要你們逐一理解。

須要注意的是:

  1. 相似Java中的get和set方法,貌似在15仍是13年的時候,OC 2.0 升級了,升級了 @property, 利用編譯器特性,在編譯階段自動添加 set、get 方法和一個帶下劃線的實例變量,例如 age 會生成 - (int)age; -(void)setAge(int)age { _age = age; }int _age;
  2. 擴展中定義的屬性會自動生成set 和 get 方法,擴展中聲明的屬性則不會補全。
  3. 擴展中出現同名方法,後加載的會覆蓋以前的方法

協議和代理

@protocol FinishDelegate : NSObject

// @required 可省略
- (void)didFinish;

@optional
- (void)didCancel;

@end
複製代碼
@interface DetectViewController() <FinishDelegate>
@end

@implementation DetectViewController

// 必須方法若是不實現,則會出警告。若是堅持不實現,也不會卡編譯,運行時會crash。
- (void)didFinish {
    NSLog(@"完成");
}

// 可選方法能夠不實現,不管是否實現,調用方在調用該方法時,正常代碼都須要判斷是否實現 responseToSelector:
- (void)didCancel {
    NSLog(@"取消");
}

- (void)_showScanController {
    ScanViewController *vc = [[ScanViewController alloc] init];
    vc.delegate = self;
    
    [self showDetailController:vc sender:self];
}

@end


@interface ScanViewController : UIViewController

@property (weak, nonatomic) id<FinishDelegate> delegate;

@end

@implementation ScanViewController

// after some actions, you will become finish or cancel state

- (void)_scanSuccess {
    if (self.delegate && [self.delegate responseToSelector:@selector(didFinish)]) {
        [self.delegate didFinish];
    }
}

- (void)_scanCancel {
    if (self.delegate && [self.delegate responseToSelector:@selector(didCancel)]) {
        [self.delegate didCancel];
    }
}

@end

複製代碼

最好是不管是什麼方法,都判斷是否實現。

內存管理

三種基本的GC方式:標記清除、複製收集和引用計數。

OC選擇使用引用計數的方式管理內存。並且是手動的。在iOS 6以後出現了ARC,自動引用計數,也意味着開發者在使用OC對象的時候無需手動申請和釋放內存空間,只要按照必定的規範編寫,編譯器會在編譯階段自動添加 alloc(分配) 和 release(釋放) 代碼。

引用計數最大缺點就是循環引用,相同的代碼在Java或者Dart中沒有問題,在OC中則大大的有問題。例以下面的代碼:

void initState {
    _textController.listen(() {
        print(_textController.text);
    });
}
複製代碼

咱們常常會這麼去監聽一個輸入框的輸入並打印其內容,這在 dart 中十分常見,也沒有什麼問題。但在OC中,相似代碼則已經形成了很是常見一種的循環引用了。

_textController 持有 listen 閉包,listen閉包又捕獲並持有_textController,他們的引用計數都 >=1,所以誰都不會被釋放,若是沒有其餘代碼斷開他們之間任一的引用關係,這段會成爲永遠沒法釋放的內存。

多線程編程

GCD

NSOperation

相關文章
相關標籤/搜索