类
类的声明
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为例:
- 设置UIScrollView的代理对象(当前控制器ViewController):
- 让ViewController遵守UIScrollViewDelegate的代理协议:
// 直接在实现文件里面声明就可以了.
@interface ViewController ()<UIScrollViewDelegate>
- 在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
语法:
@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对象,我已经拍照成功了,你可以做一些你需要做的事情了。