跟着Android 学习 ios —— oc 的基础语法

361 阅读8分钟

  • Android 中.java 为一个类文件
  • oc 中 .h和.m 和为一个类文件

.h 文件

声明文件只做属性声明,函数声明,不做具体逻辑实现,以@interface 声明

  • 方法声明

+(int)personWithA1:(float)a1 andA2:(NSString*)a2;

  • 代表可以通过 类名调用,类似于java 的static,属于类方法 调用:[Person personWithA1:2 andA2:@"类方法"];//调用对象是类名

-(int)personWithA1:(float)a1 andA2:(NSString*)a2;

代码对象方法和java 的正常方法一样
Person * obj1=[[Person alloc]init];
[obj1 personWithA1:1 andA2:@"对象方法"];//调用对象是类对象

  • 属性声明

@property int age;

@property 为oc 属性申明。和Java 属性变量和声明变量一样
oc隐士为这个属性做了get 和set 方法,新版的Java 也是隐士调用了

  • 成员变量声明: int i;
    不能有初始值只能在方法函数中传递;

  • 属性获取:

people.name=@"ZhangSan";  等价:[people setName:@"ZhangSan"];
NSString * theName=people.name; 等价:NSString * theName=[people getName];
这些语法和Java 是一样的

java 中属性声明是包括函数和成员变量,oc 是分开的

.m文件

为具体的函数实现和逻辑的处理,为类扩展文件 例如:

//对类声明中的类方法做实现\
+(int)personWithA1:(float)a1 andA2:(NSString*)a2{\
    NSLog(@"%f,%@",a1,a2);\
    return 0;\
}\
\
//对类声明中的对象方法做实现\
-(int)personWithA1:(float)a1 andA2:(NSString*)a2{\
    NSLog(@"%f,%@",a1,a2);\
    return 0;\
}\
\
//对类扩展中的对象方法做实现\
-(void)display{\
    NSLog(@"类扩展中声明的方法");\
}

类的导入

引入编程语言提供的头文件的写法是:

#import <文件名>

引入自己写的文件的写法是:

#import "文件名"

引入的文件必须是.h文件,引入.m文件会报错,类在.h文件和.m文件中都可以引入头文件,区别是:在.h文件中引入头文件,.h和.m文件都可以使用这个文件中的资源;如果在.m文件引入头文件,.h文件不能使用该文件中的资源。 所以,在引入头文件的时候,需要考虑在.h文件中当声明类的时候,是否使用该头文件中包含的资源。如果使用,就在.h文件中引入;反之,在.m文件中引入即可。类似java import ,

self 和super
  • self 在使用OC语言创建一个类时,底层就为我们创建好了一个self,它的使用范围仅限于在本类中(在类的实现部分)。

    self代表当前方法的调用者。在OC的类中,实现部分全部都是方法(声明部分无法使用self),有类方法和对象方法。如果外界对象调用对象方法时,self就代表这个对象;同样,调用类方法时,self就代表这个类。

    所以,这句话简单的理解,就是:如果self所在的这个方法是类方法,那么self就代表当前类;如果是对象方法,self就代表当前类的对象。

    在self表示本类对象的时候,就具备类对象的所有功能,可以使用self来调用类中的属性,例如:
    self.name=@"ZhangSan";

    当self表示本类的时候,可以用来调用其他类方法,相当于java 的this

  • super

super和self的使用规则一样,不同之处在于:super指父类方法的调用者。而self指当前方法的调用者。相当于java 的super

@property的几种常用修饰词

  • nonatomic:原名:非原子性,它涉及到多线程的相关知识。在这里我们只需要知道有nonatomic声明的属性,任何人任何地点任何时间都可以调用它(就是很多人可以同时使用它),所以它的缺点就是不安全,而优点就是效率高,访问速度快。
  • atomic(默认):原子性。和nonatomic正好相反,他对属性的访问进行了限制,保护了声明属性的安全,缺点就是:访问效率低,速度慢。:可以类比java volatile
  • copy:一般使用于字符串,它的特点如同它的中文翻译,将内容另外拷贝一份,保存在一个单独的存储空间中
  • strong:强引用 类比java 强引用
  • weak:弱引用 类比java 弱引用
  • assign:我认为是其中最简单的,它应用于OC的基础数据类型(NSInteger、BOOL)和C语言中的基本数据类型(int,float,double,char)
  • readwrite: 它代表这个属性即可以读,也可以写(可以调用它的get和set方法)
  • readonly:和readwrite不同,它代表这个属性只能读,不能进行写操作(只能调用get方法,不能调用set方法)

OC的动态特性

格意思函数调用的叫法是不正确的,它走得消息机制,类似发送和接收,和>java 的对象函数key -v机制不一样,这也是为什么oc 不容易崩溃的原因,减少空对象

  • 动态类型:指的就是:id类型。它可以表示任何类的对象。当我们无法知道数据的具体类型时,我们就可以将数据直接存储到id类型的对象中。

    但是需要说明的是id是动态类型绝不仅仅因为它能够存储任何类的对象。主要的原因是:由于id类型在程序编译时无法确定其中具体存储的对象类型,只有在程序通过编译到达运行阶段时,才能确定id类型对象所存储的具体对象的类型。类比java 的泛型
  • 动态绑定: 动态绑定,是基于动态类型的,简单的理解就是:只有在程序运行时才能确定对象的具体属性和方法,进而进行绑定。

    当程序中包含id动态类型时,编译过程无法确定id类型代表哪个类的对象,也就无法确定id类型调用的是哪个方法。在程序编译阶段,编译器会在你的工程文件中查找id对象调用方法的方法名是不是存在,如果存在,编译通过;反之,程序报错,提示你没有找到对象的方法。\
  • 动态加载: 动态加载,简单的理解就是在需要时才会加载

