Android开发使用的是Java语言进行编写,当然最近Google的kotlin逐渐变成Android的官方认可的语言。虽然kotlin的语法模式跟Java有些区别但对于一个多年Android开发经验的开发者来说上手比较快,Java和kotlin在Android开发中很多相关的知识点多能套用。出于对移动端开发的热爱,要了解两大移动系统的开发,iOS开发还是挺吸引人的。
大学时的C基础基本快要到忘光的程度,现在想学习iOS开发用到类C的Objective-C。现在对Java C OC三种语言基础的对比学来更快的入门iOS开发,给自己的的技能树添枝加叶。
一.语法基础:
1.标识符/变量/常量:
类型 变量 = 初始值;
标识符与Java/C中命名方式类似,标识符可以用字母/数字/美元符$/下划线组成,标识符开头只能以字母/美元符下划线。
类型说明符 变量 a = 值1;
常量分为:整型常量(12345)/实例常量(1.2345)/常量('a')/字符串常量("abc")/逻辑常量(true/false)
2.局部变量和实例变量:
大致与Java/C中的局部变量/实例标量类似。
#import <Foundation/Foundation.h>
int main(int argc ,const char *argv[]) {
@autoreleasepool {
int a = 10,b,c = 8;
b = a + c;
}
NSLog(@ "a = %d, b = %d, c = %d", a, b, c);
return 0;
}
3.基本数据类型:
oc中基本数据类型跟Java的基本数据类型一致。特殊的类型id,id类型的数据可以储存任何类型的对象属于一般的对象类型。
char,short int,int,long int,float,double,long double,id。
NSNumber类似Java中Integer的数据对象,与Java中的数据类型的解封类似。
NSString对比于Java中String:
NSString *MyString = @"abcd"; //oc
String string = "abcd"; //java
NSString *MyString1 = [[NSString alloc] intWhithString:@"abcd"]; //oc
String string1 = new String("abcd"); //java
NSString *SubString = [MySrting substringToIndex:2]; //oc
String subString = string.subString(0,2); //java
4.循环与选择结构:与Java,C中一样
二.数组和字典:
在Foundation framework的框架中提供三种收集NSObject对象的集合:数组(NSArray),集合(NSSet),字典(NSDictionary);NSArray用于储存有序的对象集合,NSSet用于存储无序的对象集合,NSDictionary用于储存键值对的集合。在iOS中这三类集合类只能储存cocoa对象(NSObject对象,如要保存一些原始C数据,int)就需要将int类型转换为NSObject对象。
1.NSArray固定数组,NSMutableArray可变数组:
NSArray *array = [[NSArray alloc]initWithObjects:@"1",@"2",@"4",nil];
int[] array = new int[1,2,3,4]; //java
NSMutableArray mArray = [[NSMutableArray alloc] initWithObject:@"hello",@"world"];
String[] mString = new String[]{"hello","world"};
NSArray,NSSet,NSDictionary跟Java中的Array,Set,Map集合类型一致:
//数组实例初始化方法
-(instancetype)initWithObjects:(id)fristObj; //直接将数组元素添加到数组中的初始化方法
-(instancetype)initWithArray:(NSArray *)array; //通过拷贝另一个数组的方法初始化一个数组
-(NSArray *)initWithContentsOfFile:(NSString *)path; //使用一个文件来创建数组
-(NSArray *) initWithContentsOfURL:(NSURL *)url; //使用一个url链接来创建数组
NSArray *array = [[NSArray alloc]initWithObjects:@"1",@"2",@"4",nil]; //oc类方法创建
//数组类初始化方法
+(instancetype)arrayWithObject:(id)object; //直接将数组中的元素添加到数组中的初始化方法
+(instancetype)arrayWithArray:(NSArry *)array;//通过拷贝另一个数组的方法初始化一个数组
+(NSArray *) arrayWithContentsOfFile:(NSString *)path //使用一个文件来创建数组
+(NSArray *) arrayWithContentsOfURL:(NSURL *)url; //使用一个url链接来创建数组
//打印数组
NSLog(@"array:%@",array1);
for(int i = 0;i < array.count;i++) {
NSLog(@"第%d个元素是:%@",i ,[array] objectAtIndex:i );
}
for(id obj in array){
NSLog(@"%@",obj);
}
//数组排序
NSArray *sortArray = [sortedArrayUsingComparator:^NSComparisonResult(id obj1,obj2){
if([obj1 intValue] > [obj2 intValue]){
return NSOrderedDescending;
}
if([obj1 intValue] < [obj2 intValue]){
return NSOrderedAscending;
}
}]
在oc中实例方法用"-",类方法用"+"表示。类方法不需要实例化对象就可以使用,直接通过类调用,类似Java中static修饰的方法;实例方法,必须通过类创建一个实例对象才能使用。
2.NSSet/NSMutableSet与Java中Set集合用法和定义一致,集合是一种哈希表,运用散列算法,查找集合数据比数组快,但是无序的,存储对象是唯一的不能重复。
NSSet *set = [[NSSet alloc] initWithObjects:@"name",@"age",@"sex",nil];//oc
NSSet *set1 = [NSSet setWithObjects:@"name",@"age",@"sex",nil];
Set set2 = new HsshSet();
set2.add("name");
set2.add("age");
set2.add("set");
3.字典(NSDictionary/NSMutableDictionary)是一种以键值对(key-value)的形式进行存储的存储结构与Java中的Map类似,通过key属性名称来获取与key对应的value,value是一个对象指针,通过valueForKey访问相应的值。
NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys:@"name",@"wang",@"age",@"28",nil]; //oc中的NSDictionary初始化以键值对成对出现
NSLog[@"键值对的个数:%ld",[dic count]]; //查看键值对数量
NSArray *keys = [dic allKeys]; // 查看所有键
NSLog[@"键为:%@",keys];
NSLog[@"name:%@",[dic vauleForKey:@"name"]]; //查看特定键对应的值
NSDictionary *dic1 = [[NSDictionary alloc]init];
dic1 =@{@"name",@"wang",@"age",@"28"};
NSArray *array = [NSArray arrayWithObjects:dic1];
NSLog(@"age:%@",[array vauleForKey:@"age"]);
三.循环与选择结构:
for,while,do while,return,break,continue与Java中的用法一样。
四.面向对象方法实现:
1.对象/类/方法:
对象就是一个实体,能感受到具体事物。类是对对象的抽象,方法是将动作进行定义和使用。
@interface申明接口,@implementation用于实现接口
接口部分的申明:
@interface NewClassName : ParentClassName
{
memberDeclarations;
}
memberDeclarations;
@end
类和实例方法:
oc中有两种方法类型,开头是符号"-"代表该方法是一个实例方法,而开头是符号"+"则代表该方法是类方法。
//方法类型(返回值) 方法名称 (参数类型) 参数名
-(void) setNumber: (int) n;
-(void) intWithNumber: (int) n andAge:(int) a;
void setNumber(int n); //java
void intWithNumber(int n,int a);
2.继承(@Property)属性和点语法:
@Property点语法调用方法:
声明@Property属性之后,在.m实现文件中要使用@synthesize方法来完成这个方法。
@Property属性中有多种参数可供选择:
readonly:只产生简单的getter方法,没有setter方法;
retain:setter方法对参数进行release旧值,再retain新值;
nonatomaic:禁止多线程,保护变量;
assign:默认类型,setter方法直接赋值,而不进行retain操作。
@interface Number1 : NSObject
{
int Mynumber1;
float Mynumber2;
}
@property(nonatomic) int Mynumber1;
@property(nonatomic) float Mynumber2;
- (void)print;
@end
#import "Number1.h"
@implementation Number1
@synthesize Mynumber1,Mynumber2;
-(void)print
{
NSLog(@"Mynumber1 is %d,Mynumber2 is %.lf",Mynumber1,Mynumber2);
}
@end
//调用
Number1 *number = [[Number1 alloc]init];
number.Mynumber1 = 10;
number.Mynumber2 = 12.090;
[number print];
2.类的继承:
NSObject类是所有类的父类(与Java中Object定义类似),子类能够继承父类的实例变量和方法,子类可以直接访问这些方法和实例变量,就像直接在类中定义了一样。private与Java中一样定义为私有变量及方法;@public定义的实例变量不仅可以在本类的方法使用;@Property实例变量(默认情况下)可被该类及任何子类中定义的方法直接访问。
3.多态:
多态的表现:有继承关系,有方法重写,父类的申明变量指向子类对象(与Java的多态一致)。
4.动态类型:
oc 中除了基本的数据类型之外,还有一种特殊的数据类型,那就是动态类型。id类型可以存储任何类型的对象,在声明了id类型的变量后,Number可以存储任何类型的对象,可以声明一个具体有id类型返回值的方法,用于创建实例。id类型不仅仅可以定义变量,也可以定义方法,使方法的返回值是动态的。
id Number;
-id NewNumbers : (int)number;
id 声明对象类型时没有使用“*”。因为id类型,其可以来保存程序中任何类型的对象。oc的系统总是跟踪对象所属的类,运行时先判定对象的所属的类,然后在运行时再确定需要动态调用的方法,而不是在编译的时候。
5.对象的复制:
假设一个对象中拥有一个数组对象,现在又需要生成一个对象,同时将现有的对象赋值给这个新对象,那么这两个对象中的数组对象是同一个,也就是说,当一个对象中对数组进行修改时,那么另一个对象的数组对象也会同时被修改,也就是说这两个对象中的数组对象是共享的。
retain的概念与copy类似,retain可以快速创建对象,但retain只是使原对象的引用计数+1,并没有创建新的对象,两个对象指针相同,指向同一个对象。copy方法用于不可变数值,mutableCopy方法用于可变数组。
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:@"one",@"two", nil];
NSMutableArray *myArray1 = [myArray retain];
if (myArray == myArray1) {
NSLog(@"myArray == myArray1");
NSLog(@"myArray引用计数为:%ld",myArray.retainCount);
}
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"three"@"four", nil];
NSMutableArray *array2 = [array1 copy];
if (array1 != array2) {
NSLog(@"array1 != array2");
NSLog(@"array1引用计数为:%ld",array1.retainCount);
}
深拷贝与浅拷贝:
深拷贝:拷贝属性对象的所有内容;
浅拷贝:只拷贝所有属性对象的指针。
6.iOS中的内存管理:
- Java中为用户提供了自动内存管理的机制,但在iPhone开发中需要开发者手动管理内存。当程序运行时占有的内存都是取决于RAM的,操作系统启动应用时,会为应用保留一部分空闲的RAM,称为堆区。在创建实例时,会从堆区中取出一小块供其使用,当不需要使用对应的对象时,应该及时释放分配给它的内存。
- 在oc中使用alloc方法为所有的Objective-C类创建实例,但是当为实例分配内存之后,还不能使用,要通过init方法将实例化对象进行初始化。init方法是一个实例方法,一个类中会包含有多个init方法,这些方法都是以init开头的。在Objective-C中局部变量时放在栈区中,而alloc的内存区域时在堆区中。
在重写init方法时,会调用父类的初始化方法,通过“[super init]”,init方法返回的值时id类型,描述了被初始化的对象。
-(id)init
{
self = [super init];
if(self) {
//初始化代码
}
}
如果父类初始化失败,则会返回nil,就无法进行当前类的初始化操作。我们也可以这样理解上述代码,init方法完成对子类对象的初始化,可以将此工作分为两个部分:继承父类的对象的初始化,子类对象本身对象的初始化,所以“[super init]”其实就是父类对象的初始化,而在中括号内才是对子类自身的对象进行初始化。
在分配内存之后如果想将内存还给堆区,可以通过dealloc方法来实现,当对象收到dealloc消息时,会将其占用的内存还给堆区。在oc中不能直接向对象发送dealloc方法,只能由对象自己向自己发送dealloc消息。
- 引用计数:
Cocoa Touch框架采用手动引用计数(mrc)来管理内存,对象不知道具体的拥有方,只是知道拥有方的个数,当拥有方(引用计数)为零时,就调用deallloc方法来释放内存。
retain计数和release管理内存:
对象通过retain计数知道拥有方的个数,当用户创建一个实例后,对象将得到一个拥有方,此时retain计数值为1;如果又有一个拥有方加入,则retain计数加1;当对象失去一个拥有方时,会收到release消息,此时的retain计数减1;当retain计数为0时,对象就会向自己发送一个dealloc消息,释放相应的内存,还有重要一点,当引用计数为0时,对象就不能使用release和其他方法,否则系统会出现内存方法的错误。
实例变量的属性:
(1).assign:对基础数据类型(NSInteger)和c类型数据(int/float/char)使用assign属性只是简单的赋值,不会改变引用计数;
(2).copy:copy属性用NSString对象,它将创建一个引用计数为1的对象,然后释放原先的旧对象;
(3).retain:用于其他NSObject和NSObject子类。当添加retain属性时,将释放旧的对象,将对象的值赋予输入对象,再提高输入对象的引用计数为1。
NSString *myString = [[NSString alloc]initWithString:@"hello"];
以上代码执行时会完成两个操作,因为alloc了一块内存区域,所以会在堆区分配一段内存来存储字符串(例如内存地址为OX1234,内容为"hello")。同时会在栈区分配一段内存来存储字符串对象myString(例如地址OXABCD,内容为堆区地址OX1234)。
NSString *myString1 = [myString assign];
当用assign属性时,myString和myString1完全相同,地址都是OXABCD,内容为OX1234,对这两任何一个进行操作均相当于对另一个进行操作,所以引用计数不改变。
NSString *myString2 = [myString retain];
当使用retain属性时,myString和myString2地址不再一样,但内容一样,为OX1234。myString和myString2都是共用@"hello"所在的内容,因此引用计数要加1。
NSString *myString3 = [myString copy];
此时会在堆区分配一段内存存储字符串@"hello",为myString3字符串对象和@''hello"重新分配地址,引用计数要加1。
- 自动释放池
autoreleasepool提供一种延迟释放的功能,实际上是把堆release的延迟调用,对于每一个autorelease,系统只是把该对象放入当前的autoreleasepool中,当自动释放池北释放是,pool中的所有对象会调用release。它的作用就是可以做到是每一个函数对自己申请的对象负责,即自己申请,自己释放,该函数的调用者不需要关心其内部申请对象的管理。
要使用autorelease命令,首先要手动创建一个自动释放池:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
Person *person = [[[Person alloc]init]autorelease];//设置了autorelease,不需要对person进行release了。
当autorelease销毁时,会遍历其内部release数组中的每一个成员,如数组中某元素的引用计数为1,那么经过[pool drain]后的值变为0,则该元素被销毁。在代码中标记autorelease的对象只有在程序结束时才会被销毁,它体现了延迟释放的特点。