iOS直接连接MacOS的MySQL服务器

Posted by Jimney Lee at 2012-02-12 with tags mysql, xcode, macos, iphone

一、在MacOS上搭建MySQL Server,本文采用Mac10.6.x,支持64位

1、根据系统版本和支持位数,下载MySQL Server的dmg文件

2、dmg包里面有三个文件

  • a、安装mysql-5.1.61-osx10.6-x86_64.pkg

  • b、安装MYSQL.prefPane,可以在系统偏好设置(system preference)的其他(Other)栏显示。可以启动和停止Server。

  • c、(可选)安装MySQLStartupItem.pkg支持开机启动

3、将安装目录下support-files目录中的my-small.cnf拷贝到ect中,并更名为my.cnf,MySQL启动时加载的配置文件

$ cd /usr/local/mysql-5.1.61-osx10.6-x86_64
$ sudo cp support-files/my-small.cnf /etc/
$ mv /ect/my-small.cnf /ect/my.cnf

4、初始root密码为空,所以需要设置root密码

$ mysqladmin -u root -p password [password]

5、登录测试,在系统设置偏好(system reference)中MySQL开启运行Server,并做些sql指令操作(本文省去)

$ mysql -h localhost -u root -p

6、关于字节编码需要做一些简单配置修改,初始默认支持latin,所以数据库存入中文utf8后,查询显示为乱码

  • a、显示字符集编码
$ mysql> show variables like'character%';
  • b、修改配置文件,完成后在登录mysql,运行上面的a操作,看字节编码是否已经全部为utf8,参考此处sql编码
$ sudo vi /ect/my.cnf

[client] default-character-set = utf8 [mysqld] default-storage-engine = INNODB character-set-server = utf8 collation-server = utf8_general_ci default-character-set = utf8

至此,完成了MySQL Server的安装与测试。过程跟linux基本一样,都是类unix系统。

二、iOS系统上访问MySQL Server

在iPhone/iPad上访问MySQL Server,是通过官方MySQL Client库来实现。iOS完全兼容C语言,所以通过官方的Client源码生成静态库来调用。 我们将制作这个静态库libmysqlclient.a,支持Simulator和iOS设备。

1、下载client的源码,当前版本(mysql-connector-c-6.0.2.tar.gz)

2、源码采用cmake进行编译,所以请下载最新的cmake编译工具

3、通过终端进入到mysql-connector-c-6.0.2目录下,运行下面命令,生成XCode工程

