独特的@符号
首先,ObjectC是C的超集,为了不和C中已有的东西冲突,ObjectC中特有的东西前面都带有@符号
语法的差异
一、方法名
ObjectC中的方法名由多个段组成。
比如我们想初始化一个controller,最常用的是这个方法
initWithNibName:bundle:
看起来很奇怪是吧?方法具体签名如下
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
参数更紧接着调用的谓语,这样看起来更符合自然语法,在拥有多个参数的情况下即使没有对参数的说明,也非常容易记住。
中括号表达式:
一个中括号代表一次调用,看起来比较清晰。
具体调用如下:
[[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
我们先是根据MyViewController类所需的大小分配了一块内存,然后发送消息去初始化这块内存。
因为方法名是包括:符号的,所以在通过selector选择方法的时候,:符号不能忘记
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
二、id
这类似于C中的通用指针void*,可以用来储存任何类的对象。也像C#中的dynamic关键字,就是告诉编译器在编译时无需检查这个变量的类型,运行的时候再检查确定。换句话说就是动态绑定。
在ObjectC中哪里会用到呢?
1.初始化的时候
ObjectC一个对象不能有两个名字相同的方法,即使他们的参数和返回类型不同。因此子类的init方法既不能返回子类,否则无法调用父类相同的init方法,当然也不能返回父类。
这时候就需要用到id这个通用的指针了。
2.不需要具体类型,只需要知道这个实例实现了某些方法。类似于C#中对接口编程的用法,常用声明delegate属性,以便使用不同的策略提供对象。
@property (nonatomic, weak) id<RequestDelegate> delegate;
需要提醒的是,虽然它可以存储任何类的对象,如果滥用它,就会失去静态类型时编译器所提供的好处。
三、强弱引用(strong, week; retain, assign)
是否加入引用计数的一种方式。iOS中没有自动垃圾回收机制,垃圾回收是根据引用计数来决定的。计数为0的时候会被认为垃圾马上回收掉。
在ARC的项目中
@property (strong, nonatomic) MyController *myController;
四、协议 (protocol)
类似于C#中的Interface,区别在于protocol中定义的方法可以选择不需要实现,也就是@optional的。当然如果没有标记那么就是必须实现的。
@protocol BoardActionDelegate<NSObject>@required- (void) selectPressed:(UIView *)sender;@optional- (void) cancelPressed:(UIView *)sender;@end
五、范畴(Categories)
1.命名范畴,类似于C#中的扩展方法,用于给已有的类扩展自定义的方法,区别在于ObjectC中不限制方法的类型,可以是实例或者是类方法。但是不能带成员变量。
比如说我们扩展UIColor类
@interface UIColor (Extract)- (void)extract_getRed; @end
这类文件的命名最好为原类名 + 扩展类名,如 UIColor+Extract.h
、UIColor+Extract.m
2.类扩展(class
extension),看起来就像一个没有名字的范畴。可以带变量成员,且必须实现。
如果根据良好的代码划分,我想放在头文件.h的往往是对外公开的方法和属性,而自己内部使用的私有方法就不需要放在头文件里。在像C#这种没有头文件的语言里,我们仍然会按照约定先写Public 方法,然后用#region 把它标记起来,方便别人查看。
假设我们有一个CarStock类,在CarStock.m中有如下代码,会遇到这么一个问题,如果init中调用的方法声明如果在init的位置下面,那么编译器就会报错,因为它是从上往下解析的.
- (id)init { if ((self = [super init])) { [self refreshStock]; } return self; }- (void)refreshStock { // ... }
这时有几种解决方式
1.refreshStock方法上移
2.refreshStock声明在头文件CarStock.h里
但这些都不符合我想要写一个私有方法的想法。ObjectC中方法是不带作用域声明的,也就是你无法像下面这样声明
private - (void)refreshStoc
所以可以在CarStock.m中,通过一个无命名的范畴,来解决这个问题
@interface CarStock ()- (void)refreshStock; @end
Xcode 4.4版本(LLVM4编译器)后编译器新增的一些语法糖
Xcode是免费的,所以咱们可以升级到新版本来享受一下新编译器的一些好处
1.更加多的字面量支持
原来我们在ObjectC中创建一个NSString类型的对象时,可以
NSString *myName=@"langxue";
现在我们创建其他对象也可以字面量语法了
NSNumber *myNumber =@3;NSNumber *yesValue =@YES;NSArray *array =@[@1,@2,@3,@4];NSDictionary *dictionary =@[@"key1":@"value1" ,@"key2":@"value2"]
2.下标访问
我们可以通过下标来直接访问我们需要的元素,这在原来是不可以的。
int element3 = array[3]; int elementAt3 = dictionary [@"key3"]
3.自动合成@property
我们声明了property以后
@property (strong, nonatomic) MyController *myController;@property (nonatomic, copy) void (^completionHandler)();
原来的情况下访问器是通过@synthesize关键字合成的。
@synthesize myController = _myController;
@synthesize completionHandler = _completionHandler;
有了新的LLVM编译器,就可以省略这些用于合成实例变量的代码了。LLVM
4编译器会自动合成这些实例变量。当然如果明确地写了get,set方法,LLVM
4就不再自动生成@synthesize指令了。需要记住的是,自动合成的实例变量会按照ObjectC中的惯例以下划线_开头。