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,所以谁都不会被释放,若是没有其余代码断开他们之间任一的引用关系,这段会成为永远没法释放的内存。