object-c 基础学习(-)

内容基本是《object-c基础教程》的,感谢国家,感谢作者。

前几章的总结。
object-c是添加了面向对象特性的C语言。
文件扩展名为.m,m代表message,是object-c语言的一个主要特性。
文件开头使用头文件包含元素声明,C语言中使用#include包含头文件,而objc中使用#import <Foundation/Foundation.h> 可保证头文件只被包含一次。这条语句的含义是告诉编译器查看Foundation框架中的Foundation.h的头文件。
框架是一种聚集在一个单元的部件的集合,包含头文件、库、图像、声音文件等。Foundation框架处理的是用户界面之下的层的特性。比如数据结构和通信机制。
下面第一个例子是经典的hello world例子。

#import <Foundation/Foundation.h>
int main(int argc,const char *argv[])
{
	NSLog(@"hello object-c!");
 	return 0;
}

NSLog()函数和C语言中的printf()很像,但是增加了时间戳、日期戳和自动换行的新特性。其中Cocoa对其所有的常量和函数、类型名称都添加了“NS”前缀,只是为了告知函数来自于Cocoa而不是其他的包。
   双引号字符串前面有一个@符号,表示引用的字符串是一个Cocoa的NSString元素来处理。其本身有一些特性,1.告知其自身长度,2.将自身与其他字符串比较。2.将自身转换成整型值或者浮点值。其详细特性以后在说。
   如果在NSLog()里面不使用@符号呢?编译的结果是这样的:(编译使用的是GNUStep 2.6.2)
hello.m:4:2: warning: passing argument 1 of ‘NSLog’ from incompatible pointer type [enabled by default]
/usr/include/GNUstep/Foundation/NSObjCRuntime.h:114:18: note: expected ‘struct NSString *’ but argument is of type ‘char *’
会有警告出现,提示不匹配的指针类型,下面的提示就更加清晰了,希望是NSString类型的,结果给出的参数是char *,也即C语言中的字符串类型,执行编译后得到的可执行文件提示core dumped。


C语言中bool类型值有true和false两种,而object-c中的BOOL类型提供了yes和no两种类型。绝不要将BOOL值直接和YES比较,而是直接和NO比较。


在object-c中提到NSLog()可以自动换行,而在下面的循环的例子中作者使用了"\n"来换行:

#import <Foundation/Foundation.h>
int main(int argc,const char *argv[])
{
	int i;
	for(i = 0;i < 5;i++)
	{
		NSLog(@"%d\n",i);
	}
	return 0;
}
得到的结果是
2013-03-21 20:50:13.123 LogTest[30452] 0
2013-03-21 20:50:13.124 LogTest[30452] 1
2013-03-21 20:50:13.124 LogTest[30452] 2
2013-03-21 20:50:13.124 LogTest[30452] 3
2013-03-21 20:50:13.124 LogTest[30452] 4
如果省略了"\n" 之后,也是可以实现换行的,并且显示的结果基本一样。那么NSLog()在何种情况之下才可以自动换行呢?
还木有查到。。。


在提到文件的间接的时候,其实在学习C的时候已经用过了,作者给出的例子是一个输出单词长度的程序。

#import <Foundation/Foundation.h>
int main()
{
	const char *word[4] = {"hello","nihao","buaa","china"};
	int wordCount = 4;
	int i;
	for(i = 0;i < 4;i++)
	{
	NSLog(@"%s is %d characters long.",word[i],strlen(word[i]));
	}
	return 0;
}
这个程序里面hello,nihao 这些字符串只提供了一个首字母的地址,而这些字母是依据哪种方法存在内存中呢??
下面引用CSDN中的一段博客的内容。
C语言中内存分为几个区域。
1.栈:用来存放函数的形参和函数内部的局部变量。由编译器分配空间,函数执行完毕后自动释放。
2.堆:用来存放由动态分配函数(比如malloc)分配的空间,是由程序员自己手动分配的,并且必须手动使用free释放。如果忘记用free释放,会导致所分配的空间一直占着不放,导致内存泄漏。
3.全局区:用来存放全局变量和静态变量,存在于整个程序的运行期间,是由编译器分配和释放的。
4.文字常量区:比如char *c="abcde",那么“abcde”是文字常量,存放于文字常量区,也由编译器控制分配和释放。
5.程序代码区:存放程序的二进制代码
一个常见的错误是:char *str1;然后用strcpy函数向str1所指向的内存拷贝数据,这时会出错。因为str1只是一个指针,指针指向的空间还没有分配,所以应该使用malloc向动态分配内存(在堆中)。堆中的内存需要手动释放,栈中的内存是自动释放。

下面还有一个和文件有关的间接的程序:
#import <Foundation/Foundation.h>
int main(int argc,const char * argv[])
{
	FILE *wordFile = fopen("/tmp/words.txt","r");
	char word[100];
	while(fgets(word,100,wordFile))
	{
		word[strlen(word)-1] = '\0';
		NSLog(@"%s is %d characters long",word,strlen(word));
	}
	fclose(wordFile);
	return 0;
}

中间在运行这个程序的时候一直是core dumped ,后来仔细一查原来是/tmp的写成了相对路径。字符串末尾是\0


如果是使用命令行参数:

#import <Foundation/Foundation.h>
int main(int argc,const char * argv[])

{ 
if(argc == 1)
	{
		NSLog(@"you need provide a file name ");
		return (1);
	}
	FILE *wordFile = fopen(argv[1],"r");
	if(wordFile == NULL)
	{
		NSLog(@"the file doesn‘t exist!");
		return (1);
	}
	char word[100];
	while(fgets(word,100,wordFile))
	{
		word[strlen(word)-1] = '\0';
		NSLog(@"%s is %d characters long",word,strlen(word));
	}
	fclose(wordFile);
	return 0;
}

