.m文件的实现,这绝对符合Objective-C完美主义开发

作者: mgm娱乐网址  发布:2019-12-21

图片 1

前言

自从苹果在WWDC2014上推出Swift语言后,国外iOS开发社区纷纷转向苹果推崇的iOS开发语言新贵. 而由于Swift语言存在时间不长,其所对应的iOS开源库远不及Objective-C的开源库多,因此在iOS平台开发语言的过渡中,Objective-C与Swift混编的项目也越来越多,而为了能让Objective-C和Swift更安全地兼容在iOS应用中,苹果不断优化Xcode编译器LLVM,为Objective-C语言增加了不少新特性,使其桥接到Swift代码时更加安全.

接下来就来看看Swift出现之后的这些Objective-C新特性,以及如何更好应用到自己的Objective-C代码中吧*(下文都以OC简称)*.

  • Objective-C新增的Nullability声明
  • 数组,字典等集合类的轻量级泛型声明
  • Objective-C 类属性的使用

2017.07.05 更新

当众人的目光聚焦在WWDC 2015新推出的Swift 2和iOS 9上时,我的思绪却飘回到办公室书架上。多年前,初入iOS开发时买的Objective-C指导书直至今日还静静地躺在那里,求知若渴地翻动着书页的场景历历在目,心中所想的不是Objective-C的优点,却是它的局限——如今这位老友旧貌换新颜,以往的“局限”不复存在。2015年Objective-C都有哪些提升?这篇文章即将揭晓答案。The Setup下面的代码你们一定再熟悉不过了,我们来重温一下吧:

Objective-C Nullability类型声明

针对引用对象,OC语言都将其可以设置nil,而常常对nil的对象访问和调用方法时会就出现问题,严重地还会直接造成应用Crash; 而Swift为引用对象引入Optional类型的概念,只有Optional类型的对象才能被设置为nil,否则就会编译为类型错误,当对Optional类型的对象为nil值的情况下仍强行访问该对象就会导致程序的Crash,也就是强制解包Opional类型对象值为nil的情况,这也是写Swift代码中要极力避免的.

为了保证OC程序的安全性以及更安全地向Swift进行桥接,OC出现nonnull,_Nonnull和nullable,_Nullable等一系列Nullability关键字,而这也对应在Swift中表示了非Optional和Optional的对象.现在(Xcode 7之后)无论声明属性还是声明变量,使用新增的这几个关键字,就能显示告诉编译器哪个对象可以被赋为nil,哪个不能被赋值为nil.

首先来看看是使用在代码中使用这些关键字的吧.

@interface Employee : NSObject
@property (nonnull, nonatomic, copy)NSString *name;
@property (nullable, nonatomic, copy)NSString *company;

- (void)workInPlace:(NSString * _Nonnull)place;
- (nullable instancetype)initWithName:(NSString * _Nonnull)name company:(NSString * _Nullable)company;
@end

这里nonnull和nullable是针对属性,返回值声明Nullability的关键字,声明时与nonatomic和copy一样都为属性的修饰符;而_Nonnull和_Nullable是针对局部变量, 参数变量来声明时使用的,注意的是该类关键字********声明顺序一定要在指针符号*********的后面********, 否则编译器无法识别该关键字.

这样声明后一旦声明了nonull/_Nullable的属性被赋值为nil时,编译器就给警告提示,告诉开发者哪些变量属性被要求不能传nill.借此,我们可以对明确清楚不会为nil的对象使用该关键字,来可以更加保证程序的安全性以及语言规范.

图片 2

screenshot.png

除此之外,属性除了设置nonnull和nullable外,还提供null_resettable关键字,而这个是用来表示该属性在setter时可设为nil,而在getter时是不为nil的,比如UIViewController的View视图就是用这个关键字声明的.当控制器的view被设置为nil时,使用getter方法访问就会触发loadView方法,创建一个视图对象返回.

@property(null_resettable, nonatomic,strong) UIView *view;

有了Nullability关键字声明后,官方推荐将属性尽可能声明为nonnull,少量根据需要设置为nullable,并且现在系统SDK也是这么做的,只不过将大量属性都添加声明nonnull时显得十分麻烦,官方巧妙地提供一对宏NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END,来表示默认两个宏之间的属性声明都为nonnull,若需要额外声明为nullable的属性直接进行声明为nullable,覆盖宏默认提供的nonnull声明.而使用宏之后对nonnull修饰的属性设置nil时也一样会警告提示的作用.

NS_ASSUME_NONNULL_BEGIN
@interface Employee : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, copy)NSString *home;
@property (nonatomic, copy)NSString *skill;
@property (nullable, nonatomic, copy)NSString *company;

- (void)workInPlace:(NSString * _Nonnull)place;
- (nonnull instancetype)initWithName:(NSString * _Nonnull)name company:(NSString * _Nullable)company;
@end
NS_ASSUME_NONNULL_END

Class Properties

Xcode 8.0对于Objective-C唯一的好消息可能只有支持类属性了。首先看下UIColor和UIApplication头文件

  • UIColor
@property(class, nonatomic, readonly) UIColor *blackColor;
  • UIApplication
