OC 基本语法

198 阅读7分钟

类的声明

OC中声明和实现是分两个文件存放的,.h是存放声明,.m存放实现。

@interface 类名 : NSObject

{

     //这类事物具有的共同的特征,将它们定义为变量

}

//功能就是一个方法,将方法的声明写在这里

@end

举例:声明一个Person的类,有名字和年龄的属性:

 @interface ASPerson : NSObject{

    //身高

    NSString *_name; 

    //年龄

    int _age;

}

-(void) inputName:(NSString *) name;

-(void) inputAge:(int *) age;

-(NSString *) info;

 @end
类的实现

语法:

@implementation 类名

    ///将方法的实现写在这里

@end

举例,完成Person类中的Info 的具体实现:

#import "ASPerson.h"



 @implementation ASPerson



- (void)inputName:(NSString *)name{

    _name = name;

}

- (void)inputAge:(int *)age{

    _age = age;

}

- (NSString *)info{

    return [@"my name is " stringByAppendingFormat:@"%@ age: %d",_name,_age];

}

 @end
类的创建

语法

第一种(推荐):

类名 *对象名 = [[类名 alloc] init];

第二种:

类名 *对象名 = [类名 new];

[类名 new] 内部就是调用了[[类名 alloc] init]方法,为什么推荐alloc呢?因为new是固定只能调用init方法,而alloc之后还可以定义调用其他方法。

//创建ASPerson对象

ASPerson *person = [[ASPerson alloc] init]; 
属性的访问

类中的属性通常会以_开头,默认情况下,类的属性是不允许被外界访问的,如果需要被访问,需要添加@public关键字。比如我们需要访问ASPerson中的_name属性,我们先在_name上添加@public关键字。

 @public

NSString *_name;

属性的访问有两种方式:

  • 对象名 -> 属性名; 
person -> _name = @"aserbao";
  • (*对象名).属性名; 
(*person)._name = @"aserbao";

案例:

//创建ASPerson对象

ASPerson *person = [[ASPerson alloc] init]; 

// 调用ASPerson中的inputName方法

[person inputName:@"aserbao"];

[person inputAge:18];

// 调用ASPerson中的info方法。

NSLog(@"%@",person.info);
类型转换
# int 转 NSNumber

NSNumber *number = [NSNumber numberWithInt:1];

# NSNumber 转 int 

int myInt = [number intValue];

# NSString 转int

NSString *stringInt = @“119;

int ivalue = [stringInt intValue];

# int 转 NSString

NSString *string = [NSString stringWithFormat:@"%d",17];



# 字符串拼接,方法一

NSString * str1 = @"123";

NSString * str2 = [str1 stringByAppendingString:@"abc"];

NSLog(@"%@",str2);

# 字符串拼接,方法二

NSString * str1 = @"123";

NSString * str2 = @"abc";

NSString * str3 =[[NSString alloc]initWithFormat:@"%@%@",str1,str2];

方法

方法声明

语法:

- (returnType)methodName:(typeName) param1:(typeName) param2;
  • returnType: 返回类型,void 表示无返回值。

    • instancetype: 返回当前方法所在类相同类型的对象,不能作为参数传递。
    • id:返回当前当前方法所在类的未知类型的对象,可以作为参数传递。

使用案例如下:

///声明一个显示日志的方法

- (void) showLog:(NSString *) input{

    NSLog(@"printStr %@",input);

}
方法调用

方法调用有下面几种方式:

    /// 第一种方式:直接调用

    [self showLog:@"调用方法的第一种方式"];

    

    /// 第二种方式: 通过performSelector调用

    [self performSelector: @selector(showLog:) withObject:@"调用方法的第二种方式"];

    [self performSelector: @selector(showLog:) withObject:@"延时1s调用showLong方法" afterDelay:1];

    

    /// 第三种方式:通过获取方法签名调用

    NSMethodSignature *signOfShowLog = [self methodSignatureForSelector: @selector(showLog:)];

    //获取方法签名对应的invocation

    NSInvocation *invocationOfShowLog = [NSInvocation invocationWithMethodSignature:signOfShowLog];

    //设置消息接受者,与[invocationOfShowLog setArgument:(__bridge void * _Nonnull)(self) atIndex:0]等价

    [invocationOfShowLog setTarget:self];

    //设置要执行的selector。与[invocationOfShowLog setArgument:@selector(printStr1:) atIndex:1] 等价

    [invocationOfShowLog setSelector: @selector(showLog:)];

    //设置参数

    NSString *str = @"通过获取方法签名的方式调用方法";

    [invocationOfShowLog setArgument:&str atIndex:2];

    //开始执行

    [invocationOfShowLog invoke];

运行结果

2021-01-10 12:03:50.512488+0800 AserbaosIOS[10840:3062752] printStr 调用方法的第一种方式

2021-01-10 12:03:50.512725+0800 AserbaosIOS[10840:3062752] printStr 调用方法的第二种方式

2021-01-10 12:03:50.513024+0800 AserbaosIOS[10840:3062752] printStr 通过获取方法签名的方式调用方法

2021-01-10 12:03:51.514044+0800 AserbaosIOS[10840:3062752] printStr 延时1s调用showLong方法
小建议
  • 如果方法只有一个参数,命名可以为xxxWithxxx,增加代码可读性
  • 如果有多个参数可以命名为:WithxxxAndxxxAndxxx.

协议

protocol 类似于java中的接口,可以定义一些基础实现类共有的方法,只需要遵循这个协议,就可以拥有这些方法并且可以自己去编写对应的实现方法。这样可以避免代码的冗余。

