10个迷惑新手的Cocoa&Objective-c开发问题

转自:http://lianxu.me/2012/11/10-cocoa-objc-newbie-problems/  with very limited rewording.

首先允许我可能使用很多英文,毕竟英文资料将来会是你的主要资料来源。

这篇教程将描述一些我见到的众多Cocoa开发新手遇到的问题和障碍。并不会手把手教你:“这个函数什么意思,那个函数如何使用”,而是站在一定搞定,统观各种技术所处的角色,让你不会迷失在各种技术细节中。在你技术深入学习MacOS编程之前,请停下脚步看清楚这些问题。如果你是新手,这个教程不要希望以此能看得非常透彻,学一定阶段返回来再看看又会有新的体会的。


1. 语言背景

首先c语言背景,必须。很多人问:“没有任何语言基础,我不想学c直接学objective-c”。是否可以?”这里我简单说几句,OC是c的超集,也就是说大部分OC代码其实是c,而且众多传统开源项目都是c写成的。你不学好c在unix世界里只能是个二流开发者!也许说得过于严厉,不过自己斟酌吧。c++呢大概了解一些极客,因为它太庞杂了。

2.运行时(runtime)

OC是动态语言,很多新手或者开发人员常常被runtime这个东西所迷惑。而恰恰这是一个非常重要的概念。我可以这么问:“如果让你(设计)实现一个计算机语言,你要如何下手?”很少程序员这么思考过。但是这么一问,就会强迫你从更高层次思考以前的问题了。注意我这句话"设计"括起来了,稍微次要点,关键是实现。

我把实现分成三种不同的层次:

第一种是传统的面向过程的语言开发,例如c语言。实现c语言编译器很简单,只要按照语法规则实现一个LALR语法分析器就可以了,编译器优化是非常难的课题,不在讨论范围内了,忽略。这里我们实现了编译器其中最最基础和原始的目标之一:就是把一份代码里的函数名称,转化成一个相对内存地址,把调用这个函数的语句转换成一个跳转命令。在程序开始运行时候,调用语句可以正确跳转到对应的函数地址。这样很好,也很猴子掰,但是太死板了。所有的操作都是事先定义的好了的。

我们希望语言更加灵活,于是有了第二种改进,开发面向对象的语言,例如c++。c++在c的基础上增加了类的部分。但这到底意味着什么呢?我们再写它的编译器要如何考虑呢?其实,就是让编译器多绕个弯,在严格的c编译器上增加一层类处理的机制,把一个函数限制在它所处在的类的环境里,每次请求一个函数调用,先找到它的对象、类型、返回值、参数等等,确定了这些后在跳转到需要的函数。这样很多程序增加了灵活性。同样一个方法函数调用会根据请求参数和类的环境返回完全不同的结果。增加类级之后,就模拟了现实世界的抽象模式,不同的对象有不同的属性和方法。同样的方法,不同的类有不同的行为!这里大家就可以看到作为一个编译器开发者都做了哪些进一步的思考。虽然面向对象语言有所改进,但还是死板,我们仍然叫c++是静态语言。

希望更加灵活!于是我们完全把上面那个类的实现部分抽象出来,做成一套完全运行时阶段的检测环境,形成低三种,动态语言。这次在写编译器甚至保留部分代码里的语法名称、名称错误检测、运行时环境注册、所有全局的类、函数变量等等信息,我们可以无限地为这个层增加必要的功能。调用方法的时候,会先从这个整体环境里检测所有可呢过的参数在做跳转,这,就是运行时。编译器开发起来比上面更加弯弯绕。但是这个层极大增加了程序的灵活性。例如当调用一个方法的时候,前两种语言,很有可能跳到了一个非法地址导致程序崩溃,但是在这个层次里面,运行时就过滤掉了这些可能性。这就是为什么动态语言更加强壮。因为编译器和运行时环境的开发者已经帮你处理了这些问题。

好了,上面说了这么多,我们再返回来看OC的这些语句。

id obj = self;
if ([obj respondsToSelector:@selector(function1:)]){
}

if ([obj isKindOfClass:[NSArray class]]){
}