@property(class, nonatomic, readonly) UIApplication *sharedApplication;

苹果把没有参数,有返回值的类方法改为类属性,其中分为非单例类属性(UIColor)和单例类属性(UIApplication),类属性可以通过点语法直接访问

UIColor *color = UIColor.redColor;
UIApplication *app = UIApplication.sharedApplication;
@property (strong, nonatomic) NSArray *someViews; 

集合类的轻量级泛型声明

Xcode 7之后由于编译器LLVM7.0的升级支持,在集合相关的声明和使用上,如NSArray,NSDictionary等提供了轻量泛型的新语法,这样使用集合对象,而从该集合里取出的对象类型也不再是id, 而是具体的对象类型.

//局部变量声明
NSArray<Employee *> *programmer = @[
       [[Employee alloc]initWithName:@"A" company:nil],
       [[Employee alloc]initWithName:@"B" company:nil],
       [[Employee alloc]initWithName:@"C" company:nil],
       [[Employee alloc]initWithName:@"D" company:nil],       
       ];

// 属性声明
@property (nonatomic, strong) NSMutableArray<Employee *> *employees       

由于明确知道了集合内部元素的对象类型,系统还提供了该对象所属类的相关属性和方法的代码提示,这样就方便直接使用该对象.这样一来不但更加明确代码的语义,让开发者看见集合声明就能明白内部的对象类型,而且减少了id出现,避免重复的id类型显示转换操作以及访问id类型对象的安全问题,可谓是一举多得.

图片 3

screenshot.png

使用了轻量泛型特性的集合对象在添加其他类型的对象时,就会引起编译器的警告,来提示开发者传的参数类型不一致: 你真的打算把这个对象放入这个集合吗???]

图片 4

screenshot.png

轻量泛型的语法还提供一个比较有用的修饰符:****__kindof****,在官方SDK中不少用到了这个新修饰符,首先看看系统是如何声明使用它的.

// UIView.h
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews;
- (nullable __kindof UIView *)viewWithTag:(NSInteger)tag;

这样声明之后, 无论从属性还是方法获得的对象都会得到与该类型相关的子类或者就是该类型,对应上面就是UIView类型或者其子类,而不再是以前的id类型,这样避免了不必要的显示类型转换,直接访问也不会任何类型警告,这一优点用在UITableview的dequeueReusableCellWithIdentifier:方法上更加明显.

图片 5

screenshot.png

类属性的实现

由于类属性不会合成,我们需要自己实现getter,setter方法

  • 非单例类属性

Person类添加一个新的属性

@property (class, nonatomic, assign) NSUInteger averageIQ;

.m文件的实现

static NSUInteger _averageIQ = 0;

+ (void)setAverageIQ:(NSUInteger)averageIQ
{
    if (_averageIQ != averageIQ) {

        _averageIQ = averageIQ;

    }
}

+ (NSUInteger)averageIQ
{
    if (_averageIQ == 0) {

        _averageIQ = 100;

    }

    return _averageIQ;
}
  • 单例类属性

创建DataSource类,单例

@property (class, nonatomic, readonly) DataSource *sharedDataSource;

.m文件的实现

static DataSource *_dataSource = nil;

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _dataSource = [super allocWithZone:zone];

    });

    return _dataSource;
}

+ (DataSource *)sharedDataSource
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        _dataSource = [[self alloc] init];

    });

    return _dataSource;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _dataSource;
}

Objective-C作为一门历史悠久的语言,迫切需要新的变化。Xcode 7.0的发布,终于使得Objective-C与时俱进了一些,也为了和Swift进行更好的互通。这篇文章根据WWDC 2015-Session 401-Swift and Objective-C Interoperability和官方文档对Objective-C新特性进行全面解读

这绝对符合Objective-C完美主义开发者的标准。对它表示的属性,不同人有不同观点。但是,其中仍然存在着一些难以察觉的缺陷。是否可能返回nil?除非有现成的文件,或开发者全程都在一旁,否则光凭看是无法获取信息的。除了UIView之外还有什么?还是那句话——不确定。也许答案是reflection? 或许问题可以改成:除了UIView,有可能出现UIView子类吗?看样子会出现诸多转换因为是一队列……东西,知道那东西是什么之后,经过cast后才能利用。会弱化Swift代码和可读性很遗憾,Swift支持泛型就意味着只会以optional的AnyObject集合的形式出现。如此一来,开发者要使用该属性就必须在Swift和Objective-C之间进行转换。Nullability Annotations单单一个属性就引发了这么多担忧,还挺让人不安的。如果代码本身引发很多质疑,出现error的可能性就大大增加,更别提在广为熟知的Objective-C和语言新秀Swift之间相互调用了。现在有了nullability annotations——我最爱的Objective-C新功能之一,问题就简单多了,编程也会省下很多麻烦。intent.现在谈到API,可能会,也可能不会返回nil。简而言之,终于不用花费数小时来排除漏洞了。以下有三个选项:nullable — Think UIView?nonnull — Think UIViewnull_unspecified — Think UIView!再回到实例属性。假设在运行时迭代这个属性来创建某个用户界面,在相应的位置应该有UIButton和UIView。但是,天哪!——不论怎么样它们也不应该是nil啊。现在出现如下的信息:

