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

相关文章
相关标签/搜索