黑马程序员 7 oc核心语法与内存管理初探

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


慢慢消化一起的内容,再继续往前!

本期目录:

1.      提醒与错误解决

2.      Oc核心语法

3. xcode 使用技巧

4.     个人总结

 

一:提醒与错误解决

1.学习了构造方法,所以以后尽量不要new一个对象

2.重写init方法时尝试不去写[super init],编译运行都可以。但是,感觉不应该这样做。

3.今天运行程序明明逻辑对了,语法对了,但一直报错,仔细看发现时未全部保存(变灰未保存)



4.@property 是对变量自动生成set方法和get方法的声明,不是对变量的声明限制,所以不能放在@interface @end之间。



5.再次提醒:成员变量要以 _开头,因为标识只能以字母、数字、下划线开头,所以为了方便程序员直接交流也为了和局部变量区分。

6.为什么自定义方法名一般要以id类为返回类型?

7.%@打印oc对象,默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>

8.方法命中如果有:,冒号也是方法的一部分,不加:是找不到方法。比如:(void)age:@“解决解决”;其中方法名为age:



二:核心语法

1,构造方法

1.构造方法:用来初始化对象的方法,是个对象方法,-开头

2.重写构造方法的目的:为了让对象创建出来,成员变量就会有一些固定的值

重写构造方法的注意点

1.先调用父类的构造方法([superinit]

2.再进行子类内部成员变量的初始化

3.注意init方法调用过程分析:

总结:

1.首先,student对象调用重写的init,而这个init会先调用父类init,这样依次类推

最后我们发现,NSObject的isa(指向对象创建的类)指向了Student类。

2.init方法调用时先完成父类初始化


3.自定义构造方法

讨论:如果我们想在程序初始化的时候给构造函数传入参数,一般的构造方式:(void)init;(继承自父类的方法,子类覆盖)。所以,我们可以去重写一个构造函数传入参 数,initWithxxx:(NSString *)xxx;。

自定义构造方法的规范

1.一定是对象方法,一定以- 开头

2.返回值一般是id的类型

3.方法名一般以init开头

错误体会:





我们可以看到:在父类Person中声明的@property NSString * name;可以再父类中使用_name成员变量(编译器自动生成)。但是,当我们在子类(Student类)中试图调用_name成员变量时,会出现“use of undeclaed identifier”(未定义的标识符)。原因在于,子类继承了父类的全部内容,但是_name 默认是@private。

解决策略:

我们尝试这样做,子类调用继承的父类方法set方法来访问相应成员变量。

<span style="font-size:14px;">if(self = [super init])
{
self.name = name;//name 为传入参数
self.age = age;
}</span>


此时并未在Student类内部产生新的_name





2.category分类
分类的作用:在不改变原来类内容的基础上,可以为类增加一些方法

使用注意:

 1.分类只能增加方法,不能增加成员变量
 2.分类方法实现中可以访问原来类中声明的成员变量
 3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
 4.方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类  --> 父类
来看一段代码:

<span style="font-size:14px;">//main
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+MJ.h"
#import "Person+JJ.h"

int main()
{
    Person *p = [[Person alloc] init];
    //p.age = 10;
    
    // 优先去分类中查找,然后再去原来类中找,最后再去父类中找
    [p test];
   // [p study];
    
    return 0;
}
</span>

Person.h

<span style="font-size:14px;">#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    int _age;
    // int age;
    
    int _height;
    
    double _weight;
    
    NSString *_name;
}

// @property:可以自动生成某个成员变量的setter和getter声明
@property int age;
//- (void)setAge:(int)age;
//- (int)age;


@property int height;
//- (void)setHeight:(int)height;
//- (int)height;

- (void)test;


@property double weight;

@property NSString *name;

@end
</span>

分类:Person+JJ.h

<span style="font-size:18px;">#import "Person.h"

@interface Person (JJ)
- (void)test2;
@end
</span>