Objective-C 类属性的使用

在OC中,我们都会将有重要的成员变量作为属性声明来使用,而Xcode 8之后(目前Xcode8还是测试版本)为OC提供支持类属性的声明和使用,并且与Swift混编时该属性桥接成Swift的类属性.(比较坑的是光声明了类属性后,编译器并不会去自动生成setter和getter方法,这些方法都需要开发者自己实现)
首先声明类属性时,需要添加上class关键字修饰该属性,接下来就是在.m文件进行setter和getter方法的实现了.

@interface User : NSObject
@property (class, nonatomic, assign, readonly) NSInteger userCount;
@end

@implementation User
static NSInteger _userCount = 1;

+ (NSInteger)userCount {
  return _userCount;
}

+ (void)setUserCount:(NSInterger)userCount {
 _userCount = userCount;
}

由于类对象的特殊性,其setter和getter方法本质就是类方法,就直接无法使用成员变量,因此只能在类中用static声明对应属性的全局变量,只初始化一次,来保证类属性值生命周期.
在通过类属性的声明以及setter和getter方法的实现后,我们就可以向像Swift一样使用点语法访问类属性了.(分明是向Swift靠拢...)

int main(int argc, char * argv[]) {
 [User setUserCount: 2];
 NSLog(@"User count: %ld",(long)User.userCount); // 2
}

Nullability

考虑以下代码

@property (nonatomic, copy) NSString *givenName;

@property (nonatomic, copy) NSString *familyName;

@property (nonatomic, strong) NSNumber *height;

@property (nonatomic, strong) NSNumber *weight;

由于Objective-C中没有optionals的概念,Swift不知道这些属性是否为空,这些属性映射为implicitly unwrapped optional

open var givenName: String!

open var familyName: String!

open var height: NSNumber!

open var weight: NSNumber!

Nullability特性最早加入是在Xcode 6.3。那个时候,我们可以看到nonnullnullable或者双下划线小写字母开头__nonnull__nullable。由于和第三方库存在潜在的冲突,在Xcode 7.0,苹果已经重新命名,之前双下划线小写字母开头成为过去,取而代之的是单下划线大写字母开头_Nonnull_Nullable。为了向下兼容,定义宏__nonnull__nullable替换为_Nonnull_Nullable。可以使用const关键字的地方,就是可以使用_Nonnull,_Nullable的地方,当然只能用于指针

@property (strong, nonatomic, nonnull) NSArray *someViews; 

小结

自Swift发布之后,苹果官方不断改建这个新语言的同时,为了让原有大量的OC项目能够更好向Swift方向迁移,在编译器优化上提供越来越多的特性来帮助OC程序更加安全,有效地桥接到Swift项目上,而利用这些新的特性,不但可以帮助OC程序员更无痛地迁移到Swift中去,而且也更是让我们加强在OC编程中对类型安全的重视,尝试写出更安全,可读性更高的OC代码.

nonnull,_Nonnull

不可以为空,映射为nonoptional

  • 用于属性
@property (nonatomic, copy, nonnull) NSString *givenName;

@property (nonatomic, copy) NSString * _Nonnull familyName;

setter

2A8EA03E-54C9-4F5B-B25B-2D0197FE7C5A.png

getter

D828E8B3-F6F4-4922-9BD8-2A129D0C9B4B.png

点语法

D431A0F6-AEEC-47FB-A41A-627B9D2B2C44.png

可以看到setter,getter,属性都有Nullability的提示

  • 用于方法
- (nonnull instancetype)initWithGivenName:(nonnull NSString *)givenName familyName:(nonnull NSString *)familyName NS_DESIGNATED_INITIALIZER;

54773E96-7E3C-4D68-919E-6180F56F9E44.png

  • 用于block指针
- (void)playWithPerson:(nonnull Person *)person completionHandler:(void (^ _Nonnull)())completionHandler;

85A31CAA-5070-4B78-9DE8-E4F5B7D8ADF2.png

值得注意的是 : block作为参数,没有Nullability的提示,但是指定block为nil,编译器依然会警告

试着让block复杂一点

- (void)playWithPerson:(nonnull Person *)person completionHandler:(NSString * _Nonnull (^ _Nonnull)(NSNumber * _Nonnull tip))completionHandler;

B1C8E455-4E5D-4222-9888-E5E25424F52A.png

可以看到block的参数和返回值都有Nullability的提示并且只能使用下划线的版本表示

  • 用于C指针
void shopWithMoney(CFNumberRef _Nonnull money);

326E7BA0-1ED5-4E5E-9006-8C64272E2E19.png

  • 对于使用nonnull还是_Nonnull

一般来说,Objective-C的属性,方法使用nonnull,其他类型指针使用_Nonnull

本文由mgm娱乐网址发布于mgm娱乐网址,转载请注明出处:.m文件的实现,这绝对符合Objective-C完美主义开发

关键词:

上一篇:没有了
下一篇:没有了