int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
int a,b,result;
a = rand() % 10;
b = rand() % 10;
NSLog(@"The sum of %d and %d is :",a,b);
scanf("%d",&result);
if(result == a + b){
NSLog(@"TRUE");
}
else{
NSLog(@"FALSE");
}
}
return 0;
}
NSString *str = @"Hello world!";
NSLog(@"%@",str);
//组合C语言里的变量
NSString *str1 = [[NSString alloc] initWithFormat:@"%d + %d = %d",3,5,8];
NSLog(@"%@",str1);
[str1 release];
//以前版本自动池的创建
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSString *name = @"Tom";
NSLog(@"My name is %@",name);//输出NSString类型
int age = 20;
NSLog(@"My age is %d",age);
BOOL isAdult =age > 18 ? YES:NO;
if(isAdult == YES)
NSLog(@"I am an adult!");
else
NSLog(@"I am under age!");
//池的释放
[pool drain];
struct rectangle{
int width;
int high;
};
struct rectangle rec = {24,16};
int area;
area = rec.width * rec.high;
NSLog(@"长方形的面积为:%d",area);
方法签名由多部分组成,每一部分签名都说明参数含义,所以方法的签名具有自说明性,例如:
-(void)setName:(NSString*)aName age:(int)aAge
方法签名为setName:age:
说明这个方法是用来设置姓名和年龄的,具有两个参数
:是签名的一部分
setName::
-(void)setName:(NSString*)aName :(int)aAge
在同一个类的方法不能重载,即方法的签名不能完全一样,但类方法和实例方法签名可以相同。
-(int) g:(int) x;
-(int) g:(float) x;//Error
-(int) g:(int) x:(int) y;//OK
-(int) g:(int) x:(float) y;//Error
-(int) g:(int) x andY:(int)y;//OK
-(int) g:(int) x andY:(float) y;//Error
-(int) g:(int) x andAlsoY:(int)y;//OK
#import
NS_ASSUME_NONNULL_BEGIN
@interface ASStudent : NSObject{
@protected
NSString * name;
@private
NSString *sid;
@public
unsigned int age;
}
//"+"号为类方法,可以直接用类名来调用,作用是创建一个实例
+(void)print;
//"-"号为实例方法,类的实例才可以调用
-(NSString*)name;
-(NSString*)setName:(NSString*)aName;
-(int)age;
-(void)setAge:(int)aAge;
@end
NS_ASSUME_NONNULL_END
#import "ASStudent.h"
@implementation ASStudent
-(void)setName:(NSString*)aName{
name = aName;
}
-(NSString*)name{
return name;
}
-(int)age{
return age;
}
-(void)setAge:(int)aAge{
age = aAge;
}
+(void)print{
NSLog(@"ok");
}
@end
这里有两个地方是需要注意⚠️
实例方法可以直接引用类的实例变量和其他实例方法
类的方法都是public的,没有protected和private方法,但是如果一个方法只出现在类的视线里,没有出现在类的声明里,那么这个方法就是私有的。
ASStudent* student = [[ASStudent alloc] init];
student->age =20;
[student setName:@"Tom"];
NSLog(@"%@",[student name]);
对象是类的一个实例,类的实例化的过程就是对象被创建的过程
类的实例化分为两步:
第一步:首先分配实例所占有的内存空间(allocate)
+(id)alloc方法分配内存
ASStudent * zhang = [ASStudent alloc];
//栈区会报错,不能够静态分配
ASStudent wen;
第二步:对实例的每一个实例变量进行初始化(initialize)
调用alloc分配完内存后还需要使用init方法将实例变量初始化为有意义的值
zhang = [zhang init];
一般把内存分配和初始化合在一起采用链式表达式
ASStudent * wang = [[ASStudent alloc]init];
#import
#import "ASStudent.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
ASStudent * zhang = [ASStudent alloc];
zhang = [zhang init];
[zhang setName:@"张三"];
[zhang setAge:18];
NSLog(@"%@ %u",[zhang name],[zhang age]);
[zhang release];
}
return 0;
}
⚠️:需要注意
1、 id li = [ASStudent alloc];
2、id pid = nil;
ASStudent* jiang;
[jiang setName:@"江"];
id pid = nil;//ok
int *plnt = NULL;//ok
pid = NULL;//error
oc面向对象最大的特色貌似就是消息传递(message passing)模型。
目前还是很不习惯这个呢,因为对象不调用方法,而是互相传递消息。
在oc中,如果让对象完成某事,可以向对象发送相应的消息告诉对象执行某个方法
消息语法:
[receiver message]
消息和方法的调用对应
[zhang setName:@"张三"];//向zhang对象发送setName
消息设置名字为“张三”
初始化方法的实现
-(id) init{
if(self = [super init]){
width = 20;
high = 16;
}
return self;
}
-(id) init;
-(id) initWithName:(NSString*)aName;
-(id)initWithID:(NSString*)aID name:(NSString*)aName;
指定初始化方法
初始化方法如下:
-(id)initWithSID:(NSString*)asid name:(NSString*)aName age:(unsigned int)aAge{
if(self =[super init]){
[self setName:aName];
name = aName;
[self setSid:asid];
age = aAge;
}
return self;
}
-(id)initWithName:(NSString*)aName
{
if(self = [super init]){
name = aName;
}
return self;
}
初始化如下:
ASStudent* zhao = [[ASStudent alloc]init];
ASStudent* wang = [[ASStudent alloc]initWithSID:@"101" name:@"王五" age:18];
ASStudent* jian = [[ASStudent alloc]initWithName:@"tom"];
得到实例变量值的方法getter方法
设置实例变量值的方法setter方法
和java里的一样嘛,找了半天没找到快捷生成getter,setter方法,查了资料原来是使用属性声明机制才可以自动生成,下个小节再使用这个方法试试。
-(void)setName:(NSString*)aName{
name = aName;
}
-(NSString*)name{
return name;
}
-(int)age{
return age;
}
-(void)setAge:(int)aAge{
age = aAge;
}
有了存取器方法,可以使用点语法,两种方式等价
[zhang setAge 18];
zhang.age = 18;
也可以直接输出
NSLog(@"%@ %u",Zhang.name,zhang.age);
点语法针对的是存取方法,只要写了对应的存储器方法,就可以使用点语法
点语法是语法“糖”,实际上还是调用相应的存储器方法
//设置方法如下,两个等价
zhang.name= @"张三";
[zhang setName:@"张三"];
//读取方法如下,两个等价
NSLog(@%d),zhang.age);
NSLog(@"%d",[zhang age]);
属性的声明
#import
NS_ASSUME_NONNULL_BEGIN
@interface Rectangle : NSObject
{
NSString *width;
int heigh;
}
@property(nonatomic,retain)NSString *width;
@property(nonatomic,assign)int heigh;
@end
NS_ASSUME_NONNULL_END
属性的实现
#import "Rectangle.h"
@implementation Rectangle
@synthesize width,heigh;
@end
属性也有其他用法呀
指定属性的存取器方法的名字,不用默认的存取器方法名
试了试可以是这样
@property(getter = isMarried) BOOL marry
经测试,不要实例变量只声明属性也是可以的,是个小惊喜哦
import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Rectangle : NSObject
//{
// NSString *width;
// int heigh;
//}
@property(nonatomic,retain)NSString *width;
@property(nonatomic,assign)int heigh;
@end
同样,以前的好习惯要延续过来呀,为了实例变量的安全,属性实现的时候不和实例变量同名,可以这样声明属性。方法里使用_age来引用实例变量,也就是self.age
如果是类外只能用object.age
不同的语言,思想还是相通的嘛,这就很开心了。
@property int age;
@synthesize age = _age;
写到这,感觉oc里的self和java里的this有点像啊
简单来说就是,谁调用self就代表谁。都是代表当前方法的一个调用者。
因为想要确认下这两个有没有区别,分别写了一个静态方法和动态方法来测试。
果然呢,java里的this只能用于动态不能用在静态方法中。这个是之前忽视的一个地方了吧,也没有很深入的研究过java的this,this一般就用在setter函数里,现在发现如果放在静态方法里一用一个错啊!!
然而然而,放在oc里我就很惊喜啦,动态静态方法都可以。只不过区别在于,动态中方法中,self代表对象。静态方法代表类。
将自己了解到的一些知识点,转化为自己的理解记录了下来。
截止目前,大体感觉如下吧:
1、oc其实应该算是C语言的扩充
2、具备完善的面向对象特征,封装继承和多态
3、大致了解了iOS支持两种内存管理方式,ARC和MRC,还没太搞明白,后面再研究研究
4、核心思想其实也是类和对象呀
5、“:”是标识参数,不能省略,有冒号必须要有参数,冒号也是方法名的一部分
6、+是类方法,-是实例方法
7、在OC中使用消息发送机制:[receiver message]
①给received对象发送message消息
②received接收到消息,即方法message
③teacher找到message方法,并执行。
8、只要有封装,就一定逃不过setter设置器和getter访问器
内存啊,超级重要重要的一个东西呢
内存管理,就是管理程序运行时进行内存分配,使用内存,结束时释放内存。
要是写一个可糟糕的程序,占用很大内存,我想,是个失败的开发者。一个手机就那么大点内存,超出限额就要崩溃!
在oc里也如此啦。要尽量尽量写一个棒一点的程序,尽量少占点内存。依然还是,需要的时候分配,不使用就回收♻️。
经了解得知,oc里有三种内存管理机制
悄悄给一个对象写了两个release,果然很爽快的崩溃。
我想着试试这种写法吧,给自己挖了个大坑出来!!!
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang retain];
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang release];
只有为0才能释放掉呀,如果写了很多这种代码,我觉得程序要炸啦
找了一个比较官方的说法:
oc的内存管理模型,是基于对象的所有权,在oc中如果一个实体拥有一个对象,那么这个实体对这个对象有所有权。所有权就意味着该实体确保对其拥有的对象进行清理。
有权利就有义务!!
这个说法太官方来,我的理解就是,任何一个对象都可以有一个或者更多的“所有者”(owner),当一个对象有至少一个所有者的时候,这个对象就是存在的。如果没有所有者啦,那就没有用啦,系统就会析构它。
在oc中,所有权管理规则:
让我想到了java里的java GC内存管理,一般不会专门去写内存回收和垃圾清理代码,对内存泄露和溢出的问题,好像不怎么去重视,感觉java GC挺完善的。
所有权的策略是通过引用计数来实现的,通常称之为"retain count",每个对象都有一个retainCount
是0就回收啦
ASStudent * zhang = [[ASStudent alloc]init];
//ASStudent * zhang = [ASStudent new];
zhang.name = @"Tom";
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang release];//成功回收
ASStudent * zhang = [[ASStudent alloc]init];
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang retain];
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang release];
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang release];
alloc、new、copy、retain
与
release、autorelease
要成对儿出现
自动释放池机制其实就是一个“延时”释放对象的机制。也就是想放弃对象所有权,又不想立即放弃,可真纠结呀。这个时候可以向对象发送一个autorelease消息,将对象加入到自动释放池。
ASStudent * zhang = [[ASStudent alloc]init];
NSLog(@"zhang:%lu",zhang.retainCount);
[zhang autorelease];
NSLog(@"zhang:%lu",zhang.retainCount);
仍需研究其原理
日后总结吧
简单写了两个类:
第一个,图的类,属性为原点(包含getter、setter),和一个初始化原点的方法
@interface ASGaphic : NSObject{
NSPoint origin;
}
@property(nonatomic) NSPoint origin;
-(id) initWithOrigin:(NSPoint)aPoint;
@end
第二个写了一个形状类,属性为原点和颜色包含getter、setter),和一个方法
@interface ASshape : NSObject{
NSPoint origin;
NSString * primaryColor;
}
@property(nonatomic,assign)NSPoint origin;
@property(nonatomic,retain)NSString* primaryColor;
-(id)initWithOrigin:(NSPoint)aPoint color:(NSString*)aColor;
@end
两个类中的origin属性、getter、setter方法是一样的
可以按照继承父类的实例变量和方法来写,实现代码复用
如下:
#import "ASGaphic.h"
#import "ASGaphic.h"
NS_ASSUME_NONNULL_BEGIN
@interface ASshape : NSObject{
NSString * primaryColor;
}
@property(nonatomic,retain)NSString* primaryColor;
-(id)initWithOrigin:(NSPoint)aPoint color:(NSString*)aColor;
@end