OC 的类别

类别的用途: 简单的说,就是如果我们想在一个类中增加一些可供外界调用的方法,但是又不想影响它的子类,我们就要用类别。类别能够在原有类的基础上添加新的方法,而且还不会让子类继承。这也是类别的优点所在

类别的创建:
原始类:

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,copy)NSString * name;
@property (nonatomic,assign)int age;
-(void)run;
@end

#import "Person.h"
@implementation Person
-(void)run{
       NSLog(@"the person is running!");
}
@end

类别类

#import "Person.h"
@interface Person (NewFuntion)
//添加方法
@end

#import "Person+NewFuntion.h"
@implementation Person (NewFuntion)
//实现方法
@end


使用的时候和正常的类使用是一致的

还是java好,主动权还是应该给到开发者,虽然这种方式做到了代码隔离

OC中的协议

和java 的接口是一样的,只是相关书写规则有差异;

#import <Foundation/Foundation.h>
@protocol ChildrenDelegate <NSObject>//协议声明
-(void)eat;
@end
@interface Children : NSObject
@property(nonatomic,weak)id<ChildrenDelegate>delegete;
@end  

@protocol是定义协议的关键字,告诉编译器,要开始声明协议了。
ChildrenDelegate是协议的名字,规范命名为:类名+Delegate,多个协议在 <>用, 分开

在协议中,有几个方法的修饰词供我们使用:
@required(表示以下方法必须实现)
@optional(表示以下方法可以不实现)
如果不使用任何关键字修饰,默认情况下是@required。

使用和java 很像不再叙述

OC中的单例

和 java 的一样

h
#import <Foundation/Foundation.h>
@interface Car : NSObject
@property (nonatomic,assign)int driveHours;
+(instancetype)car;
@end

m
#import "Car.h"
static Car * car=nil; // 和java 的null类似
@implementation Car
+(instancetype)car{ //instancetype 和id 一样只是作用在方法上面
         if (car==nil) {
             car=[[Car alloc] init];
         }
         return car;
}
@end

oc 的常用数据类型

  • OC对应-> JAVA 对应
  • NSString -> String
  • NSArray -> []
  • NSDictionary -> Map
  • int -> int
  • float -> float
  • char -> char
  • bool -> bool
  • NSNumber->integer
  • NSValue -> 结构体转成对象

OC中的通知

类似Android 的广播接受器

#import "Worker.h"
@implementation Worker
-(instancetype)init{// 类似java的 构造函数
    if (self=[super init]) {
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(makeCar) name:@"canMake" object:nil];
    }  
    return self;
}
-(void)makeCar{
    NSLog(@"Let's begin to make car,gogogo");
}
@end
#import <Foundation/Foundation.h>
#import "Worker.h"
int main(int argc, const char * argv[]) {
    Worker * worker=[[Worker alloc] init];
[[NSNotificationCenter defaultCenter]postNotificationName:@"canMake" object:nil];
    [[NSNotificationCenter defaultCenter]removeObserver:worker name:@"canMake" object:nil];
    return 0;
}

KVC和KVO

  • kvc Key-Value-Coding,它是一种间接调取对象属性的方法。它的实现方式是通过字符串来自动找到要更改的对象属性。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,copy)NSString * name;
@property (nonatomic,assign)int age;
@property (nonatomic,assign)int sex;
@end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    Person * person=[[Person alloc] init];
    [person setValue:@"ZhangSan" forKey:@"name"];
    [person setValue:[NSNumber numberWithInt:10] forKey:@"age"];
    [person setValue:[NSNumber numberWithBool:YES] forKey:@"sex"];
    NSLog(@"The person's name is :%@,and age is :%d, sex is :%d",person.name,person.age,person.sex);
    return 0;
}
  • kvo

Key-Value-Observer,键值观察(或键值监听)) 和Android databing 类似

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,copy)NSString * name;
@property (nonatomic,assign)int age;
@end
#import "Person.h"
@implementation Person
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
                  Person * person=[[Person alloc] init];
        person.name=@"ZhangSan";
        person.age=10;
        [person addObserver:person forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        [person addObserver:person forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        person.name=@"LiSi";
       person.age=20;
       [person removeObserver:person forKeyPath:@"name" context:nil];
       [person removeObserver:person forKeyPath:@"age" context:nil];
       return 0;
}

Block代码

类似java中 匿名内部类和js 的闭包:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
void (^helloWorld)(void);
  // void 返回类型, (^helloWorld) 代码块称^ BLOCK标识,void 参数
     helloWorld=^(void){   //helloWorld 名字^ 标识(void) 参数
     NSLog(@"Hello World!");
     };
    helloWorld();//调用
    return 0;
}

使用示例:

#import <Foundation/Foundation.h>
typedef void(^myBlock)(NSString * name,int age);
@interface Person : NSObject
-(void)exercise:(myBlock)block;
@end
#import "Person.h"
@implementation Person
-(void)exercise:(myBlock)block{
    NSString * theName=@"ZhangSan";
    int age=10;
    block(theName,age);
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    Person * person=[[Person alloc] init];
    [person exercise:^(NSString *name, int age) {
        NSLog(@"%@,%d",name,age);// ZHANGSAN 10
    }];
    return 0;
}

内存管理

  • ARC->GC
  • 自动引用计数-> JAVA 自动引用计数