空空叶博客 学习与开发博客

Objective C

2018-09-06
kongkongye
ios

Objective C学习笔记

面向对象

objc添加了面向对象的支持,主要特征如下:

  1. 封装: 相关的方法与数据写在一个类内
  2. 继承: 可以多级继承,但不能多继承;可以访问基类私有成员(定义在接口里而非实现里的)
  3. 多态:
  4. 方法覆盖
  5. 方法重载

操作符

算术

+ - * / % ++ –

关系

== != > < >= <=

逻辑

&& || !

& | ^ ~ « »

赋值

= += -= *= /= %= «= »= &= ^= |=

其它

  • sizeof() 变量大小
  • & 变量地址
  • * 变量指针
  • ?: 条件表达式

interface

objc里的@interface头文件用来定义对外的接口,分为几种情况:

  1. .h文件定义方法,.m文件实现方法,这是最正常的做法
  2. .h文件定义方法,.m文件没实现方法,这样在编译时仍然没有问题,但在运行时会报错
  3. .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

扩展用于解决的问题:

  1. 允许对象拥有私有的interface,且可由编译器验证.
  2. 支持一个公有只读,私有可写的属性: 在类的声明中,声明一个属性是只读的,随后在扩展中,声明属性可写.

特点:

  • 必须有源类的实现代码
  • 方法与数据都是私有的
  • 继承的类也无法访问扩展的方法
  • 会在编译期将定义的方法等合入主类

分类

语法:

@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声明:

  1. void: 返回值
  2. blockName: block名称
  3. int arg1, int arg2: 参数列表

void ^(int arg1, int arg2) {//do something};是block定义:

  1. void: 返回值,可省略
  2. int arg1, int arg2: 参数列表
  3. //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, ...)

使用方式:

  1. 静态方式: SEL sel = @selector(instanceMethod);
  2. 字符串方式: SEL sel = NSSelectorFromString(@"aSelectorName") (此方式可能产生内存泄露,因为在ARC模式下,返回值类型是不知道的,因此无法自动释放)

注意:

  1. @selector是查找当前类(及子类)的方法
  2. 方法参数也是查询条件之一,如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
}

内存管理

可以分为两类:

  1. MRR(Manual Retain-Release)
    • 对自己创建的对象拥有所有权
    • 可以使用retain来获取对象所有权,使用场景:
    1. 在访问方块或init方法里,需要获取对象所有权来存储为属性值
    2. 阻止对象因为某些操作的副作用而失效 * 不再需要时,必须放弃所有权(release) * 不能释放没有所有权的对象
  2. ARC(Automatic Reference Counting): 不需要手动去retainrelease(推荐使用),使用方法:
  int main() {
     @autoreleasepool {
        SampleClass *sampleClass = [[SampleClass alloc]init];
        [sampleClass sampleMethod];
        sampleClass = nil;
     }
     return 0;
  }

#import

  1. <>: 全局,那些直接传给编译器的,如#import <math.h>
  2. "": 局部,使用相对路径,如#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中方法的重载是根据标签来识别区分的,并不是根据参数名来区分

上一篇 react native

下一篇 XCode

目录