if ([obj conformsToProtocol:@protocol(myProtocol)]){
}
if([[obj class] isSubclassOfClass:[NSArray class]]){
}
[obj someNonExistFunction];

看似很简单的语句,但是为了让语言实现这个能力,语言开发者要付出很多努力实现运行时环境。这里运行时环境处理了弱类型、函数存在验证检查工作。运行时检测注册列表里是否存在对应的函数,类型是否正确,最后确定下来正确的函数地址,在进行保存寄存器状态、压栈、函数调用等等实际的操作。

id knife=[Knife createKnife];
NSArray *monsterList = [NSArray array];
[monsterList makeObjectPerformSelector:@selector(killMonster:)withObject:knife];

用c、c++完成这个功能还是比较麻烦的,但是动态语言处理却非常简单,并且让这些语句让OC语言更加直观。

在OC中针对对象的函数调用不再是普通的函数调用,[obj function1Wth:var1];这样的函数调用将被运行时环境转换成objc_msgSend(targe, @selector(function1With:),var1);

OC的运行时环境是开源的,所以我们可以拿出一下实现做简单介绍,可以看到objc_msgSend由汇编语言实现,我们甚至不必阅读代码,只需查看注释就可以了解,运行时环境在函数调用前做了比较全面的安全检查,已确保动态语言函数调用不会导致程序崩溃。对于希望深入学习的朋友可以自行下载Objc-runtime源代码来阅读,这里就不在深入讲解。

/********************************************************************
 * id       objc_msgSend(id self,
 *          SEL op,
 *          ...)
 *
 * On entry: a1 is the message receiver,
 *           a2 is the selector
 ********************************************************************/

    ENTRY objc_msgSend
# check whether receiver is nil
    teq     a1, #0
    moveq   a2, #0
    bxeq    lr