$ cmake -G Xcode 在这里推荐一个MacOS开源插件[cdto](https://github.com/jbtule/cdto)。类似ubuntu系统下,在可视化的目录窗口,终端进入直接对应的目录路径,挺方便的。

4、修改一些文件,不然编译会出错

  • a、在my_net.h文件中,注销掉47和49行
//#include <netinet/in_systm.h>
#include <netinet/in.h>
//#include <netinet/ip.h>
  • b、在my_global.h,129行编译宏添加arm编译选择
#if defined(__i386__) || defined(__ppc__) || defined(__arm__)
  • c、在my_config.h,注销掉63行

5、通过xcodebuild命令为simulator和ios设备,生成对应的编译库,然后通过lipo生成一个统一包。首先在终端(terminal)cd到源码的libmysql路径下,运行如下命令:

  • a、生成simulator静态包,注意sdk选择与mac系统中一致
$ xcodebuild -project ../libmysql.xcodeproj -target mysqlclient -configuration Release -sdk iphonesimulator4.3 ONLY_ACTIVE_ARCH=NO ARCHS=i386 PRODUCT_NAME=mysqlclient_simulator
  • b、生成ios设备静态包
$ xcodebuild -project ../libmysql.xcodeproj -target mysqlclient -configuration Release -sdk iphoneos4.3 ONLY_ACTIVE_ARCH=NO ARCHS="armv6 armv7" PRODUCT_NAME=mysqlclient_device
  • c、使用lipo打在一起,保存在Release-iphoneos路径下
$ lipo ./Release-iphonesimulator/libmysqlclient_simulator.a ./Release-iphoneos/libmysqlclient_device.a -create -output ./Release-iphoneos/libmysqlclient.a

至此所用的静态包已经打好,库头文件为源码目录中的include目录下所有的头文件。

注意:今天测试发现,在lion系统下打的SDK 5.0的libmysqlclient.a到Snow Leaporad编译出错,无法使用,所以在Snow Leaporad下重新编译这个库,可以正常使用,可能跟上面编译选项有关,如果有路过的兄弟知道原因,请不吝赐教。

三、访问测试,使用库测试参考MYSQL C API

通过XCode建立一个视图工程,在视图viewDidLoad中添加如下代码:

#import "mysql.h"
MYSQL *mysql = mysql_init(nil);
if(mysql_real_connect(mysql, localhost, username, passwd, dbname, 3306, NULL, 0)) {
    //set encode utf8
    mysql_set_character_set(mysql, "utf8");
    NSLog(@"Connection Successful!");
    //TODO:CRUD
}
else {
    NSLog(@"Connection Failed");
}

后记

今天发现这些东西梳理出来写成博文的过程,跟code有一样的体验。理清思路,把知道的东西简洁表达而不被误解,还是要有一些思考和推敲。好长时间没写东西,纲领概要啥都无知,今后也要多写写。软件开发忌讳重复劳动,这意味额外的代价。人生短暂,伤不起也损不起。如果这篇博文能为您节省生命,乃我大幸大乐也!

解决three20在sdk5.0编译时tableView出现Empty section header

Posted by Jimney Lee at 2011-12-29 with tags three20, iphone

如题,three20的tableview在最新的sdk5.0中,即使未设置section的title,tableview头部仍然会显示一个空的sectionview,在网上瞎搜索几天,今天终于找到解决方法,e文不好,贴在这里也给其他遇到同样问题的朋友参考下。 参考git上320讨论组: https://github.com/facebook/three20/issues/694 解决方法,简译一下: 为TTTableViewDelegate方法创建一个category(类别),实现 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 获取section头高度的代理方法 TTTableViewDelegate+EmptySectionHeader.m如下:

#import "TTTableViewDelegate+EmptySectionHeader.h"
 
@implementation TTTableViewDelegate (EmptySectionHeader)
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    id<TTTableViewDataSource> dataSource = (id<TTTableViewDataSource>)tableView.dataSource;
    if ([dataSource isKindOfClass:[TTListDataSource class]])
        return 0.;
    return 20.;
}
@end

update 2011-12-29: 原来的方法使用比较受限制,对于自定义的datesource无法处理,而且比较麻烦。但是我们可以修改一下这个代理方法的实现过程,参考three20中TTTableViewGroupedVarHeightDelegate中这个代理方法的实现,修改如下:

#import "TTTableViewDelegate+EmptySectionHeader.h"
static const CGFloat kEmptyHeaderHeight = 0;
static const CGFloat kSectionHeaderHeight = 18;
 
static const CGFloat kGroupEmptyHeaderHeight = 5;
static const CGFloat kGroupSectionHeaderHeight = 35;
 
@implementation TTTableViewDelegate (EmptySectionHeader)
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (tableView.style == UITableViewStylePlain) {
        if ([tableView.dataSource respondsToSelector:@selector(tableView:titleForHeaderInSection:)]) {
            NSString* title = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
            if (!title.length) {
                return kEmptyHeaderHeight;
            } else {
                return kSectionHeaderHeight;
            }
        }
        else {
            return kEmptyHeaderHeight;
        }
    }
    else {
        if ([tableView.dataSource respondsToSelector:@selector(tableView:titleForHeaderInSection:)]) {
            NSString* title = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
            if (!title.length) {
                return kGroupEmptyHeaderHeight;
            }
            else {
                return kGroupSectionHeaderHeight;
            }
        }
        else {
            return kGroupEmptyHeaderHeight;
        }
    }
}
@end

UIImage的一个category:缩放图片限制横宽区域内

Posted by Jimney Lee at 2011-05-20 with tags iphone

/*
 * 缩放图片限制横宽区域内
 */