分类:Person+MJ.h

<span style="font-size:18px;">#import "Person.h"

@interface Person (MJ)
- (void)study;
@end</span>

结果



分析:这里的执行过程式和编译器的编译顺序有关,通过查看编译顺序:




总结:可以看到我们可能不知道原来类的具体属性,但是我们可以再不妨碍原来类的基础上使用category产生类的补充,比如:我们想对系统提供的类NSString进行补充个性的方法,那么我们就可以使用category方式,这样将很大的提高编码的效率。


3.类的深究

Ø   其实类也是一个对象,是Class类型的对象,简称“类对象”

Ø   Class类型的定义

typedef struct objc_class *Class;

Ø   类名就代表着类对象,每个类只有一个类对象

举个例子:


可以看到,内存中类只加载一次,其他的对象都是对这个类的指向(isa指向同一块区域),而内存中Person类本身也是以Class类的对象,所以Person也是个”特殊“的对象,类名就代表着类对象


<span style="font-size:18px;">//这里的代码是等价的
Person *p = [[Person alloc] init];
Person  *p2 = [[Person alloc] init];

Class c = [p class];

Class c2 = [p2 class];

Class c3 = [Person class];//结果是相同的
</span>


类方法:load和initialize

Ø   +load

  在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法

  先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load

 先加载元原始类,再加载分类

 不管程序运行过程有没有用到这个类,都会调用+load加载

 Ø   +initialize

   在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法

   一个类只会调用一次+initialize方法,先调用父类的,再调用子类的


-description方法

使用原理:

1.使用NSLog和%@时,会调用对象的-description方法

2.拿到-description方法的返回值(NSString *)显示到屏幕上

3.-description默认返回的是“类名+内存地址”

4,不要再description中使用self

-和+description方法:

-开头为对象的description方法

+开头是类对象的description,默认实现显示类名

NSLog的使用技巧

NSlog(@"%p",p);//对象地址

NSLog(@“%p",&p); //指针变量的地址

NSLog方法输出c语言字符串时是不能输出中文,所以可以使用printf


SEL类型

方法的存储位置

Ø   每个类的方法列表都存储在类对象中

Ø   每个方法都有一个与之对应的SEL类型的对象

Ø   根据一个SEL对象就可以找到方法的地址,进而调用方法

Ø   SEL类型的定义

常用的SEL相关方法:

SEL s = @selector(test);

SEL s2 = NSSelectorFromString(@"test");

NSString *str = NSStringFromSelector(@selector(test));

调用:

[p performSelector:@selector(test3) withObject:@"jjjj"];

#import "Person.h"

@implementation Person
+ (void)test
{
    NSLog(@"test-----");
}

- (void)test2
{
    // _cmd代表着当前方法
    
    NSString *str = NSStringFromSelector(_cmd);
    
    // 会引发死循环
    // [self performSelector:_cmd];
    
    NSLog(@"调用了test2方法-----%@", str);
}

- (void)test3:(NSString *)abc
{
    NSLog(@"test3-----%@", abc);
}
@end




3.xcode模板

如果我们不习惯已有command line 模板,我们可以通过xcode自己更改模板。同时,我们也可以自定义模板文件。

具体方法:

Finder -> applications ->xcode -> 右键 “显示包内容” 



选择"TemplateInfor.plist“ 文件,这时我们就可以很轻松的完成更改。(推荐:用类似记事本打开后,command+f 搜索)




举一反三,我们可以发现可以更改:Class文件开始注释,文件名注释等等。


4.个人总结:

我有点明白oc执行效率高的原因了,因为他涉及了好多对底层的访问。不过最近发现,oc也有java类加载的思想,那么就很像jvm的模式了。所以有点疑问。

总体上,感觉oc语法和c和java都有不同,所以可能会比较难记忆,但代码多了就记住了。

 










------Java培训、Android培训、iOS培训、.Net培训、期待与您交

                            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值