- 面向对象
- 操作符
- interface
- 协议(Protocol)与代理/委托(Delegate)
- 扩展(Extension) & 分类(Category)
- block
- typedef
- Property
- SEL & IMP
- delegate
- 动态绑定(Dynamic Binding)
- Composite
- Foundation Framework
- 集合遍历
- 内存管理
- #import
- 问题
- 注意
Objective C学习笔记
面向对象
objc添加了面向对象的支持,主要特征如下:
- 封装: 相关的方法与数据写在一个类内
- 继承: 可以多级继承,但不能多继承;可以访问基类私有成员(定义在接口里而非实现里的)
- 多态:
- 方法覆盖
- 方法重载
操作符
算术
+ - * / % ++ –
关系
== != > < >= <=
逻辑
&& || !
位
& | ^ ~ « »
赋值
= += -= *= /= %= «= »= &= ^= |=
其它
- sizeof() 变量大小
- & 变量地址
- * 变量指针
- ?: 条件表达式
interface
objc里的@interface
头文件用来定义对外的接口,分为几种情况:
- .h文件定义方法,.m文件实现方法,这是最正常的做法
- .h文件定义方法,.m文件没实现方法,这样在编译时仍然没有问题,但在运行时会报错
- .h文件未定义方法,.m文件里有这个方法,这样相当于私有方法
协议(Protocol)与代理/委托(Delegate)
类似java的接口,将一些类通用的方法抽象出来
协议的语法:
@protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end
遵从协议的类:
@interface MyClass : NSObject <MyProtocol>
...
@end
多个协议以逗号,
分隔
可以使用这样的方法来初始化一个实现了名为Hello的protocol的类:
id<Hello> test = [[Test alloc] init];
代理是协议的一种使用方法,也是一种设计模式.
扩展(Extension) & 分类(Category)
从语法上,扩展类似于一个匿名的分类,但它们的作用是不同的.
扩展可以添加实例变量
,但分类不能
扩展
语法:
@interface ClassName ()
@end
扩展用于解决的问题:
- 允许对象拥有私有的interface,且可由编译器验证.
- 支持一个公有只读,私有可写的属性: 在类的声明中,声明一个属性是只读的,随后在扩展中,声明属性可写.
特点:
- 必须有源类的实现代码
- 方法与数据都是私有的
- 继承的类也无法访问扩展的方法
- 会在编译期将定义的方法等合入主类
分类
语法:
@interface ClassName (CategoryName)
@end
使用场景:
- 可以把类的实现分开在几个不同的文件里面
- 可以减少单个文件的体积
- 可以把不同功能组织到不同category里
- 可以由多个开发者共同完成一个类
- 可以按需加载想要的category
- 声明私有方法
特点:
- 可以为任何类定义,即使没有原始的实现代码
- 定义在目录中的方法可以在源类及子类的实例中使用
- 对于运行时来说,定义在源类中的与目录中的方法并无区别
- 会在程序启动时才将方法等合入主类
block
block在有的情况下可以代替delegate方式,更加简单直观.
- block的代码是内联的,效率高于函数调用
- block可以访问外界的局部变量,但默认是只读的,如果需要修改,则需要在变量上加上
__block
关键字,表示外部变化将在block内进行同样操作 - block会被objc看成是
对象
处理
语法:
void (^blockName)(int arg1, int arg2) = void ^(int arg1, int arg2) {
//do something
};
其中void (^blockName)(int arg1, int arg2)
是block声明:
void
: 返回值blockName
: block名称int arg1, int arg2
: 参数列表
void ^(int arg1, int arg2) {//do something};
是block定义:
void
: 返回值,可省略int arg1, int arg2
: 参数列表//do something
: 封装的代码
注意,如以下代码
__block float price = 1.99;
float (^finalPrice)(int) = ^(int quantity) {
return quantity * price;
};
int orderQuantity = 1;
price = .99;
NSLog(@"%2.2f", orderQuantity, finalPrice(orderQuantity));
将输出1.99
.
可以理解为只在定义block时赋值(在定义时,值复制了一份到block内,引用类型除外),可以将局部变量声明为__block
,这样输出将是0.99
可以用typedef来简化使用:
typedef double (^unary_operation_t)(double op);
然后就能使用unary_operation_t
:
unary_operation_t square;
square = ^(double operand) {
return operand * operand;
}
typedef
Property
一. 正常情况下手工创建存取器:
@interface Car : NSObject
{
NSString *carName; //实例变量
}
// setter
- (void)setCarName:(NSString *)newCarName;
// getter
- (NSString *)carName;
@end
@implementation Car
// setter
- (void)setCarName:(NSString *)newCarName
{
carName = newCarName;
}
// getter
- (NSString *)carName
{
return carName;
}
@end
二. 使用@Property创建存取器:
@interface Car : NSObject
{
// 实例变量
NSString *carName;
}
@property(nonatomic,strong) NSString *carName;
@end
@implementation Car
@synthesize carName;
@end
使用@synthesize
自动生成存取器,并隐藏了存取器
使用操作符.
来获取与设置:
car.carName = @"Jeep";
NSLog(@"Car Name: %@", car.carName);
三. 不声明实例变量(由系统自动生成,变量名为_属性名
):
@interface Car : NSObject
@property(nonatomic,strong) NSString *carName;
@end
然后可以使用实例变量_carName
SEL & IMP
- SEL定义:
typedef struct objc_selector *SEL;
- IMP定义:
typedef id (*IMP)(id, SEL, ...)
使用方式:
- 静态方式:
SEL sel = @selector(instanceMethod);
- 字符串方式:
SEL sel = NSSelectorFromString(@"aSelectorName")
(此方式可能产生内存泄露,因为在ARC模式下,返回值类型是不知道的,因此无法自动释放)
注意:
- @selector是查找当前类(及子类)的方法
- 方法参数也是查询条件之一,如
SEL sel = @selector(add:number2:)
IMP相当于实现函数的地址,使用方式:
IMP theImplementation = [self methodForSelector:theSelector];
theImplementation(self, theSelector, 3, 5);
delegate
动态绑定(Dynamic Binding)
在Objective C中,所有方法都是在运行时动态解析的,比如:
NSArray *shapes = [[NSArray alloc]initWithObjects: square, rectangle];
id object1 = [shapes objectAtIndex:0];
[object1 printArea];
id object2 = [shapes objectAtIndex:1];
[object2 printArea];
其中square与rectangle是两个类
Composite
似乎有点类似于装饰模式?似乎只是一种设计模式?暂时还不是很清楚.
Foundation Framework
- 提供基础实用类
- 引入一致的约定使开发更简单,像取消分配
- 支持unicode,对象持久化,对象分发
- 增加一级,减少与操作系统的依赖
框架由NeXTStep
开发,因此以NS
作为类前缀
集合遍历
正向遍历:
for (classType variable in collectionObject) {
statements
}
反向遍历:
for (classType variable in [collectionObject reverseObjectEnumerator]) {
statements
}
内存管理
可以分为两类:
MRR
(Manual Retain-Release)- 对自己创建的对象拥有所有权
- 可以使用
retain
来获取对象所有权,使用场景:
- 在访问方块或init方法里,需要获取对象所有权来存储为属性值
- 阻止对象因为某些操作的副作用而失效
* 不再需要时,必须放弃所有权(
release
) * 不能释放没有所有权的对象
ARC
(Automatic Reference Counting): 不需要手动去retain
与release
(推荐使用),使用方法:
int main() {
@autoreleasepool {
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
sampleClass = nil;
}
return 0;
}
#import
<>
: 全局,那些直接传给编译器的,如#import <math.h>
""
: 局部,使用相对路径,如#import "headers/my_header.h"
objc内,编译器会解决互相import的问题,可以放心导入
问题
block循环引用造成内存泄露
因为block会视作对象,因此其生命周期会持续到持有者的生命周期结束, 但由于block捕获变量的机制,可能持有block的对象也被block持有,造成循环引用,如:
@implementation LXDObject
{
void (^_cycleReferenceBlock)(void);
}
- (void)viewDidLoad
{
[super viewDidLoad];
_cycleReferenceBlock = ^{
NSLog(@"%@", self); //引发循环引用
};
}
@end
可以使用__weak
来解决:
__weak typeof(*&self) weakSelf = self;
_cycleReferenceBlock = ^{
NSLog(@"%@", weakSelf); //弱指针引用,不会造成循环引用
};
注意
- oc中方法的重载是根据
标签
来识别区分的,并不是根据参数名来区分