在熟悉一下命令行参数的使用。
书中提到OOP真正的革命性就是它在调用代码中使用间接。不理解。




过程式编程的中心是函数。面向对象是以程序的数据为中心的,函数为数据服务。
下面是一个很简单的面向对象的一个例子。

#import <Foundation/Foundation.h>

@interface Power :NSObject
{
}
	
- (void)  power : (int) i;

@end

@implementation Power
- (void) power : (int) i
{
	NSLog(@"the power result is %d",i*i);
}
@end
int main(int argc,const char *argv[])
{
	id test;
	int i=10;
	test = [Power new];
	[test power:i];
	return 0;
}


@interface是表示接下来的部分是关于类的声明部分,是为Power类定义的接口。而后面的:NSOject是表示每个Power类都将继承NSObject类定义的所有行为。也即每个Power类都是一个NSObject类。
在接下来的花括号里面可以定义一些数据成员,因为这个程序极其简单,所以就没有定义任何数据。举个不太恰当的例子,人好比是一个类,创建新类就和生孩子一样,小孩继承了人的所有自然属性。其中的身高体重等等可以称之为数据成员。而小孩出生时候的哭泣 笑 挥拳等等都可以算做是方法。
- (void) power:(int) i;这一行类似于旧式的C函数原型,最前面的短线代表这是object-c的方法的声明,最后的i是表示函数的参数。


@end 表示的是结束。并且良好的习惯是在@end 后面添加对类的简单说明。
@implementation 是对类提供代码,比如说方法的代码。方法的定义不必要拘束于@interface 中对方法的声明顺序,而且可以定义在@interface 中没有声明的方法,可以把它们看成是私有方法,仅在类的实现中使用。(object-c中不存在真正的私有方法,也无法把某个方法标识为私有方法,从而禁止其他代码调用它,这是objc动态本质的副作用)
方法的代码和C语言中类似。
在main函数中,向相应的类发送new消息即可得到一个可以使用的新对象的实例。
Object-c 语言一个极好的特性就是可以把类当成对象来发送消息,对全体类都是通用的,通常用在创建新对象时候,请求类创建新对象比请求某个现有的对象更合适一些。


id 是一种数据类型,可以存储任何类型的对象。
test = [Power new] 即是向类发送创建新对象的消息,test是id型。
[test power:i];即是调用test对象中的power方法,其中i是参数。


继承

#import <Foundation/Foundation.h>

@interface Cal:NSObject
{
	//int i;
}
- (void) power:(int) i;
- (void) cube : (int) i;
@end
@implementation Cal
- (void) power :(int) i
{
	NSLog(@"the power is %d",i*i);
}
- (void) cube :(int) i 
{
	NSLog(@"the cube is %d",i*i*i);
}
@end

@interface Power : Cal
@end
@implementation Power
@end
@interface Cube : Cal
@end
@implementation Cube
@end

int main(int argc,const char *argv[])
{
	id power;
	id cube;
	int i = 10;
	power = [Power new];
	cube = [Cube new];
	[power power:i];
	[cube cube:i];
	return 0;
}

Power 和 Cube都继承了Cal类。也可以将方法分别放到子类里面实现。比如这样:
#include <Foundation/Foundation.h>

@interface Cal:NSObject
{
	//int i;
}
- (void) cal:(int) i;

@end
@implementation Cal
- (void) cal : (int) i
{

}
@end

@interface Power : Cal
- (void) cal : (int) i;
@end
@implementation Power
- (void) cal : (int) i
{
NSLog(@"the power is %d",i*i);
}
@end
@interface Cube : Cal
- (void) cal : (int) i;
@end
@implementation Cube
- (void) cal : (int) i
{
 NSLog(@"the cube is %d",i*i*i);
}
@end

int main(int argc,const char *argv[])
{
	id power;
	id cube;
	int i = 10;
	power = [Power new];
	cube = [Cube new];
	[power cal:i];
	[cube cal:i];
	return 0;
}
这样是在Cal类里面写一个空函数,将具体实现放到相应的子类里面。即Power和Cube类分别重写了cal方法。
关于super关键字:

#import <Foundation/Foundation.h>
@interface Cal:NSObject
{
	int j;
}
- (void) cal;

- (void) setj:(int) tmp;
@end

@implementation Cal
- (void) cal
{

}
- (void) setj:(int) tmp
{
	j = tmp;
}
@end

@interface Power : Cal
- (void) cal;
- (void) setj:(int) tmp;
@end
@implementation Power
- (void) setj : (int) tmp
{
	if(tmp == 10)
	{	tmp = 100;
		[super setj:tmp];
	}
}
- (void) cal
{	
NSLog(@"the power is %d",j*j);
}
@end

int main(int argc,const char *argv[])
{
	id power;	
	power = [Power new];	
	[power setj:10];
	[power cal];	
	return 0;
}

上面的程序中如果没有[super setj:tmp]这一行,那么在调用setj方法的时候,方法调度程序将运行重写的方法,即Power类里面的setj实现,而超类Cal里面定义的
setj的实现将会全部被忽略。而super关键字可以实现既重写了方法,仍然调用超类的实现方式。不要直接更改由继承得到的实例变量的值,一定要使用方法进行更改。(不理解)。
整个大致过程是这样的:[power setj:10]会首先向Power类查询有无setj方法,如果有就执行,否则会向继承链中的超类查找。此例子中子类中有方法,所以直接执行,不会在向上查找,而
[super setj:tmp]则是请求调用超类的setj方法,传递参数为tmp;根据超类里面的方法可以将变量j的值更改为tmp的值,然后在调用cal方法。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值