比如我们有一个ITask的协议,其中init的方法是所有子类必须得实现的,setPriority实现可选。我们可以这么写:

 @protocol ITask <NSObject>

 @required

- (int) init;

 @optional

- (int) setPriority;

 @end

代理

通过代理来接收UI的触发事件,以UIScrollView为例:

  1. 设置UIScrollView的代理对象(当前控制器ViewController):
  2. 让ViewController遵守UIScrollViewDelegate的代理协议:
// 直接在实现文件里面声明就可以了.

 @interface ViewController ()<UIScrollViewDelegate>
  1. 在ViewController中重写需要监听的方法,具体的方法直接看代理协议里面的方法既可:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

    NSString *pointStr = NSStringFromCGPoint(scrollView.contentOffset);

    NSLog(@"正在滚动中…… %@",pointStr);

}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{

    NSLog(@"开始拖拽……");

}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

    NSLog(@"停止拖拽……");

}

其他

block

参考链接:fuckingblocksyntax.com/

语法:
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
作用:

在iOS中,在以下情况下通常使用block:

  • 代替代理和委托方法
  • 作为回调函数的替代
  • 一次性编写多次处理的任务
  • 方便对集合中的所有项执行任务
  • 与GCD队列一起执行异步任务
用法:

举个简单拍照录像回调的例子:

声明一个ASCameraVideoAndPhoto的类处理相机的视频和拍照。我们在拍照完成之后,需要在调用ASCameraVideoAndPhoto的地方获取到当前的视频和拍照的成功返,来实现通过block代替回调函数的功能。

第一步:在ASCameraVideoAndPhoto.h 中声明函数里面添加两个block.

  • didRecord:(void (^)(NSURL *outputFileUrl, NSError *error))completionBlock: 拍视频成功回调
  • didTakeSuccess:(void (^)(void))completionTakeBlock: 处理拍照成功的回调
#import <UIKit/UIKit.h>

 @interface ASCameraVideoAndPhoto : UIView

//completionBlock 用来拍视频成功回调,

//completionTakeBlock: 用来处理拍照成功的回调

- (instancetype)initWithFrame:(CGRect)frame withPositionDevice:(BOOL)isBack didRecord:(void (^)(NSURL *outputFileUrl, NSError *error))completionBlock didTakeSuccess:(void (^)(void))completionTakeBlock;

 @end

第二步:我们在ASCameraVideoAndPhoto.m实现initWithFrame方法:

 @interface ASCameraVideoAndPhoto()<AVCaptureFileOutputRecordingDelegate>

// 声明一个Block的属性,didRecordCompletionBlock用来引用传入的录像完成的block

 @property (nonatomic, copy) void (^didRecordCompletionBlock)(NSURL *outputFileUrl, NSError *error);

//声明一个Block的属性,didRecordCompletionBlock用来引用传入的拍照成的block

 @property (nonatomic,copy) void(^didTakeCompletionBlock)(void);

 @end

 @implementation ASCameraVideoAndPhoto

- (instancetype)initWithFrame:(CGRect)frame withPositionDevice:(BOOL)isBack didRecord:(void (^)(NSURL *outputFileUrl, NSError *error))completionBlock didTakeSuccess:(void (^)(void))completionTakeBlock{

        self.didRecordCompletionBlock = completionBlock; 

        self.didTakeCompletionBlock = completionTakeBlock;

}

 @end

/// 录像完成的回调

- (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections error:(NSError *)error{

    if(self.didRecordCompletionBlock){

        //调用录像完成的引用,达到回调的效果

        self.didRecordCompletionBlock(outputFileURL,error);

    }

}

第三步:在调用ASCameraVideoAndPhoto的地方处理对应的block。

/// 添加视频拍摄和照片获取

- (void)addCameraVideoAndCapture{

    ASCameraVideoAndPhoto *asCameraVideoAndCapture = [[ASCameraVideoAndPhoto alloc]initWithFrame:[UIScreen mainScreen].bounds withPositionDevice:YES didRecord:^(NSURL *outputFileUrl, NSError *error) {

        NSLog(@"视频拍摄成功了 保存路径在:%@",outputFileUrl);

    } didTakeSuccess:^{

        NSLog(@"拍照成功了");

    }];

    [self.view addSubview:asCameraVideoAndCapture];

}

ok,大功告成,拍照的成功之后在ASCameraVideoAndPhoto.m中调用block的引用就可以将对应的消息回调到外部了。

我们也可以直接将block用typedef事先声明好,效果一样,可读性会更高一点。

typedef

typedef的主要作用用来定义自己习惯的数据类型名称,可以用来替代系统的基本类型名称,也可以允许定义自己的数据类型名称。

比如:我们可以自定义一个ABOOL,当ABOOL = 1 时表示true,ABOOL = 0时表示false

typedef int BOOL;

#define TRUE 1

#define FALSE 0

下面讲下自定义方法,我们通过typedef来定义一个TakePhotoSuccess的方法,这样就可以将TakePhotoSuccess作为参数进行传递:

我们在.h文件中添加TakePhotoSuccess的定义:

typedef void(^TakePhotoSuccess)(void);

在.m文件中函数中将TakePhotoSuccess作为参数传递:

//

 TakePhotoSuccess _takePhotoSuccess;

- (instancetype)initWithFrame:(TakePhotoSuccess)takePhotoSuccess{

    _takePhotoSuccess = takePhotoSuccess;// 内部持有takePhotoSuccess方法的引用

}

在外部调用initWithFrame这个方法时,我们可以实现内部方法的一些回调。比如当拍照成功之后,我在.m文件内部就可以调用takePhotoSuccess这个方法,告诉调用initWithFrame对象,我已经拍照成功了,你可以做一些你需要做的事情了。