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 類型。編程
int
long
double
等NSInterger
等還有不少SDK內置的類型,和自定義的class類型。多線程
對象類型的基類有兩個,NSObject, NSProxy。其中NSObject是最多見最經常使用的。閉包
double width = 100;
CGFloat width = 100;
NSString *str = @"123";
複製代碼
須要注意的:函數
let a = 1;
這種定義形式,定義變量必定要聲明類型。''
和 ""
在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分類,須要你們逐一理解。
須要注意的是:
- (int)age; -(void)setAge(int)age { _age = age; }
和 int _age;
@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,所以誰都不會被釋放,若是沒有其餘代碼斷開他們之間任一的引用關係,這段會成爲永遠沒法釋放的內存。