zoukankan      html  css  js  c++  java
  • 和我一起来学iOS(一)ObjectC的语法

    前言:
    为什么叫和我一起学呢?因为从开始写这系列博客中我就定了一个方向,不从最基本的讲,而是挑一些如果从其他语言(C、C#、 Java、 Javascript等)转过来的程序员容易出错的地方。

    假设你是有几年其他语言的开发经验,对我说的上述基本语言有了不错的了解。这其实也是我当初学这门语言时最希望别人告诉我的地方。

    独特的@符号

    首先,ObjectC是C的超集,为了不和C中已有的东西冲突,ObjectC中特有的东西前面都带有@符号

    比如声明字符串 ,可以这么声明,这时就是一个C类型的字符串

    char *name = "langxue";

    这么声明出来的字符串不是一个ObjectC对象,因此不会带有一些特定的方法,ObjejctC中字面量字符串是这么声明的

    NSString *name = @"langxue"

    语法的差异

    一、方法名

    ObjectC中的方法名由多个段组成。

    比如我们想初始化一个controller,最常用的是这个方法

    initWithNibName:bundle:

    看起来很奇怪是吧?方法具体签名如下

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

    参数更紧接着调用的谓语,这样看起来更符合自然语法,在拥有多个参数的情况下即使没有对参数的说明,也非常容易记住。

    中括号表达式:

    一个中括号代表一次调用,看起来比较清晰。

    具体调用如下:

    [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];

    我们先是根据MyViewController类所需的大小分配了一块内存,然后发送消息去初始化这块内存。

    因为方法名是包括:符号的,所以在通过selector选择方法的时候,:符号不能忘记

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

    二、id

    这类似于C中的通用指针void*,可以用来储存任何类的对象。也像C#中的dynamic关键字,就是告诉编译器在编译时无需检查这个变量的类型,运行的时候再检查确定。换句话说就是动态绑定。由于对象总是带着isa指针,所以即使我们无法从指针类型得出信息,也总能从对象本身获取类型信息。

    在ObjectC中哪里会用到呢?

    1.初始化的时候

    ObjectC一个对象不能有两个名字相同的方法,即使他们的参数和返回类型不同。因此子类的init方法既不能返回子类,否则无法调用父类相同的init方法,当然也不能返回父类。

    这时候就需要用到id这个通用的指针了。

    2.不需要具体类型,只需要知道这个实例实现了某些方法。类似于C#中对接口编程的用法,常用声明delegate属性,以便使用不同的策略提供对象。

    @property (nonatomic, weak) id<RequestDelegate> delegate;

    需要提醒的是,虽然它可以存储任何类的对象,如果滥用它,就会失去静态类型时编译器所提供的好处。



    三、强弱引用( strong, week(ARC); retain, assign(非ARC) )

    是否加入引用计数的一种方式。iOS中没有自动垃圾回收机制,垃圾回收是根据引用计数来决定的。一个对象的引用计数为0的时候会被认为是垃圾马上被销毁,这和GC机制的有延迟的回收不一样的地方。内存能更高效的被使用。

    在ARC的项目中

    @property (strong, nonatomic) MyController *myController;

    四、协议 (protocol)

     类似于C#中的Interface,区别在于protocol中定义的方法可以选择不需要实现,也就是@optional的。当然如果没有标记那么就是必须实现的。

    @protocol BoardActionDelegate<NSObject>
    
    @required
    - (void) selectPressed:(UIView *)sender;
    
    @optional
    - (void) cancelPressed:(UIView *)sender;
    
    @end

    五、范畴(Categories)

    1.命名范畴,类似于C#中的扩展方法,用于给已有的类扩展自定义的方法,区别在于ObjectC中不限制方法的类型,可以是实例或者是类方法。但是不能带成员变量。

     比如说我们扩展UIColor类

    @interface UIColor (Extract)
    
    - (void)extract_getRed;       
    
    @end

    这类文件的命名最好为原类名 + 扩展类名,如 UIColor+Extract.h 、UIColor+Extract.m

    2.类扩展(class extension),看起来就像一个没有名字的范畴。可以带变量成员,且必须实现。

    如果根据良好的代码划分,我想放在头文件.h的往往是对外公开的方法和属性,而自己内部使用的私有方法就不需要放在头文件里。在像C#这种没有头文件的语言里,我们仍然会按照约定先写Public 方法,然后用#region 把它标记起来,方便别人查看。

    假设我们有一个CarStock类,在CarStock.m中有如下代码,会遇到这么一个问题,如果init中调用的方法声明如果在init的位置下面,那么编译器就会报错,因为它是从上往下解析的.

    - (id)init {    
        if ((self = [super init])) {    
            [self refreshStock];    
        }   
        return self;    
    }
    
    - (void)refreshStock {  
        // ...  
    }

    这时有几种解决方式

    1.refreshStock方法上移

    2.refreshStock声明在头文件CarStock.h里

    但这些都不符合我想要写一个私有方法的想法。ObjectC中方法是不带作用域声明的,也就是你无法像下面这样声明

    private - (void)refreshStoc

     所以可以在CarStock.m中,通过一个无命名的范畴,来解决这个问题

    @interface CarStock ()
    
    - (void)refreshStock;       
    
    @end

    六、@dynamic
    告诉编译器我们这个属性会在其他地方生成相应的get和set方法(在ObjectC中是通过@synthesize(合成)关键字来合成属性)

    Super class中对这个属性合成:

    @property (nonatomic, retain) NSButton *someButton;
    ...
    @synthesize someButton;


    Subclass如果不对*someButton合成或者自己提供get,set方法,那么编译器就会提示有问题:

    @property (nonatomic, retain) IBOutlet NSButton *someButton;
    ...
    @dynamic someButton;

    我们想用Super class中的合成方法来合成Subclass,我们这时候就可以通过@dynamic关键字来关闭编译器的警告。

    这在通常代码中非常少用,往往在使用Core data(ObjectC中的ORM框架)中使用。我们继承了NSManagedObject,希望相应的访问器方法由它来完成。

    七、@class

    在头文件.h里我们往往不需要知道所引用类的具体方法信息,而只是需要知道有这么一个类,便于我们声明变量类型,我们这时候就可以用@class关键字来代替#import,这样告诉编译器我们肯定会有这么一个类,你就不用检查了。这样在一些大型项目中可以加快编译速度。也可以解决循环import可能带来的问题。
    当具体使用的使用我们才在.m里import所需要的头文件。

    八、nil
    nil 类似于 null,区别在于给nil发送消息并不会产生错误,它的默认实现是忽视这条信息。在C#和其他语言中则会产生类似NullReference的错误。

    原因是在底层C的实现中,nil不带self指针,发送消息调用selector的时候如果检测self为空,则直接返回。

    我们通常在使用NSError的时候,通过检测NSError是否为nil来判断方法调用是否出错

            NSError *error = nil;
            self.responseString = [NSString stringWithContentsOfFile:zipFileName_ encoding:NSUTF8StringEncoding error:&error];
            if (error)

    还有就是在dealloc方法中把不用的变量设置为nil,可以防止在release后指针指向无效内存而导致错误。





    Xcode 4.4版本(LLVM4编译器)后编译器新增的一些语法糖

    Xcode是免费的,所以咱们可以升级到新版本来享受一下新编译器的一些好处

    1.更加多的字面量支持

    原来我们在ObjectC中创建一个NSString类型的对象时,可以

    NSString *myName=@"langxue";

    现在我们创建其他对象也可以字面量语法了

    NSNumber *myNumber =@3;
    
    NSNumber *yesValue =@YES;
    
    NSArray *array =@[@1,@2,@3,@4];
    
    NSDictionary *dictionary =@[@"key1":@"value1"
      
                    ,@"key2":@"value2"]
    

    2.下标访问

    我们可以通过下标来直接访问我们需要的元素,这在原来是不可以的。

    int element3 = array[3]; 
    int elementAt3 = dictionary [@"key3"]

    3.自动合成@property

    我们声明了property以后

    @property (strong, nonatomic) MyController *myController;
    @property (nonatomic, copy) void (^completionHandler)();

    原来的情况下访问器是通过@synthesize关键字合成的。

    @synthesize myController = _myController;
    @synthesize completionHandler = _completionHandler;


    有了新的LLVM编译器,就可以省略这些用于合成实例变量的代码了。LLVM 4编译器会自动合成这些实例变量。当然如果明确地写了get,set方法,LLVM 4就不再自动生成@synthesize指令了。需要记住的是,自动合成的实例变量会按照ObjectC中的惯例以下划线_开头。

     


     最后,如果你觉得这篇文章有帮助,请点右下角一下推荐,这是我继续下去的动力,谢谢!

  • 相关阅读:
    js 多媒体文件(图片,表格 等) 下载方法
    CentOS7 + asp.net core 3.1 + mysql 8 配置备忘
    项目管理平台参考设计
    golang 使用rate实现redis qps令牌桶限流
    golang执行命令实时输出(协程通过channel更新数据到主进程)
    go-chart go后端生成图表base64
    go-chart go后端生成图表base64
    golang OOM分析
    Golang xorm time自定义解析
    python 多线程
  • 原文地址:https://www.cnblogs.com/lwzz/p/3029521.html
Copyright © 2011-2022 走看看