# save registers and load receiver's class for CacheLookup
    stmfd   sp!, {a4,v1-v3}
    ldr     v1, [a1, #ISA]

# receiver is non-nil: search the cache
    CacheLookup a2, LMsgSendCacheMiss

# cache hit (imp in ip) - prep for forwarding, restore registers and call
    teq v1, v1      /* set nonstret (eq) */
    ldmfd   sp!, {a4,v1-v3}
    bx      ip

# cache miss: go search the method lists
LMsgSendCacheMiss:
    ldmfd   sp!, {a4,v1-v3}
    b   _objc_msgSend_uncached

LMsgSendExit:
    END_ENTRY objc_msgSend


    .text
    .align 2
_objc_msgSend_uncached:

# Push stack frame
    stmfd   sp!, {a1-a4,r7,lr}
    add     r7, sp, #16
    SAVE_VFP

# Load class and selector
    ldr a1, [a1, #ISA]      /* class = receiver->isa  */
    # MOVE  a2, a2          /* selector already in a2 */

# Do the lookup
    MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache)
    MOVE    ip, a1

# Prep for forwarding, Pop stack frame and call imp
    teq v1, v1      /* set nonstret (eq) */
    RESTORE_VFP
    ldmfd   sp!, {a1-a4,r7,lr}
    bx  ip

现在说一些runtime的负面影响:

1. 关于执行效率问题。“静态语言执行效率要比动态语言高”,这句没错。因为一部分CPU计算损耗在了runtime过程中,而从上面的汇编代码也可以看出,大概损耗在哪些地方。而静态语言生成的机器指令更简洁。正因为知道这个原因,所以开发与的人付出了很大一部分努力为了保持runtime小巧上。所以OC是c的超集+一个小巧的运行时环境。但是,换句话说,从算法角度考虑,这点复杂度不算差别的,Big O notation结果不会有差别的,起码其差别不在log(n)比之n平方的差别。

2. 安全性。动态语言由于运行时环境的需求,会保留一些源代码级别的程序结果。这样就给破解带来了方便之门。一个现成的说明就是,java,大家都知道java运行在jre上面。这就是典型的runtime例子。它的执行文件.class全部都可以反编译回近似源代码。所以这里的额外提示就是如果你需要写和安全有关的代码,离OC远点,直接用c去。

简单理解:“运行时就是你所有函数调用之外的东西。”

但是大家要明白,第二点我提到的运行时并不只是因为它带来了这些简便的语言特性。而是这些简单的语言特性,在实际运用中需要你从完全不同的角度考虑和解决问题。知识结算1+1,很多语言都是一样的,但是随着问题的复杂,项目的增长,静态语言和动态语言就会演化出完全不同的风景。

3. 线程

记得上学时候学操作系统这门课,里面都会有专门一章介绍任务调度和生产者消费者的问题。这就是为今后使用进程、线程开发打基础。概念很简单,但难点在同步,因为死锁检测算法不是100%有效,否则根本就没有死锁这个说法了。另一个原因是往往这类错误很隐晦,静态分析很难找到。同时多线程开发抽象度较高,需要经验去把握。

总体来说,我见到的在这方面的问题可以分为以下几点:

1. 对系统整体结构认识模糊

不知道多线程开发的几个基点,看别人代码越看越糊涂的。一会NSThread,一会Grand Central Dispatch、block,一会又看到了pthread等等。Apple封装了很多多线程的API,多线程开发的基本结构如下图:


可以看到在多线程开发中你可以选择上面这四种不同的方式。

Mach是核心的操作系统部分。其实这个我也不是非常熟悉,至少我还没有读到过直接使用mach做多线程的代码。

pthread(POSIX Threads)是传统的多线程标准,灵活、轻巧,但需要理论基础,开发复杂。需要注意一点,根据 apple文档提示,在Cocoa下使用pthread需要先启动至少一个NSThread,确定进入多线程环境后才可以。

NSThread是mac OS 10.0后发布的多线程API。较为高层,但是缺乏理工活性,而且和pthread相比效率低下。

Grand Central Dispatch是10.6后引入的开源多线程库,它介于pthread和NSThread之间。比NSThread更灵活、小巧,并且不需要向pthread一样考虑很多锁的问题。同时OC2.0发布的新语法特性之一blocks,也正是根据Grand Central Dispatch需求推出的。

所以在你写多线程代码或者阅读多线程代码的时候,心里要先明确使用哪种技术。

2. 线程和引用计数内存管理造成的问题

              “线程里面的方法都要放到NSAutorealeasePool里面吗?”

进入互联网新媒体时代,“股吧”作为一类专门针对上市公司的社交媒介,已经成为中小投资者分享投资经验和发表对公司运营意见的重要平台,股吧舆论作为投资者情绪的反映,直接影响股票的市场表现。 一、上市公司股吧舆论数据的介绍 “股吧”作为新兴社交媒体代表,本身并不提供信息,仅提供多方交互平台,其将个体间的实时交流和回应形成公众关注和舆论;因此,股吧舆论数据可以帮助研究人员深入分析网络舆论与企业表现之间的关系,并为投资者提供情绪波动的参考依据。 本分享数据年份为2008年到2023年,数据来源于东方财富网股吧,涉及A股上市公司的讨论情况,涵盖了股吧发帖数量、阅读量、评论次数等多个维度。 二、数据指标 指标名称 描述 计算方法 Post 股吧发帖数量 上市公司当年度东方财富网股吧发帖数量之和加1并取自然对数 Positive 正面帖子数量 上市公司当年度东方财富网股吧正面帖子数量之和加1并取自然对数 Negative 负面帖子数量 上市公司当年度东方财富网股吧负面帖子数量之和加1并取自然对数 Neutral 中性帖子数量 上市公司当年度东方财富网股吧中性帖子数量之和加1并取自然对数 Read 股吧阅读量 上市公司当年度东方财富网股吧被阅读次数之和加1并取自然对数 Comment 股吧评论量 上市公司当年度东方财富网股吧被跟帖评论次数之和加1并取自然对数 三、数据说明 本数据集的统计范围为A股上市公司,数据分为三个版本: 未剔除金融STPT未缩尾版本 已剔除金融STPT未缩尾版本 已剔除金融STPT已缩尾版本 数据提供格式:Excel、dta格式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值