- (UIImage*)scaleWithSize:(CGSize)size {
    CGFloat imageWidth = self.size.width;
    CGFloat imageHeight = self.size.height;
 
    CGFloat wScale = imageWidth / size.width;
    CGFloat hScale = imageHeight / size.height;
    NSLog(@"ws = %f, hs = %f", wScale, hScale);
     
    CGSize newSize = self.size;
    BOOL needRedraw;
    if (wScale > hScale && wScale > 1) {
        newSize.width = size.width;
        newSize.height = imageHeight / wScale;
        needRedraw = YES;
    }
    else if (hScale > wScale && hScale > 1) {
        newSize.width = imageWidth / hScale;
        newSize.height = size.height;
        needRedraw = YES;
    }
     
    NSLog(@"w = %f, h = %f", newSize.width, newSize.height);
     
    if (needRedraw) {
        UIGraphicsBeginImageContext(newSize);
        [self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
        self = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
 
    return self;
}

赞一个开源库:720全景显示

Posted by Jimney Lee at 2011-01-05 with tags iphone, panorama

展示的效果跟香港720一样,感谢老外的开源精神。

google代码首页:

http://code.google.com/p/panoramagl/

库使用详细介绍:

http://www.codeproject.com/KB/iPhone/panoramagl.aspx

功能介绍:

  • 1、支持720全景展示
  • 2、支持重力加速度感应控制

遇到主要问题及解决方法:

1、报错如: >No architectures to compile for (ARCHS=armv6 armv7, VALID_ARCHS=i386 ppc ppc64 ppc7400 ppc970 x86_64).

需要添加armv6和armv7,如下图:

2、glu.h链接文件出错,需要include "glues.h"

#ifndef __glu_h__
#define __glu_h__
#include "glues.h"
#endif

3、报错如:

Undefined symbols: “OBJC_CLASS$_PLTexture”, referenced from: objc-class-ref-to-PLTexture in HelloPanoramaViewController.o ld: symbol(s) not found

一般这个问题由于添加PanoramaGL.xcodeprojHelloPanorama项目中时,忘了勾选库的右上角的圆框,如下图:

也就是stackoverflow上这句话的意思,但是因为没图,其他人看了很不理解,包括我 :( >I had the same problem, and noticed that although I included PanoramaGL xcode project, its lib (libPanoramaGL.a) was not selected to build with the target (that >little checkbox in the top right list of >XCode).

其实这句话意思是:这个.a静态库没有被选中,导致不能被编译到目标程序中。 右键.a文件,get info -->Targets,多targets项目,可以指定一个静态库为那几个targets所有,同样的道理使用于程序源文件和资源文件,看了下面这幅图就更加理解了

记录一个低级错误

Posted by Jimney Lee at 2010-11-19 with tags iphone, debug

程序中需要在视图上重复添加新的子层CATiledLayer类对象来显示不同的pdf文件,因为层对象采用[CATiledLayer layer]静态方法来获取内存,心里想当然认为子层对象的生命周期不需要我再管了,每次都采用此静态方法产生新的子层对象,然后添加到当前视图的根层上。但是下班前突然发现打开七八张pdf文件后,机子就罢工,显示内存警告然后翘辫子了。

第一时间我就想到leak工具,但是悲剧的是,并没有内存泄露提示。接着google了半夜,看了千层帖,终无果!浑天暗地之际,突然醒悟,addSublayer到父层,会对子层retain一次,所以每次新开辟前,需要[tiledLayer removeFromSuperlayer]来释放前一次的层对象。 总结:

  • 1、写程序是穿线的活,心细不能想当然。
  • 2、leak工具不是万能,关键时还需冷静分析。
  • 3、调试不是自己写的程序,很费神。
  • 4、头昏时就休息,不然效率低下,而且伤身体。

PS:不是因为紧急,偶是不会熬通宵,这是偶上班来的处女夜,竟然竟为此差错,不谈也罢,只为纪念!欢迎拍砖!

iPhone 锁屏后事件的处理

Posted by Jimney Lee at 2010-09-14 with tags iphone

今天测试程序发现,在一代机上,按了最上面的屏保键,然后回到程序界面,机子就死掉了,不过ipad每这个问题。 请教: 这个问题是一代机子本身问题,还是我的程序问题? 如果是机子问题,我在程序中怎么避免,在哪边可以获得外界屏保设置的消息?不甚感谢!

PS(2010-09-16):=============================================

之前的标题:iphone一代机,屏保后,程序死掉,机也完蛋 问题描述: 可能是我之前的描述有问题吧,其实我遇到的问题就是当人为按了最上面的锁屏键,解锁后回到程序时,一操作就会发生死机,郁闷一天! 后来经过分析调试,是主界面背景音乐的循环播放造成的,机器锁屏后,程序依旧运行,只是声音被系统暂停。 但是在我程序中,循环播放的背景音乐,因为锁屏、解锁,系统暂停再开启就会导致系统挂掉。 解决思路: 既然是因为锁屏,系统暂停我程序音乐播放导致的,而我程序内部对声音的暂停和恢复是没问题的。 所以我最初的思路就是不让系统暂停我的背景声音,通过获得锁屏后的事件,程序自己来暂停声音,然后锁屏后再恢复播放。沿着这个思路,找到这个触发的事件是王道。

未完待续,肚子催我回去了,明天补充!

PS (2010-09-17):================================================

深入理解iPhone委托模式兼谈iPhone生命周期

从cc这篇文章中我了解到(PS:感谢文章作者,同时也非常感谢版主们的辛苦整理),程序的主代理UIApplicationDelegate接口提供给生命周期函数来处理应用程序以及应用程序的系统事件,也就是说UIApplicationDelegate代理能够处理系统发给应用程序的消息,那么我要找的锁屏后系统触发的事件,应该在这个主代理所能处理的生命周期函数中处理。

通过log输出发现,下面几个函数中能够输出信息:

- (void)applicationWillBecomeActive::(UIApplication *)application;
- (void)applicationWillResignActive:(UIApplication *)application;

- (void)applicationDidBecomeActive:(UIApplication *)application;
- (void)applicationDidResignActive:(UIApplication *)application;   从官方的SDK中得知,系统发送改变应用程序活动状态的事件,可以通过这两个函数处理。可以是一个来电或者SMS事件,当然还有我遇到的这个系统锁屏事件。 譬如:游戏正在运行,有来电了,你可以先暂停游戏,等通话结束,再恢复游戏。那我遇到的这个声音恢复导致死机问题,就可以自己来暂停和恢复背景声音。 问题是,我要在程序的主代理中`UIApplicationDelegate`处理这个事件很不方便,我的播放背景声音是在主Controller的子Controller中定义的,如果能在子Controller中处理这个事件,问题就容易多了。如何实现呢?

SDK文档这句话给了我启示:

applicationWillResignActive: Sent by the default notification center immediately before the application is deactivated. A notification named NSApplicationWillResignActiveNotification. Calling the object method of this notification returns the NSApplication object itself.

NSApplicationWillResignActiveNotification是系统变成不活动(inactive)状态的消息,通过默认的notification center能够处理这个消息。对这个概念的理解,还得感谢CC的babylon_0049朋友乐于分享他自己的一些理解和分析,这在帮助我解决这个问题,启到了很大的提示。

cc地址 同时关于UIApplicationDidBecomeActiveNotification,文档里有这句话:

when the device is locked, and gains focus when the device is unlocked

这使我解决问题的方向更加明确具体。

呵呵,到这里,解决方法就不言而喻了! 在自控制器或者视图的初始化内添加如下语句:

//把self添加到NSNotificationCenter的观察者中
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(becomeActive) 
                                             name:UIApplicationDidBecomeActiveNotification 
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(resignActive) 
                                             name:UIApplicationWillResignActiveNotification 
                                           object:nil];

响应的动作函数:

/*
 *程序活动的动作
 */
- (void)becomeActive
{
    //创建新的定时器
    //继续播放声音,stop后currentTime值不变,直接使用play
    //TODO:
}    
/*
 *程序不活动的动作
 */
- (void)resignActive
{
    //关闭定时器
    //停止背景播放声音,个人测试发现最好使用stop方法
    //TODO:
} 与大家一起分享,希望能给没有接触这部分知识的朋友有一点帮助! cc地址:[http://www.cocoachina.com/bbs/read.php?tid=32245](http://www.cocoachina.com/bbs/read.php?tid=32245)