分类目录归档:Mac

iPhone开发:Working With USB Device Interfaces

本文来自apple开发者网站:http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html#//apple_ref/doc/uid/TP40002645-TPXREF101

讲述了如何通过usb与外围设备交互信息,操作usb接口示例、源代码,非常有用。

This chapter describes how to develop a user-space tool that finds and communicates with an attached USB device and one of its interfaces. The sample code in this chapter is taken from the USB Notification Example sample project, available in /Developer/Examples/IOKit/usb of the Developer version of Mac OS X.

Using USB Device Interfaces

Applications running in Mac OS X get access to USB devices by using I/O Kit functions to acquire a device interface, a type of plug-in that specifies functions the application can call to communicate with the device. The USB family provides two types of device interface:

  • IOUSBDeviceInterface for communicating with the device itself
  • IOUSBInterfaceInterface for communicating with an interface in the device

Both device interfaces are defined in /System/Library/Frameworks/IOKit.framework/Headers/usb/IOUSBLib.h.

Communicating with the device itself is usually only necessary when you need to set or change its configuration. For example, vendor-specific devices are often not configured because there are no default drivers that set a particular configuration. In this case, your application must use the device interface for the device to set the configuration it needs so the interfaces become available.

The process of finding and communicating with a USB device is divided into two sets of steps. The first set outlines how to find a USB device, acquire a device interface of type IOUSBDeviceInterface for it, and set or change its configuration. The second set describes how to find an interface in a device, acquire a device interface of typeIOUSBInterfaceInterface for it, and use it to communicate with that interface. If you need to communicate with an unconfigured device or if you need to change a device’s configuration, you follow both sets of steps. If you need to communicate with a device that is already configured to your specification, you follow only the second set of steps. The sample code in “Accessing a USB Device” follows both sets of steps and extends them to include setting up notifications it can receive when devices are dynamically added or removed.

Follow this first set of steps only to set or change the configuration of a device. If the device you’re interested in is already configured for your needs, skip these steps and follow the second set of steps.

  1. Find the IOUSBDevice object that represents the device in the I/O Registry. This includes setting up a matching dictionary with a key from the USB Common Class Specification (see “Finding USB Devices and Interfaces”). The sample code uses the key elements kUSBVendorName and kUSBProductName to find a particular USB device (this is the second key listed in Table 1-2).
  2. Create a device interface of type IOUSBDeviceInterface for the device. This device interface provides functions that perform tasks such as setting or changing the configuration of the device, getting information about the device, and resetting the device.
  3. Examine the device’s configurations with GetConfigurationDescriptorPtr, choose the appropriate one, and call SetConfiguration to set the device’s configuration and instantiate the IOUSBInterface objects for that configuration.

Follow this second set of steps to find and choose an interface, acquire a device interface for it, and communicate with the device.

  1. Create an interface iterator to iterate over the available interfaces.
  2. Create a device interface for each interface so you can examine its properties and select the appropriate one. To do this, you create a device interface of typeIOUSBInterfaceInterface. This device interface provides functions that perform tasks such as getting information about the interface, setting the interface’s alternate setting, and accessing its pipes.
  3. Use the USBInterfaceOpen function to open the selected interface. This will cause the pipes associated with the interface to be instantiated so you can examine the properties of each and select the appropriate one.
  4. Communicate with the device through the selected pipe. You can write to and read from the pipe synchronously or asynchronously—the sample code in“Accessing a USB Device” shows how to do both.

Accessing a USB Device

This section provides snippets of sample code that show how to access a Cypress EZ-USB chip with an 8051 microcontroller core. The sample code follows the first set of steps in section “Using USB Device Interfaces” to find the Cypress EZ-USB chip in its default, unprogrammed state (also referred to as the “raw device”). It then configures the device and downloads firmware provided by Cypress to program the chip to behave as a device that echoes all information it receives on its bulk out pipe to its bulk in pipe.

Once the chip has been programmed, the device nub representing the default, unprogrammed device is detached from the I/O Registry and a new device nub, representing the programmed chip, is attached. To communicate with the programmed chip (also referred to as the “bulk test device”), the sample code must perform the first set of steps again to find the device, create a device interface for it, and configure it. Then it performs the second set of steps to find an interface, create a device interface for it, and test the device. The sample code also shows how to set up notifications for the dynamic addition and removal of a device.

If you want to build and run the sample code, open the USB Notification Example project (located in /Developer/Examples/IOKit/usb) in Xcode. You can observe the resulting tool’s status messages in the Xcode Run Log and terminate the tool by clicking the Terminate button. Alternatively, you can open a Terminal window, navigate to the location of the executable (for example, /Developer/Examples/IOKit/usb/USBNotification Example/build), run the tool, and terminate it by pressing Control-C. Note that the project also includes the bulktest.c file, which contains the firmware to download to the raw device, and the hex2c.h file, which defines the structure the firmware uses.

Definitions and Global Variables

The code in the USB Notification Example uses the definitions and global variables shown in Listing 2-1. The definition of USE_ASYNC_IO allows you to choose to use either synchronous or asynchronous calls to read from and write to the chip by commenting out the line or leaving it in, respectively. The definition of kTestMessagesets up a simple message to write to the device. The remaining definitions are specific to the Cypress EZ-USB chip.

Listing 2-1  Definitions and global variables

#define USE_ASYNC_IO    //Comment this line out if you want to use
                        //synchronous calls for reads and writes
#define kTestMessage        "Bulk I/O Test"
#define k8051_USBCS         0x7f92
#define kOurVendorID        1351    //Vendor ID of the USB device
#define kOurProductID           8193    //Product ID of device BEFORE it
                                        //is programmed (raw device)
#define kOurProductIDBulkTest   4098    //Product ID of device AFTER it is
                                        //programmed (bulk test device)
//Global variables
static IONotificationPortRef    gNotifyPort;
static io_iterator_t            gRawAddedIter;
static io_iterator_t            gRawRemovedIter;
static io_iterator_t            gBulkTestAddedIter;
static io_iterator_t            gBulkTestRemovedIter;
static char                     gBuffer[64];

The main Function

The main function in the USB Notification Example project (contained in the file main.c) accomplishes the following tasks.

  • It establishes communication with the I/O Kit and sets up a matching dictionary to find the Cypress EZ-USB chip.
  • It sets up an asynchronous notification to be called when an unprogrammed (raw) device is first attached to the I/O Registry and another to be called when the device is removed.
  • It modifies the matching dictionary to find the programmed (bulk test) device.
  • It sets up additional notifications to be called when the bulk test device is first attached or removed.
  • It starts the run loop so the notifications that have been set up will be received.

The main function uses I/O Kit functions to set up and modify a matching dictionary and set up notifications, and Core Foundation functions to set up the run loop for receiving the notifications. It calls the following functions to access both the raw device and the bulk test device.

  • RawDeviceAdded, shown in Listing 2-3, iterates over the set of matching devices and creates a device interface for each one. It calls ConfigureDevice (shown inListing 2-5) to set the device’s configuration, and then DownloadToDevice (shown in Listing 2-6) to download the firmware to program it.
  • RawDeviceRemoved, shown in Listing 2-4, iterates over the set of matching devices and releases each one in turn.
  • BulkTestDeviceAdded, shown in Listing 2-7, iterates over the new set of matching devices, creates a device interface for each one, and calls ConfigureDevice(shown in Listing 2-5) to set the device’s configuration. It then calls FindInterfaces (shown in Listing 2-8) to get access to the interfaces on the device.
  • BulkTestDeviceRemoved iterates over the new set of matching devices and releases each one in turn. This function is not shown in this chapter; seeRawDeviceRemoved (Listing 2-4) for a nearly identical function.

Listing 2-2  The main function

int main (int argc, const char *argv[])
{
    mach_port_t             masterPort;
    CFMutableDictionaryRef  matchingDict;
    CFRunLoopSourceRef      runLoopSource;
    kern_return_t           kr;
    SInt32                  usbVendor = kOurVendorID;
    SInt32                  usbProduct = kOurProductID;
    // Get command line arguments, if any
    if (argc > 1)
        usbVendor = atoi(argv[1]);
    if (argc > 2)
        usbProduct = atoi(argv[2]);
    //Create a master port for communication with the I/O Kit
    kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
    if (kr || !masterPort)
    {
        printf("ERR: Couldn’t create a master I/O Kit port(%08x)\n", kr);
        return -1;
    }
    //Set up matching dictionary for class IOUSBDevice and its subclasses
    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    if (!matchingDict)
    {
        printf("Couldn’t create a USB matching dictionary\n");
        mach_port_deallocate(mach_task_self(), masterPort);
        return -1;
    }
    //Add the vendor and product IDs to the matching dictionary.
    //This is the second key in the table of device-matching keys of the
    //USB Common Class Specification
    CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorName),
                        CFNumberCreate(kCFAllocatorDefault,
                                     kCFNumberSInt32Type, &usbVendor));
    CFDictionarySetValue(matchingDict, CFSTR(kUSBProductName),
                        CFNumberCreate(kCFAllocatorDefault,
                                    kCFNumberSInt32Type, &usbProduct));
    //To set up asynchronous notifications, create a notification port and
    //add its run loop event source to the program’s run loop
    gNotifyPort = IONotificationPortCreate(masterPort);
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
                        kCFRunLoopDefaultMode);
    //Retain additional dictionary references because each call to
    //IOServiceAddMatchingNotification consumes one reference
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
    matchingDict = (CFMutableDictionaryRef) CFRetain(matchingDict);
    //Now set up two notifications: one to be called when a raw device
    //is first matched by the I/O Kit and another to be called when the
    //device is terminated
    //Notification of first match:
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOFirstMatchNotification, matchingDict,
                    RawDeviceAdded, NULL, &gRawAddedIter);
    //Iterate over set of matching devices to access already-present devices
    //and to arm the notification
    RawDeviceAdded(NULL, gRawAddedIter);
    //Notification of termination:
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOTerminatedNotification, matchingDict,
                    RawDeviceRemoved, NULL, &gRawRemovedIter);
    //Iterate over set of matching devices to release each one and to
    //arm the notification
    RawDeviceRemoved(NULL, gRawRemovedIter);
    //Now change the USB product ID in the matching dictionary to match
    //the one the device will have after the firmware has been downloaded
    usbProduct = kOurProductIDBulkTest;
    CFDictionarySetValue(matchingDict, CFSTR(kUSBProductName),
                        CFNumberCreate(kCFAllocatorDefault,
                                    kCFNumberSInt32Type, &usbProduct));
    //Now set up two notifications: one to be called when a bulk test device
    //is first matched by the I/O Kit and another to be called when the
    //device is terminated.
    //Notification of first match
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOFirstMatchNotification, matchingDict,
                    BulkTestDeviceAdded, NULL, &gBulkTestAddedIter);
    //Iterate over set of matching devices to access already-present devices
    //and to arm the notification
    BulkTestDeviceAdded(NULL, gBulkTestAddedIter);
    //Notification of termination
    kr = IOServiceAddMatchingNotification(gNotifyPort,
                    kIOTerminatedNotification, matchingDict,
                    BulkTestDeviceRemoved, NULL, &gBulkTestRemovedIter);
    //Iterate over set of matching devices to release each one and to
    //arm the notification. NOTE: this function is not shown in this document.
    BulkTestDeviceRemoved(NULL, gBulkTestRemovedIter);
    //Finished with master port
    mach_port_deallocate(mach_task_self(), masterPort);
    masterPort = 0;
    //Start the run loop so notifications will be received
    CFRunLoopRun();
    //Because the run loop will run forever until interrupted,
    //the program should never reach this point
    return 0;
}

Working With the Raw Device

Now that you’ve obtained an iterator for a set of matching devices, you can use it to gain access to each raw device, configure it, and download the appropriate firmware to it. The function RawDeviceAdded (shown in Listing 2-3) uses I/O Kit functions to create a device interface for each device and then calls the following functions to configure the device and download firmware to it.

  • ConfigureDevice, shown in Listing 2-5, uses device interface functions to get the number of configurations, examine the first one, and set the device’s configuration.
  • DownloadToDevice, shown in Listing 2-6, downloads the firmware in bulktest.c to the device.

Listing 2-3  Accessing and programming the raw device

void RawDeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t               kr;
    io_service_t                usbDevice;
    IOCFPlugInInterface         **plugInInterface = NULL;
    IOUSBDeviceInterface        **dev = NULL;
    HRESULT                     result;
    SInt32                      score;
    UInt16                      vendor;
    UInt16                      product;
    UInt16                      release;
    while (usbDevice = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in
        kr = IOCreatePlugInInterfaceForService(usbDevice,
                    kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
                    &plugInInterface, &score);
        //Don’t need the device object after intermediate plug-in is created
        kr = IOObjectRelease(usbDevice);
        if ((kIOReturnSuccess != kr) || !plugInInterface)
        {
            printf("Unable to create a plug-in (%08x)\n", kr);
            continue;
        }
        //Now create the device interface
        result = (*plugInInterface)->QueryInterface(plugInInterface,
                        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                        (LPVOID *)&dev);
        //Don’t need the intermediate plug-in after device interface
        //is created
        (*plugInInterface)->Release(plugInInterface);
        if (result || !dev)
        {
            printf("Couldn’t create a device interface (%08x)\n",
                                                    (int) result);
            continue;
        }
        //Check these values for confirmation
        kr = (*dev)->GetDeviceVendor(dev, &vendor);
        kr = (*dev)->GetDeviceProduct(dev, &product);
        kr = (*dev)->GetDeviceReleaseNumber(dev, &release);
        if ((vendor != kOurVendorID) || (product != kOurProductID) ||
            (release != 1))
        {
            printf("Found unwanted device (vendor = %d, product = %d)\n",
                    vendor, product);
            (void) (*dev)->Release(dev);
            continue;
        }
        //Open the device to change its state
        kr = (*dev)->USBDeviceOpen(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to open device: %08x\n", kr);
            (void) (*dev)->Release(dev);
            continue;
        }
        //Configure device
        kr = ConfigureDevice(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to configure device: %08x\n", kr);
            (void) (*dev)->USBDeviceClose(dev);
            (void) (*dev)->Release(dev);
            continue;
        }
        //Download firmware to device
        kr = DownloadToDevice(dev);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to download firmware to device: %08x\n", kr);
            (void) (*dev)->USBDeviceClose(dev);
            (void) (*dev)->Release(dev);
            continue;
        }
        //Close this device and release object
        kr = (*dev)->USBDeviceClose(dev);
        kr = (*dev)->Release(dev);
    }
}

The function RawDeviceRemoved simply uses the iterator obtained from the main function (shown in Listing 2-2) to release each device object. This also has the effect of arming the raw device termination notification so it will notify the program of future device removals. RawDeviceRemoved is shown in Listing 2-4.

Listing 2-4  Releasing the raw device objects

void RawDeviceRemoved(void *refCon, io_iterator_t iterator)
{
    kern_return_t   kr;
    io_service_t    object;
    while (object = IOIteratorNext(iterator))
    {
        kr = IOObjectRelease(object);
        if (kr != kIOReturnSuccess)
        {
            printf("Couldn’t release raw device object: %08x\n", kr);
            continue;
        }
    }
}

Although every USB device has one or more configurations, unless the device is a composite class device that’s been matched by the AppleUSBComposite driver which automatically sets the first configuration, none of those configurations may have been set. Therefore, your application may have to use device interface functions to get the appropriate configuration value and use it to set the device’s configuration. In the sample code, the function ConfigureDevice (shown in Listing 2-5) accomplishes this task. In fact, it is called twice: once by RawDeviceAdded to configure the raw device and again by BulkTestDeviceAdded (shown in Listing 2-7) to configure the bulk test device.

Listing 2-5  Configuring a USB device

IOReturn ConfigureDevice(IOUSBDeviceInterface **dev)
{
    UInt8                           numConfig;
    IOReturn                        kr;
    IOUSBConfigurationDescriptorPtr configDesc;
    //Get the number of configurations. The sample code always chooses
    //the first configuration (at index 0) but your code may need a
    //different one
    kr = (*dev)->GetNumberOfConfigurations(dev, &numConfig);
    if (!numConfig)
        return -1;
    //Get the configuration descriptor for index 0
    kr = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &configDesc);
    if (kr)
    {
        printf("Couldn’t get configuration descriptor for index %d (err =
                %08x)\n", 0, kr);
        return -1;
    }
    //Set the device’s configuration. The configuration value is found in
    //the bConfigurationValue field of the configuration descriptor
    kr = (*dev)->SetConfiguration(dev, configDesc->bConfigurationValue);
    if (kr)
    {
        printf("Couldn’t set configuration to value %d (err = %08x)\n", 0,
                kr);
        return -1;
    }
    return kIOReturnSuccess;
}

Now that the device is configured, you can download firmware to it. Cypress makes firmware available to program the EZ-USB chip to emulate different devices. The sample code in this document uses firmware that programs the chip to be a bulk test device, a device that takes the data it receives from its bulk out pipe and echoes it to its bulk in pipe. The firmware, contained in the file bulktest.c, is an array of INTEL_HEX_RECORD structures (defined in the file hex2c.h).

The function DownloadToDevice uses the function WriteToDevice (shown together in Listing 2-6) to prepare the device to receive the download and then to write information from each structure to the appropriate address on the device. When all the firmware has been downloaded, DownloadToDevice calls WriteToDevice a last time to inform the device that the download is complete. At this point, the raw device detaches itself from the bus and reattaches as a bulk test device. This causes the device nub representing the raw device to be removed from the I/O Registry and a new device nub, representing the bulk test device, to be attached.

Listing 2-6  Two functions to download firmware to the raw device

IOReturn DownloadToDevice(IOUSBDeviceInterface **dev)
{
    int         i;
    UInt8       writeVal;
    IOReturn    kr;
    //Assert reset. This tells the device that the download is
    //about to occur
    writeVal = 1;   //For this device, a value of 1 indicates a download
    kr = WriteToDevice(dev, k8051_USBCS, 1, &writeVal);
    if (kr != kIOReturnSuccess)
    {
        printf("WriteToDevice reset returned err 0x%x\n", kr);
        (*dev)->USBDeviceClose(dev);
        (*dev)->Release(dev);
        return kr;
    }
    //Download firmware
    i = 0;
    while (bulktest[i].Type == 0)   //While bulktest[i].Type == 0, this is
    {                               //not the last firmware record to
                                    //download
        kr = WriteToDevice(dev, bulktest[i].Address,
                            bulktest[i].Length, bulktest[i].Data);
        if (kr != kIOReturnSuccess)
        {
            printf("WriteToDevice download %i returned err 0x%x\n", i,
                    kr);
            (*dev)->USBDeviceClose(dev);
            (*dev)->Release(dev);
            return kr;
        }
        i++;
    }
    //De-assert reset. This tells the device that the download is complete
    writeVal = 0;
    kr = WriteToDevice(dev, k8051_USBCS, 1, &writeVal);
    if (kr != kIOReturnSuccess)
        printf("WriteToDevice run returned err 0x%x\n", kr);
    return kr;
}
IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress,
                        UInt16 length, UInt8 writeBuffer[])
{
    IOUSBDevRequest     request;
    request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
                                                kUSBDevice);
    request.bRequest = 0xa0;
    request.wValue = deviceAddress;
    request.wIndex = 0;
    request.wLength = length;
    request.pData = writeBuffer;
    return (*dev)->DeviceRequest(dev, &request);
}

Working With the Bulk Test Device

After you download the firmware to the device, the raw device is no longer attached to the bus. To gain access to the bulk test device, you repeat most of the same steps you used to get access to the raw device.

  • Use the iterator obtained by a call to IOServiceAddMatchingNotification in the main function (shown in Listing 2-2) to iterate over a set of matching devices.
  • Create a device interface for each device.
  • Configure the device.

This time, however, the next step is to find the interfaces on the device so you can choose the appropriate one and get access to its pipes. Because of the similarities of these tasks, the function BulkTestDeviceAdded follows the same outline of the RawDeviceAdded function except that instead of downloading firmware to the device, it calls FindInterfaces (shown in Listing 2-8) to examine the available interfaces and their pipes. The code in Listing 2-7 replaces most of theBulkTestDeviceAdded function’s code with comments, focusing on the differences between it and the RawDeviceAdded function.

Listing 2-7  Accessing the bulk test device

void BulkTestDeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t           kr;
    io_service_t            usbDevice;
    IOUSBDeviceInterface    **device=NULL;
    while (usbDevice = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in using the
        //IOCreatePlugInInterfaceForService function
        //Release the device object after getting the intermediate plug-in
        //Create the device interface using the QueryInterface function
        //Release the intermediate plug-in object
        //Check the vendor, product, and release number values to
        //confirm we’ve got the right device
        //Open the device before configuring it
        kr = (*device)->USBDeviceOpen(device);
        //Configure the device by calling ConfigureDevice
        //Close the device and release the device interface object if
        //the configuration is unsuccessful
        //Get the interfaces
        kr = FindInterfaces(device);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to find interfaces on device: %08x\n", kr);
            (*device)->USBDeviceClose(device);
            (*device)->Release(device);
            continue;
        }
//If using synchronous IO, close and release the device interface here
#ifndef USB_ASYNC_IO
        kr = (*device)->USBDeviceClose(device);
        kr = (*device)->Release(device);
#endif
    }
}

The function BulkTestDeviceRemoved simply uses the iterator obtained from the main function (shown in Listing 2-2) to release each device object. This also has the effect of arming the bulk test device termination notification so it will notify the program of future device removals.The BulkTestDeviceRemoved function is identical to the RawDeviceRemoved function (shown in Listing 2-4), with the exception of the wording of the printed error statement.

Working With Interfaces

Now that you’ve configured the device, you have access to its interfaces. The FindInterfaces function (shown in Listing 2-8) creates an iterator to iterate over all interfaces on the device and then creates a device interface to communicate with each one. For each interface found, the function opens the interface, determines how many endpoints (or pipes) it has, and prints out the properties of each pipe. Because opening an interface causes its pipes to be instantiated, you can get access to any pipe by using its pipe index. The pipe index is the number of the pipe within the interface, ranging from one to the number of endpoints returned by GetNumEndpoints. You can communicate with the default control pipe (described in “USB Transfer Types”) from any interface by using pipe index 0, but it is usually better to use the device interface functions for the device itself (see the use of IOUSBDeviceInterface functions in Listing 2-5).

The sample code employs conditional compilation using #ifdef and #ifndef to demonstrate both synchronous and asynchronous I/O. If you’ve chosen to test synchronous I/O, FindInterfaces writes the test message (defined in Listing 2-1) to pipe index 2 on the device and reads its echo before returning. For asynchronous I/O, FindInterfaces first creates an event source and adds it to the run loop created by the main function (shown in Listing 2-2). It then sets up an asynchronous write and read that will cause a notification to be sent upon completion. The completion functions WriteCompletion and ReadCompletion are shown together inListing 2-9.

Listing 2-8  Finding interfaces on the bulk test device

IOReturn FindInterfaces(IOUSBDeviceInterface **device)
{
    IOReturn                    kr;
    IOUSBFindInterfaceRequest   request;
    io_iterator_t               iterator;
    io_service_t                usbInterface;
    IOCFPlugInInterface         **plugInInterface = NULL;
    IOUSBInterfaceInterface     **interface = NULL;
    HRESULT                     result;
    SInt32                      score;
    UInt8                       interfaceClass;
    UInt8                       interfaceSubClass;
    UInt8                       interfaceNumEndpoints;
    int                         pipeRef;
#ifndef USE_ASYNC_IO
    UInt32                      numBytesRead;
    UInt32                      i;
#else
    CFRunLoopSourceRef          runLoopSource;
#endif
    //Placing the constant kIOUSBFindInterfaceDontCare into the following
    //fields of the IOUSBFindInterfaceRequest structure will allow you
    //to find all the interfaces
    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
    //Get an iterator for the interfaces on the device
    kr = (*device)->CreateInterfaceIterator(device,
                                        &request, &iterator);
    while (usbInterface = IOIteratorNext(iterator))
    {
        //Create an intermediate plug-in
        kr = IOCreatePlugInInterfaceForService(usbInterface,
                            kIOUSBInterfaceUserClientTypeID,
                            kIOCFPlugInInterfaceID,
                            &plugInInterface, &score);
        //Release the usbInterface object after getting the plug-in
        kr = IOObjectRelease(usbInterface);
        if ((kr != kIOReturnSuccess) || !plugInInterface)
        {
            printf("Unable to create a plug-in (%08x)\n", kr);
            break;
        }
        //Now create the device interface for the interface
        result = (*plugInInterface)->QueryInterface(plugInInterface,
                    CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
                    (LPVOID *) &interface);
        //No longer need the intermediate plug-in
        (*plugInInterface)->Release(plugInInterface);
        if (result || !interface)
        {
            printf("Couldn’t create a device interface for the interface
                    (%08x)\n", (int) result);
            break;
        }
        //Get interface class and subclass
        kr = (*interface)->GetInterfaceClass(interface,
                                                    &interfaceClass);
        kr = (*interface)->GetInterfaceSubClass(interface,
                                                &interfaceSubClass);
        printf("Interface class %d, subclass %d\n", interfaceClass,
                                                    interfaceSubClass);
        //Now open the interface. This will cause the pipes associated with
        //the endpoints in the interface descriptor to be instantiated
        kr = (*interface)->USBInterfaceOpen(interface);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to open interface (%08x)\n", kr);
            (void) (*interface)->Release(interface);
            break;
        }
        //Get the number of endpoints associated with this interface
        kr = (*interface)->GetNumEndpoints(interface,
                                        &interfaceNumEndpoints);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to get number of endpoints (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
        printf("Interface has %d endpoints\n", interfaceNumEndpoints);
        //Access each pipe in turn, starting with the pipe at index 1
        //The pipe at index 0 is the default control pipe and should be
        //accessed using (*usbDevice)->DeviceRequest() instead
        for (pipeRef = 1; pipeRef <= interfaceNumEndpoints; pipeRef++)
        {
            IOReturn        kr2;
            UInt8           direction;
            UInt8           number;
            UInt8           transferType;
            UInt16          maxPacketSize;
            UInt8           interval;
            char            *message;
            kr2 = (*interface)->GetPipeProperties(interface,
                                        pipeRef, &direction,
                                        &number, &transferType,
                                        &maxPacketSize, &interval);
            if (kr2 != kIOReturnSuccess)
                printf("Unable to get properties of pipe %d (%08x)\n",
                                        pipeRef, kr2);
            else
            {
                printf("PipeRef %d: ", pipeRef);
                switch (direction)
                {
                    case kUSBOut:
                        message = "out";
                        break;
                    case kUSBIn:
                        message = "in";
                        break;
                    case kUSBNone:
                        message = "none";
                        break;
                    case kUSBAnyDirn:
                        message = "any";
                        break;
                    default:
                        message = "???";
                }
                printf("direction %s, ", message);
                switch (transferType)
                {
                    case kUSBControl:
                        message = "control";
                        break;
                    case kUSBIsoc:
                        message = "isoc";
                        break;
                    case kUSBBulk:
                        message = "bulk";
                        break;
                    case kUSBInterrupt:
                        message = "interrupt";
                        break;
                    case kUSBAnyType:
                        message = "any";
                        break;
                    default:
                        message = "???";
                }
                printf("transfer type %s, maxPacketSize %d\n", message,
                                                    maxPacketSize);
            }
        }
#ifndef USE_ASYNC_IO    //Demonstrate synchronous I/O
        kr = (*interface)->WritePipe(interface, 2, kTestMessage,
                                            strlen(kTestMessage));
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform bulk write (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
        printf("Wrote \"%s\" (%ld bytes) to bulk endpoint\n", kTestMessage,
                                        (UInt32) strlen(kTestMessage));
        numBytesRead = sizeof(gBuffer) - 1; //leave one byte at the end
                                             //for NULL termination
        kr = (*interface)->ReadPipe(interface, 9, gBuffer,
                                            &numBytesRead);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform bulk read (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
        //Because the downloaded firmware echoes the one’s complement of the
        //message, now complement the buffer contents to get the original data
        for (i = 0; i < numBytesRead; i++)
            gBuffer[i] = ~gBuffer[i];
        printf("Read \"%s\" (%ld bytes) from bulk endpoint\n", gBuffer,
                    numBytesRead);
#else   //Demonstrate asynchronous I/O
        //As with service matching notifications, to receive asynchronous
        //I/O completion notifications, you must create an event source and
        //add it to the run loop
        kr = (*interface)->CreateInterfaceAsyncEventSource(
                                    interface, &runLoopSource);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to create asynchronous event source
                                    (%08x)\n", kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
                            kCFRunLoopDefaultMode);
        printf("Asynchronous event source added to run loop\n");
        bzero(gBuffer, sizeof(gBuffer));
        strcpy(gBuffer, kTestMessage);
        kr = (*interface)->WritePipeAsync(interface, 2, gBuffer,
                                    strlen(gBuffer),
                                    WriteCompletion, (void *) interface);
        if (kr != kIOReturnSuccess)
        {
            printf("Unable to perform asynchronous bulk write (%08x)\n",
                                                    kr);
            (void) (*interface)->USBInterfaceClose(interface);
            (void) (*interface)->Release(interface);
            break;
        }
#endif
        //For this test, just use first interface, so exit loop
        break;
    }
    return kr;
}

When an asynchronous write action is complete, the WriteCompletion function is called by the notification. WriteCompletion then calls the interface functionReadPipeAsync to perform an asynchronous read from the pipe. When the read is complete, control passes to ReadCompletion which simply prints status messages and adds a NULL termination to the global buffer containing the test message read from the device. The WriteCompletion and ReadCompletion functions are shown together in Listing 2-9.

Listing 2-9  Two asynchronous I/O completion functions

void WriteCompletion(void *refCon, IOReturn result, void *arg0)
{
    IOUSBInterfaceInterface **interface = (IOUSBInterfaceInterface **) refCon;
    UInt32                  numBytesWritten = (UInt32) arg0;
    UInt32                  numBytesRead;
    printf("Asynchronous write complete\n");
    if (result != kIOReturnSuccess)
    {
        printf("error from asynchronous bulk write (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
    printf("Wrote \"%s\" (%ld bytes) to bulk endpoint\n", kTestMessage,
                                        numBytesWritten);
    numBytesRead = sizeof(gBuffer) - 1; //leave one byte at the end for
                                            //NULL termination
    result = (*interface)->ReadPipeAsync(interface, 9, gBuffer,
                                    numBytesRead, ReadCompletion, refCon);
    if (result != kIOReturnSuccess)
    {
        printf("Unable to perform asynchronous bulk read (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
}
void ReadCompletion(void *refCon, IOReturn result, void *arg0)
{
    IOUSBInterfaceInterface **interface = (IOUSBInterfaceInterface **) refCon;
    UInt32      numBytesRead = (UInt32) arg0;
    UInt32      i;
    printf("Asynchronous bulk read complete\n");
    if (result != kIOReturnSuccess) {
        printf("error from async bulk read (%08x)\n", result);
        (void) (*interface)->USBInterfaceClose(interface);
        (void) (*interface)->Release(interface);
        return;
    }
    //Check the complement of the buffer’s contents for original data
    for (i = 0; i < numBytesRead; i++)
        gBuffer[i] = ~gBuffer[i];
    printf("Read \"%s\" (%ld bytes) from bulk endpoint\n", gBuffer,
                                                    numBytesRead);
}

iPhone开发:如何直接用Object-C连接数据库

iphone开发连数据库一般有两种

1.core data 这种是可视化的存储方式,不带sql语句的,应该是官方封装好了
2.直接通过sql语句连接sqlite:
[code lang="c"]
- (BOOL) databaseTest
{
//数据库操作
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"mydb.db"];//查找db文件返回其path
NSLog(path);//打印db文件的路径
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL find = [fileManager fileExistsAtPath:path];
if(find){
NSLog(@"Sucess:find dn file.");
if(sqlite3_open([path UTF8String], &database_) == SQLITE_OK)//打开数据库
{

//打开数据库成功
NSLog(@"Sucess:open database sucess.");
/////////////////////////////////////////////////////////////////////////////////
//这里进行数据库操作
/////////////////////////////////////////////////////////////////////////////////
//////////////////////////1.插入数据////////////////////////////////////////////////
//SQL查询语句
char *sql = "INSERT INTO students (name) VALUES(?)";
//会话
sqlite3_stmt *statement;
//调制一个会话
int success = sqlite3_prepare_v2(database_, sql, -1, &statement, NULL);
if (success != SQLITE_OK) {
NSLog(@"Error: failed to insert:channels");
}
//绑定数据

sqlite3_bind_text(statement, 1, "Kevin", -1, SQLITE_TRANSIENT);//注意此处的字符串为旧字符串
success = sqlite3_step(statement);
sqlite3_finalize(statement);
if (success == SQLITE_ERROR) {
NSLog(@"Error: failed to insert into the database with message.");
}

//2.查询数据
statement = nil;
char *sql_select= "SELECT name FROM students";
if (sqlite3_prepare_v2(database_, sql_select, -1, &statement, NULL) != SQLITE_OK) {
NSLog(@"Error: failed to prepare statement with message:get channels.");
}
//查询结果集中一条一条的遍历所有的记录,这里的数字对应的是列值。
while (sqlite3_step(statement) == SQLITE_ROW) {
char* name = (char*) sqlite3_column_text(statement, 0);//第一列数据,注意此处师从0开始的
NSString *nameNs=[[NSString alloc] initWithUTF8String:name];
NSLog(nameNs);
[nameNs release];

}
sqlite3_finalize(statement);

//关闭数据库
sqlite3_close(database_);
return YES;
}
else
{
sqlite3_close(database_);
NSLog(@"Error: open database file.");
return NO;
}

return NO;
}
}
[/code]

iPhone4S刚刚发布,iFixit即宣布拆解完成

iFixit一直因为拆解、披露苹果设备组件而闻名。

iPhone 4S还采用A5片上系统(System-on-a-Chip),频率为1GHz,配512MB DDR2内存,后置800万像素摄像头,可以捕捉1080p视频,前置VGA摄像头。装备有802.11 b/g/n,蓝牙4.0。显示屏为LED背光IPS TFT LCD Retina 屏,分辨率960X640。手机支持GSM、GPRS、EDGE、WCDMA网络。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

乔布斯生平

乔布斯1955年2月24日生于旧金山。母亲乔安·卡罗尔-席博(Joanne Carole Schieble)和叙利亚裔父亲阿卜杜勒法塔赫-江达利(Abdulfattah Jandali)当时尚未结婚。克拉拉·乔布斯和保罗·乔布斯收养了乔布斯,这是一个家境并不宽裕的美国中产阶级家庭。1972年,乔布斯从美国加州库比提诺市Homestead高级中学(Homestead High School)毕业,同时被俄勒冈瑞德学院 (Reed College )录取。

一个学期后,乔布斯辍学了,并在1976年4月份与一道辍学的斯蒂夫·盖瑞-沃兹尼亚克(Stephen Gary Wozniak)在自己的车库中创建了苹果电脑。当时,21岁的乔布斯担任销售员的角色,而沃兹尼亚克则是工程师。选择“苹果电脑”这一名称,部分的缘由是披头士乐队,因为乔布斯是披头士的粉丝,崇拜该乐队的苹果唱片公司。

1977年,乔布斯和沃兹尼亚克推出了Apple II–第一款成功的个人电脑。其目的是面向大众市场,而不再是给工程师或爱好者提供的工具。它很大程度上仍是来自沃兹尼亚克的设计。该模型几经升级,产品线一直延续到了1993年。在1980年苹果电脑进行首次公开招股时,Apple II已经能够给公司带来1.17亿美元的年营收。通过公司的首次公开招股,乔布斯也成为了一名百万富豪。

并不是乔布斯所有的初期想法都能取得成效。在1980年推出的Apple III和1983年推出的Lisa就曾兵败滑铁卢。但是与众不同的一体机Macintosh,真正创建了现代计算机操作系统的标准,用户可以手动鼠标点击图标,而不再是输入命令符。

即使那样,乔布斯在设计细节上依然坚持己见。1978年加入苹果的前用户界面专家布鲁斯·托格纳兹尼(Bruce Tognazzini)曾回忆说,乔布斯曾坚持认为,键盘不应当包括“上、下、左、右”键,应当让用户移动鼠标了操控计算机屏幕。

乔布斯对美学的追求有事会走向极端。在上世纪80年代和1998年至2005年曾担任苹果工程师的乔治·克劳(George Crow)回忆说,乔布斯甚至会要求计算机内部非常美观。在推出首款Macintosh时,乔布斯要求机箱内的内部线路的颜色与苹果早期的彩虹标识一致。克劳说,他最终让乔布斯信服,这些是不必要的浪费。

Macintosh的设计,很大程度上来自于乔布斯1979年在参观施乐位于帕洛阿尔托实验室时,看到的Xerox Alto拥有的粗糙的用户界面和鼠标。这台计算机和当时的计算机没有任何相似之处,仿佛是未来的人们设计的。它配备了图形化的界面、用鼠标进行操作、配有文字和图像处理程序、并且连上了以太网。乔布斯在上世纪90年代中期美国公共广播公司(PBS)的节目中曾表示,“毕加索曾经说过,‘优秀的艺术家复制,伟大的艺术家剽窃。’我一直无耻的剽窃了伟大的创意。”

即便是在穿着打扮上,乔布斯也更像是一位艺术家而不是企业高管。在公众场合,乔布斯几乎一直穿着李维斯牛仔裤、黑色圆领衫和New Balance跑鞋。

随着苹果的不断扩张,乔布斯决定聘用一名更富有经验的管理者来执掌公司。1983年,乔布斯将百事可乐高管约翰·斯卡利招至麾下,没想到却为自己埋下了隐患。由于二人意见分歧,斯卡利后来占据了上风,导致乔布斯1985年被驱逐。在挖来斯库利的过程中,乔布斯说出了也许是他一生中最具说服力的话,“你想一辈子卖糖水,还是改变整个世界?”

在被苹果驱逐之后,乔布斯创建了一家新公司NeXT计算机,主要开发工作站。有“互联网之父”美誉的蒂姆·伯纳斯-李(Tim Berners-Lee)最初就是在一台NeXT工作站上创建了最早的网站。NeXT最终销售硬件,但后来停止了硬件销售。但是作为一家软件公司,NeXT并未赚到太多的钱。但是该公司的操作系统成为了OS X操作系统的前身。1996年12月,苹果宣布4亿美元收购NeXT,标志着乔布斯的回归。

早在1986年,乔布斯使用他自己的资金,斥资1000万美元从著名制片人、《星球大战》的导演乔治·卢卡斯(George Lucas)的手中买下了Lucasfilm的计算机绘图部门。该公司包括了皮克斯动画工作室,最初曾卖过硬件,也卖过软件,最后才成为了一家动画工作室。皮克斯动画工作室推出了《玩具总动员》等一系列绘制人口的动画片。2006年1月,乔布斯以74亿美元的价格将皮克斯动画工作室出售给沃尔特迪士尼。这一交易不仅上乔布斯成为沃尔特迪士尼的最大股东,而且还获得了该公司董事会的一个席位。

就在这一时期,苹果开始沉沦。使用英特尔处理器和微软操作系统的PC开始统治市场,在微软的Windows操作系统开始借助Macintosh虚拟界面的许多元素之后,这一趋势更是开始加剧。

与微软不同,苹果在这时需要同时资助公司内部的硬件和软件开发。由于几乎没有应用开发者为Macintosh开发应用,苹果最终决定把自己的操作系统向其它硬件厂商授权,但是这一决定太晚,已经于事无补。

在1997年时,苹果在过去两年中已经亏损了20亿美元左右,该公司股价接连创出历史新低。当时,带领苹果的是公司第三任首席执行官吉尔·阿梅里奥(Gil Amelio)。在2006年12月收购NeXT之后,苹果董事会决定解雇阿梅里奥 ,并让乔布斯回到苹果董事会就任特别顾问,同时他们也在紧锣密鼓地寻找新首席执行官的最佳人选。此后接下来的3个月里,乔布斯一直监管着董事会的动态,并否决了公司的操作系统复制许可项目,并与微软达成了一项划时代的交易合作计划。这些只能由乔布斯主导所采取的举措,最后证明对苹果的生存起到了至关重要的作用。1997年9月,苹果董事会终于再度任命史蒂夫为首席执行官。

在重新担任苹果首席执行官之后,乔布斯只拿着1美元的年薪和公司的股票期权,做出了一系列快速产生结果的变革。乔布斯终止了带来Mac克隆机的软件授权项目,取消了陷入困境的Newton掌上电脑,并决定让Mac专注于企业市场。

1998年5月,苹果在乔布斯的带领下推出了iMac。这一产品的推出,迫使竞争对手纷纷对自己的产品设计进行改进。在乔布斯的带领下,苹果发起了“Think Different”广告活动。在苹果股东对公司变化感到欣喜的情况下,乔布斯在苹果的地位进一步得到了巩固。在接管苹果的数月时间里,乔布斯用前NeXT党羽替换了苹果5位高管中的4位。他还发布内部电子邮件,禁止总部员工悠闲的带着宠物来到办公室,禁止在停车场抽烟,而且警告泄露公司机密文件的员工一定会被开除。

在乔布斯重新返回苹果后,苹果也曾犯下了一些错误,其中包括推出立方体的Macintosh,由于无法在市场中流行,苹果在2001年撤下了该项目。这一产品的失败也导致苹果当季业绩出现亏损,并警告在2000年和2001年数个季度无法实现业绩预期。

但是在此之后,苹果推出了一系列伟大的产品。2001年,苹果推出了钛金属外壳的PowerBook笔记本电脑,这种金属经常在战斗机中使用。同年,苹果推出了改变数字音乐产业的iPod音乐播放器。到2010年9月,苹果已累计售出了2.75亿部iPod,占据了全球音乐播放器市场超过70%的份额。

2003年,苹果推出了iTunes音乐商店。当时,音乐产业与数字革新还没有任何的联系,虽然受到了非法下载产业的重创,但是仍不愿意轻松、便宜的在网上销售歌曲。但是乔布斯说服了主要唱片商,销售每首99美分的歌曲,以及对盗版的限制,能够让绝大多数的消费者所接受。

已经销售出超过100亿首歌曲的iTunes,在2008年成为美国最大的音乐零售商。iTunes的成功,也刺激着消费者去购买iPod。因为在iTunes创办至今的绝大多数时间里,来自iTunes的歌曲只能够被下载到苹果的音乐播放器当中,其它厂商的设备无法从iTunes下载歌曲。

就在这一时期,乔布斯也在打造自己的管理团队。他在上世纪90年代末招募了前康柏计算机高管蒂姆·库克(Tim Cook),来加强苹果的运营,随后又把库克提升为首席运营官。2000年,乔布斯从零售商塔吉特挖来了罗恩·约翰逊(Ron Johnson),让其担任苹果零售业务高级副总裁,并开始在全球开设苹果零售店。苹果首席设计师乔纳森·艾夫(Jonathan Ive)则负责公司产品的设计工作,因为他与乔布斯在产品设计上有着类似的感悟力。

2004年,乔布斯突然因病从公司短暂离职。后来乔布斯披露说,离职期间他接受了胰腺癌手术治疗。不过当时便有消息人士透露,乔布斯早在9个月之前便已经知道了自己患病的情况。而在这一段时期内,苹果董事会及乔布斯均对苹果股东未透露任何消息。

当乔布斯最初确诊为癌症时,医生认为他已经时日无多。但是,他从未被病魔吓倒,一直保持着健康人的工作状态和心情。

2007年,苹果推出iPhone让乔布斯再次走上神坛。也正是iPhone手机,让整个手机产业经历了一场巨变。消息人士透露,乔布斯亲自参与了iPhone手机的整个设计过程,而且乔布斯也是把iPhone频幕从塑料转变为玻璃的关键人物之一。从开始测试iPhone频幕至产品发布,仅仅用了7个月的时间。尽管在进入智能手机市场之初,曾有人对苹果的能力产生过怀疑,但是事实证明,这种怀疑无疑是杞人忧天。市场调研公司IDC的统计数据显示,今年第二季度,苹果已成为了全球最大的智能手机制造商。

去年,乔布斯还推出了iPad平板电脑。在这款产品发布的前9个月时间里,苹果销售出了1480万部iPad。据接近乔布斯的苹果工作人员透露,iPad项目对乔布斯非常重要,即便是刚从2009年的肝脏移植手术中恢复,乔布斯还是私下参与了iPad的开发工作。

iPad和iPhone的一大卖点是苹果应用商店(App Store)。截至2010年7月底,苹果应用商店的应用下载总数突破了70亿次。

乔布斯的任职履历上也有过污点。2006年年中以来,美国证券市场爆发了大规模“期权股权倒签丑闻”,上市公司通常授予高管股票期权作为提高公司业绩表现和股价激励机制,但许多上市公司管理层凭借操纵股票期权回溯日期,刻意挑选股价较低水平时作为期权授予日,以扩大获利空间,所带来的差价利润通常是几千万到上亿美元。2006年6月,苹果经内部调查发现,1997年至2001年间的部分股票期权发放存在问题,有6400多份股权日期错填,为此需额外计入8400万美元支出。不过苹果否认乔布斯因“股权倒签”受益。

熟悉乔布斯的人透露,乔布斯之所以能够保持创新,是因为他不会迷恋过去的成绩,持续关注并要求员工也是如此。乔布斯2005年在斯坦福大学的学生毕业典礼上说:“死亡可能是生命的最佳创新,因为它将彻底改变你的生命。死亡让老人消失,从而为年轻人让路。现在你们是年轻人,但未来会逐步变为老人并消失。也许我的讲话过于戏剧性,但这是事实。”

在乔布斯超过30年的职业生涯中,他帮助硅谷这一气氛慵懒的农村地区变为科技行业的创新中心。除与微软联合创始人比尔·盖茨(Bill Gates)、甲骨文创始人拉里·埃里森(Larry Ellison)等产业先驱为现代高科技产业顶顶了基础之外,乔布斯还向世人证明,设计精巧的感官产品的感染力超过了技术力量的本身,并在一个越来越数字化的世界中,改变了消费者与技术互动的方式。

但是与那些人不同,乔布斯最大的创造力出现在生命的最后阶段,当几乎一系列完整的创新和取得巨大成功的产品,如iPod、iPhone和iPad极大的改变了PC、电子和数字媒体产品。与此同时,乔布斯通过具有魅力的广告宣传和自有零售店推广和销售这些产品,让苹果成为了流行文化的图标。

在这一阶段开始之时,乔布斯曾描述自己的理念是让产品“成为艺术和技术的结合体”。按照这一理念,乔布斯把苹果变成了全球市值最高的公司。

在乔布斯的人生最后时刻,他的妻子劳伦(Laurene),以及四位子女陪他一起度过。

乔布斯不仅在科技产业取得了巨大的成就,而且在娱乐产业也取得了突破性的成绩。他把苹果转变成为了全球最大的音乐零售商,而且作为皮克斯动画工作室的首席执行官和投资家,他协助了计算机动画电影的流行。后来,他把皮克斯动画工作室出售给了沃尔特迪士尼公司。乔布斯是改变了人们使用互联网,收听音乐,观看电视、电影,阅读图书的关键人物,并逐步的瓦解了各个产业。

在现代商业史中,乔布斯回归苹果留下了最光辉的一页。在离开苹果11年并重返这家公司之后,乔布斯带领着这家濒临破产的公司推出了iMac一体电脑、iPod音乐播放器和iTunes音乐商店。

苹果当前的年营收达到652亿美元,远超过1997财年(截至1997年9月末)的71亿美元。苹果已成为了全球首屈一指的消费电子设备设计厂商,并在2007年1月将名称中的“电脑”(苹果原名为苹果电脑,即Apple Computer)一词去除,以强调公司向PC之外的业务进行扩张。

尽管乔布斯在今年8月把公司帅印正式交付给他长期的助手蒂姆·库克(Tim Cook),但是他的逝世还是会引发一系列的质疑,在没有乔布斯的眼光和指引下,苹果如何能够延续自己的成功。在过去的十年当中,苹果已成为了全球技术创新力的排头兵。美国资本主义的其它一些标志性企业,如沃尔特迪士尼、沃尔玛、IBM等公司,也曾经历了转型之痛,但是在他们具有超凡魅力的创始人退位之后,都最终生存了下来。

但是几乎没有一家像苹果这样规模的企业表露出对创始人如此强烈的依附感,或是在最巅峰的时期失去了自己的创始人。在苹果1985年解雇乔布斯后的几年时间里,苹果业绩开始逐步下滑。直到乔布斯1997年重返苹果之后,才止住了苹果业绩的进一步下滑。

乔布斯同时还留下了关于他善变的管理方式的无数的故事,如他不喜欢时,会称员工或员工的想法为“愚蠢”。像微软、谷歌、亚马逊这样的竞争对手,乔布斯又显露出其好战的一面。2010年4月,当Adobe采用一系列手段哭诉苹果的iPhone和iPad不支持其Flash视频格式后,乔布斯洋洋洒洒写下一篇1600字的随笔,解释Flash已经过时,不适用于移动设备。

乔布斯对苹果的硬件和软件维持了高标准。他要求,从消费者走进苹果零售店的那一瞬间起,苹果应当为消费者提供“酷毙”的美感,以及易于使用的体验。他对产品开发和设计中的小细节非常关注,这给苹果产品带来了一些最具特色的功能。而乔布斯在舞台上的演讲方式则能够激起外界的无穷兴趣,这也是其他同行无法比拟的。

在多次苹果的产品发布活动上,乔布斯形成了一个著名的口头禅,即“还有一件事(There is one more thing)”。在乔布斯说这句话之后,苹果往往会发布最重要的新闻。他要求苹果员工对产品严格保密,这一策略确保了外界对苹果新产品的兴趣。

iphone开发-UITableView详细讲解(转)

-、建立 UITableView

[code lang=”c”]

 DataTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
 [DataTable setDelegate:self];
 [DataTable setDataSource:self];
 [self.view addSubview:DataTable];
 [DataTable release];

[/code]

二、UITableView各Method说明

[code lang=”c”]
//Section总数
– (NSArray *)sectionIndexTitlesForTab leView:(UITableView *)tableView{
 return TitleData;
}

// Section Titles
//每个section显示的标题
– (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
 return @"";
}
//指定有多少个分区(Section),默认为1
– (NSInteger)numberOfSectionsInTableV iew:(UITableView *)tableView {
 return 4;
}
//指定每个分区中有多少行,默认为1
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
}
//绘制Cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";

   UITableViewCell *cell = [tableView dequeueReusableCellWithI dentifier:
                            SimpleTableIdentifier];
   if (cell == nil) {
       cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefa ult
                                      reuseIdentifier: SimpleTableIdentifier] autorelease];
 }
 cell.imageView.image=image;//未选cell时的图片
 cell.imageView.highlightedImage=highlightImage;//选中cell后的图片
 cell.text=//…..
 return cell;
}
//行缩进
-(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAt IndexPath:(NSIndexPath *)indexPath{
 NSUInteger row = [indexPath row];
 return row;
}
//改变行的高度
– (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
   return 40;
}
//定位
[TopicsTable setContentOffset:CGPointMake(0, promiseNum * 44 + Chapter * 20)];
//返回当前所选cell
NSIndexPath *ip = [NSIndexPath indexPathForRow:row inSection:section];
[TopicsTable selectRowAtIndexPath:ip animated:YES scrollPosition:UITableViewScrollPositio nNone];
[tableView setSeparatorStyle:UITableViewCellSelection StyleNone];
//选中Cell响应事件
– (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

 [tableView deselectRowAtIndexPath:indexPath animated:YES];//选中后的反显颜色即刻消失
}
//判断选中的行(阻止选中第一行)
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath :(NSIndexPath *)indexPath
{
   NSUInteger row = [indexPath row];
   if (row == 0)
       return nil;

   return indexPath;
}
//划动cell是否出现del按钮
– (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
}
//编辑状态
– (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingSt yle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{

[topicsTable setContentSize:CGSizeMake(0,controller.promiseNum * 44)];
//右侧添加一个索引表
– (NSArray *)sectionIndexTitlesForTab leView:(UITableView *)tableView{
}

//返回Section标题内容
– (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
}

//自定义划动时del按钮内容
– (NSString *)tableView:(UITableView *)tableView
titleForDeleteConfirmati onButtonForRowAtIndexPat h:(NSIndexPath *)indexPath
//跳到指的row or section
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositio nBottom animated:NO];

[/code]

三、在UITableViewCell上建立UILable多行显示

[code lang=”c”]

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   static NSString *CellIdentifier = @"Cell";
   UITableViewCell *cell = [tableView dequeueReusableCellWithI dentifier:CellIdentifier];
   if (cell == nil) {
       cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
  UILabel *Datalabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 320, 44)];
  [Datalabel setTag:100];
  Datalabel.autoresizingMask = UIViewAutoresizingFlexib leWidth | UIViewAutoresizingFlexib leHeight;
  [cell.contentView addSubview:Datalabel];
  [Datalabel release];
 }
 UILabel *Datalabel = (UILabel *)[cell.contentView viewWithTag:100];
 [Datalabel setFont:[UIFont boldSystemFontOfSize:18]];
 Datalabel.text = [data.DataArray objectAtIndex:indexPath.row];
 cell.accessoryType = UITableViewCellAccessory DisclosureIndicator;
   return cell;
}

//选中cell时的颜色

typedef enum {
   UITableViewCellSelection StyleNone,
   UITableViewCellSelection StyleBlue,
   UITableViewCellSelection StyleGray
} UITableViewCellSelection Style

//cell右边按钮格式

typedef enum {
   UITableViewCellAccessory None,                   // don’t show any accessory view
   UITableViewCellAccessory DisclosureIndicator,    // regular chevron. doesn’t track
   UITableViewCellAccessory DetailDisclosureButton, // blue button w/ chevron. tracks
   UITableViewCellAccessory Checkmark               // checkmark. doesn’t track
} UITableViewCellAccessory Type //是否加换行线

typedef enum {
   UITableViewCellSeparator StyleNone,
   UITableViewCellSeparator StyleSingleLine
} UITableViewCellSeparator Style//改变换行线颜色

tableView.separatorColor = [UIColor blueColor];

[/code]

本文转自:http://zyc-to.blog.163.com/blog/static/171524002010112831630425/

约翰·斯卡利谈乔布斯[采访全文]

 

 

【本文原载:Cult of Mac ,链接在此:http://www.cultofmac.com/john-sculley-on-steve-jobs-the-full-interview-transcript/63295

 

乔布斯与约翰·斯卡利,苹果前 CEO ,二人当时被《商业周刊》称为「动态二重奏」

这是约翰·斯卡利就史蒂夫·乔布斯接受采访的文字抄本。文章有些长,但值得阅读,因为其中有一些关于乔布斯如何做事的惊人洞察和见解。这同样也是你能听到的最坦率的 CEO 采访之一。斯卡利完全放开了谈论乔布斯和苹果公司,他承认苹果雇佣他去运营这家公司是一个错误,他表示自己一点也不懂电脑。这对谁来说都很难得,要公开对自己的职业生涯做出诚恳坦率的评价并不容易,何况他曾是风光一时的大公司 CEO 。

你提到了「史蒂夫·乔布斯的方法论」,什么是史蒂夫的方法论?

斯卡利:我给你说个大致概念。我第一次见到乔布斯时,大约是 25 年前,他把一些与今天相同的「基本原则」放到一起,这些「基本原则」也就是我所说的,乔布斯如何打造伟大产品的方法论。

史蒂夫从我遇见他那天起就热爱美妙的产品,尤其是硬件。有次他来我家的时候,被我家的门深深吸引了,因为门上有个特别设计的铰链和锁。我曾有过工业设计师的学习经历,因此连接起史蒂夫和我的是工业设计,而不是计算机。

我当时对计算机几乎一窍不通,当时的其他人也是一样。那时候个人计算机革命才刚刚开始。但我们两个都相信美妙设计的价值,但史蒂夫尤其觉得,你应该从提供良好用户体验的角度开始设计。

他总是以「用户的体验将会因此变成怎样?」这个视角来看待很多事情。但和今天的很多做市场的人不同,他们会走出去做消费者调查,问人们「你们想要什么?」史蒂夫不信这一套。

他说,「我怎么可能去问一个不知图形计算机为何物,一个从没见过图形计算机的人,图形计算机的未来会是怎样?」史蒂夫相信,这就像是拿着一个数学计算器给人看,他们是无法想像出计算机将会发展成怎样的。因为,这个鸿沟太大了。

史蒂夫看待产品的视角总是先从用户体验开始,而工业设计在产品给人印象中占据非常重要的位置。因此,他把我招到苹果,因为他相信计算机最终将称为消费产品。在 1980 年代初期,这种想法是很惊人的,因为当时人们都觉得个人电脑只不过是大型计算机的微缩版本。IBM 就这么看。

当时也有些人觉得,个人电脑的市场和游戏机差不多。当时有些早期的游戏机,结构比较简单,可以连接到电视上玩……但史蒂夫的看法却完全不同,他认为电脑将会改变这个世界,用他的话来说,他觉得电脑将成为「人类思维的自行车」。它将赋予人们之前自己从未想象过的能力。它与游戏机的市场规模无关,也不只是大型计算机的缩小化……

史蒂夫是一个具备强大想象力和愿景的人。而且还是一个执着于每一步细节都要精益求精的人。他在有系统地在关注每一件事情——归根到底,他是一个完美主义者。

如果你回到 Apple II ,史蒂夫是第一个把计算机放进塑料盒子的人,我们今天称之为 ABS 塑料(ABS plastic)的材料,他也是第一个把键盘集成到电脑上的人。今天回过头来看,这似乎是再简单不过,但在当时他创造第一台 Apple II 的时候,1977 年——也是史蒂夫方法论开始的时候,却并非如此。史蒂夫的方法论贯穿到 Macintosh 、NeXT 电脑,以及后来的 Mac 电脑,iMac ,iPod 和 iPhone 都在其中。

让史蒂夫的方法论与其他人的区别开的一点是,他相信你最重要的决定,不是你要做什么——而是你决定不做什么。他是个极简主义者。

 

我记得有次去史蒂夫的家里,他房间里面几乎没有任何家具。只有一幅爱因斯坦的画像,他非常钦佩爱因斯坦;还有一盏蒂凡尼台灯(Tiffany lamp)、一把椅子和一张床。他不仅是不喜欢被太多东西包围,而是对于挑选东西有难以置信的在意。对于苹果他也是一样。有些做用户体验的人,他们觉得工业设计不应该提到与开发科技产品的其他工作相提并论,他们觉得那是做珠宝生意的人该关心的……但回到我刚才说的锁的例子,铰链、精美的黄铜制作的门、精细的工艺、机械装置,我想,这能反映出史蒂夫感受到的一切。

当我第一次看到 Macintosh ——当时它还在开发之中——基本上只是一堆元件安装在我们称之为「面包板」的电路板上。它什么也不是,但史蒂夫拥有那种去接触和发现(他感觉会成为)绝对最优秀、最聪明的人的能力。在激发别人加入到自己的事业中去方面,他拥有异乎寻常的个人魅力和极强的煽动性。即便产品还不存在,他也能让别人相信他的愿景。当我去见麦金塔团队的时候(麦金塔团队最终的人数达到 100 人左右,但在我见他们的时候人数还比较少),当时他们的平均年龄是 22 岁。

有些人很明显从来没有从事过商业产品开发,但他们信任史蒂夫,相信史蒂夫的愿景。史蒂夫能够同时在多个层面工作。

在一个层面上,他的工作是「改变世界」,这是大的层面。在另一个层面上,他的工作又沉至实际打造一款产品,设计软件、硬件、系统设计,甚至应用程序、外围周边产品等成功的必要细节。

在不同的情况下,他总是能够找到在某个领域最棒的人,并且总是亲自完成自己团队的所有招聘工作,他从不将这个工作授权给任何其他人。

关于史蒂夫的另一点是,他从不尊重大的组织。他认为那是官僚且无效的。他基本上叫他们「蠢伙」(bozos),这是他对自己不尊重的组织或机构的习惯叫法。

麦金塔一体式的开发团队,最终人数涨到了 100 个。史蒂夫就给自己定下一个规矩,发誓麦金塔团队的人数永远不超过 100 个。所有,如果要想添新人进来,就得裁另外的人出去。这种思考是典型的乔布斯观察法「我无法记住超过一百个人的姓名,所以,我只想跟我私下认识的人呆在一起。如果团队的人数超过了 100 个,就会强迫我们变成了另外一种组织结构,我无法在那种环境下工作。我喜欢的工作方式是,我可以碰任何事情」。我在苹果期间了解了他,就是这样运营自己的部门的。

那么当苹果越来越大之后,乔布斯怎么做呢?我是说,现在的苹果已经有上万员工了。

斯卡利:史蒂夫会说:「公司的组织可以长大,但麦金塔团队不能」。麦金塔是以一个产品开发部门设立的——所以苹果公司是一个中心销售组织,是一个后勤总办公室,负责所有其他的行政、法律等事务,还是一个中心制造部门。但真正的团队是麦金塔团队,对高科技公司而言确实如此。建造伟大的产品,并不需要太多人。一般来说,你只会看到数量不多的软件工程师在开发操作系统。事实并非你想的那样,其实只要很小的一支团队。想象一下,它就像一个艺术家的画室,或者一个艺术家的工作间,乔布斯就是主任工匠师傅,他四处走动查看作品并作出判断,很多时候他的判断就是拒绝某些事情。

我能回忆起很多的夜晚,我们都在办公室呆到 12 点或者凌晨一点,因为工程师一般直到午餐时间才现身,然后一直工作到深夜。一个工程师可能把史蒂夫叫过来,向他展示自己最新写成的软件代码。史蒂夫会在看完之后扔给他一句:「还不是足够好。」他会不断地驱使人们提高对于自己能够做到的水平的期望值。所以,人们做出来的作品往往是自己都没有想到自己能做到的。很大程度上,或许因为乔布斯经常在进行角色切换,一会儿他会以其强大的领袖魅力高度赞许员工,刺激他们,让他们觉得自己正在从事一件伟大的事业;另一方面,他在拒绝作品方面又几乎残酷无情,直到他觉得已经达到完美的水平,可以装进产品——这个例子里,是麦金塔电脑。

他自己也清楚地意识到,对吗?这是个非常棒的发现,他并不是个喜怒无常的疯子?

斯卡利:对,史蒂夫让人难以置信的有系统性。他的办公室里总是有一块白板。他很少亲自去画。他的绘画技能并不突出,但他的品味让人叫绝。

把史蒂夫与其他人区分开来的是,比如比尔·盖茨——同样也非常杰出——但比尔对于卓绝的品味并不感兴趣。他感兴趣的是如何占领这个市场,他愿意去推出任何需要推出的产品,只要能够占领市场份额就行。史蒂夫绝对不会这么做。史蒂夫相信尽善尽美。他愿意在新的产品领域做额外的冒险,但从设计师的角度他总能得到特别的优势。所以,当我思考不同类型的 CEO ——有的是优秀的领导,有的是扭亏为盈的大师,有的是聪明的谈判代表,有的很棒的激励者——但史蒂夫最天才的技巧在于,他们是一个伟大的设计师。在苹果的每一件事情,都可以通过设计的镜头来理解。

在苹果公司,无论是用户体验的外观感觉,还是产品的工业设计,软件的系统设计,甚至是主板线路如何安排,都是经过设计的。机箱内部的电路板必须要让史蒂夫看起来觉得漂亮才行。甚至在设计麦金塔电脑的机箱的时候,史蒂夫故意使它设计的让普通消费者难以拆开,因为他不希望有顾客乱改里面的任何东西。

以他的苛刻标准来看,每件事情都必须经过完美的设计,即便很多用户根本看不到它们存在。

直到建造麦金塔制造工厂的时候,这一标准已经被系统化。麦金塔工厂初中仅计划成为一家自动化工厂,但最终成为了拥有自动分拣机器人的总装和测试工厂。今天这并不新鲜,但在 25 年前可不一样。我还能记得通用汽车 CEO 和罗斯·佩罗(Ross Perot)一起参观麦金塔工厂时的情景。我们的工厂只是进行产品总装和测试,但这一切进行的非常完美。因为这个工厂在设计的时候,就已经想得非常透彻,自动化的工厂就像是一个产品。

现在,如果你快进到今天看看史蒂夫创造的产品,如今的科技已经能做更多的事情,这使得产品能够更微型化、商品化,成本也更便宜。现在苹果已经不再制造任何东西。我在苹果的时候,人们常常称苹果为一家「垂直整合的广告代理公司」,这可不是什么恭维的话。

事实上,到今天大家都是如此。惠普、苹果,以及绝大多数科技公司都不再自己制造,而是将制造完全外包给 EMS ——电子制造服务商,也称 OEM 。

 

耐克是不是很类似?

斯卡利: 是的,大概吧,耐克已经很接近了,我想确实如此。我想如果你回头看看当时日本的消费电子厂商,他们都是类似的公司。

史蒂夫当时非常羡慕的公司是索尼。我们曾去拜访过盛田昭夫(Akio Morita),他和史蒂夫对于美妙的产品有着同样的高标准。我记得他送给我和史蒂夫一人一个第一代索尼 Walkman 随身听。在此之前我们从没见过类似的东西,因为根本就没这样的产品。那是 25 年前的事了,当时乔布斯对它非常着迷。他对 Walkman 做的第一件事就是把它拆开,然后观察他的每一个零部件。研究它的外观和质地,看它是如何制造出来的。

他也被索尼的工厂吸引了。我们进入索尼工厂里面。看见不同的人有不同颜色的制服,有的是红色制服,有的是绿色,有些是蓝色的,取决于他们不同的职能。这些都是非常细心的考虑,而工厂干净的几乎一尘不染。这些事都对史蒂夫产生了很大的冲击。

麦金塔的工厂就和索尼的非常像。虽然员工没有彩色的制服,但和索尼工厂一样优雅考究。史蒂夫当时参考的基准对象就是索尼。他非常想让苹果成为另一个索尼。他不想让公司成为 IBM 那样,也不想成为微软。他想要成为索尼。

但挑战在于,那个时候你还很难造出一款很像索尼的数字产品。一切都是模拟的,日本的公司更擅长模拟电子。你可以去阅读密歇根大学普拉哈拉德( C.K. Prahalad)的书《为未来竞争》(Competing for the Future),史蒂夫也学习了这本书。

日本公司总是先从元件的市场份额开始,所以往往由一家公司支配一种元件,比如这家公司主导着传感器市场,另一家主导了内存,还有的可能主导了硬盘,等等。然后他们会基于已有元件的市场份额优势去生产一种最终产品。这对模拟型电子产品来说行的通,因为核心竞争聚焦在降低产品的成本——无论谁控制了关键元件的成本,都将是巨大的优势。但这对完全数字化的产品是无效的,因为数字电子的价值链主要不在这一块。你不是从某一个元件开始的,而是从用户体验开始的。

你可以看到今天的索尼存在的巨大问题,这个问题已经存在了至少 15 年,自从数字电子产品出现那天起。他们的组织是一种「烟囱管式」的,做软件的人从来不与做硬件的交谈,做硬件的不与做元件的交谈,做元件的不与做设计的交谈。组织之间相互争辩,而且组织又庞大又官僚。

按理说,应该是索尼先造出 iPod ,但他们没有——而是苹果。iPod 是史蒂夫方法论的完美展现,从用户出发,关注一个完整的端到端(end-to-end)系统。

伴随史蒂夫的永远都是端到端系统。他不是设计师,但却是一个很棒的系统思考者。这是在其他电脑公司所看不到的。后者总是趋向于专注在自己熟悉的部分,然后将其他的都外包出去。

如果你看下 iPod 的情形,它从供应链一路直到 iPod 在中国的制造工厂——其复杂程度不亚于设计这个产品本事。同样的完美苛刻的标准,索尼将其放到供应链的挑战,而苹果将其放到为用户设计。这是两种完全不同的看待事情的方式。

控制整盘全局的想法是怎么来的?就是控制一切、整个系统的这个想法。

斯卡利:乔布斯认为如果你开放了系统,人们就会自己动小手脚进行修改,而这些修改是对用户体验的妥协,而他不会交付一种他自己不想提供的用户体验。

 

译文未完,原文如下:

John Sculley On Steve Jobs, The Full Interview Transcript

By Leander Kahney (2:59 am, Oct. 14, 2010)

 

Steve Jobs and John Sculley, the former CEO of Apple. The pair were dubbed the “dynamic duo.”

 

Here’s a full transcript of the interview with John Sculley on the subject of Steve Jobs.

It’s long but worth reading because there are some awesome insights into how Jobs does things.

It’s also one of the frankest CEO interviews you’ll ever read. Sculley talks openly about Jobs and Apple, admits it was a mistake to hire him to run the company and that he knows little about computers. It’s rare for anyone, never mind a big-time CEO, to make such frank assessment of their career in public.

UPDATE: Here’s an audio version of the entire interview made by reader Rick Mansfield using OS X’s text-to-speech system. It’s a bit robotic (Rick used the “Alex” voice, which he says is “more than tolerable to listen to”) but you might enjoy it while commuting or at the gym. The audio is 52 minutes long and it’s a 45MB download. It’s in .m4a format, which will play on any iPod/iPhone, etc. Download it here (Option-Click the link; or right-click and choose “Save Linked File…”).

 

Q: You talk about the “Steve Jobs methodology.” What is Steve’s methodology?

Sculley: Let me give you a framework. The time that I first met Jobs, which was over 25 years ago, he was putting together the same first principles that I call the Steve Jobs methodology of how to build great products.

Steve from the moment I met him always loved beautiful products, especially hardware. He came to my house and he was fascinated because I had special hinges and locks designed for doors. I had studied as an industrial designer and the thing that connected Steve and me was industrial design. It wasn’t computing.

I didn’t know really anything about computers nor did any other people in the world at that time. This was at the beginning of the personal computer revolution, but we both believed in beautiful design and Steve in particular felt that you had to begin design from the vantage point of the experience of the user.

He always looked at things from the perspective of what was the user’s experience going to be? But unlike a lot of people in product marketing in those days, who would go out and do consumer testing, asking people, “What did they want?” Steve didn’t believe in that.

He said, “How can I possibly ask somebody what a graphics-based computer ought to be when they have no idea what a graphic based computer is? No one has ever seen one before.” He believed that showing someone a calculator, for example, would not give them any indication as to where the computer was going to go because it was just too big a leap.

Steve had this perspective that always started with the user’s experience; and that industrial design was an incredibly important part of that user impression. And he recruited me to Apple because he believed that the computer was eventually going to become a consumer product. That was an outrageous idea back in the early 1980′s because people thought that personal computers were just smaller versions of bigger computers. That’s how IBM looked at it.

Some of them thought it was more like a game machine because there were early game machines, which were very simple and played on televisions… But Steve was thinking about something entirely different. He felt that the computer was going to change the world and it it was going to become what he called “the bicycle for the mind.” It would enable individuals to have this incredible capability that they never dreamed of before. It was not about game machines. It was not about big computers getting smaller…

He was a person of huge vision. But he was also a person that believed in the precise detail of every step. He was methodical and careful about everything — a perfectionist to the end.

If you go back to the Apple II, Steve was the first one to put a computer into a plastic case, which was called ABS plastic in those days, and actually put the keyboard into the computer. It seems like a pretty simple idea today, looking back at it, but even at the time when he created the first Apple II, in 1977 — that was the beginning of the Jobs methodology. And it showed up in the Macintosh and showed up with his NeXT computer. And it showed up with the future Macs, the iMacs, the iPods and the iPhones.

What makes Steve’s methodology different from everyone else’s is that he always believed the most important decisions you make are not the things you do – but the things that you decide not to do. He’s a minimalist.

 

I remember going into Steve’s house and he had almost no furniture in it. He just had a picture of Einstein, whom he admired greatly, and he had a Tiffany lamp and a chair and a bed. He just didn’t believe in having lots of things around but he was incredibly careful in what he selected. The same thing was true with Apple. Here’s someone who starts with the user experience, who believes that industrial design shouldn’t be compared to what other people were doing with technology products but it should be compared to people were doing with jewelry… Go back to my lock example, and hinges and a door with beautiful brass, finely machined, mechanical devices. And I think that reflects everything that I have ever seen that Steve has touched.

When I first saw the Macintosh — it was in the process of being created — it was basically just a series of components over what is called a bread board. It wasn’t anything, but Steve had this ability to reach out to find the absolute best, smartest people he felt were out there. He was extremely charismatic and extremely compelling in getting people to join up with him and he got people to believe in his visions even before the products existed. When I met the Mac team, which eventually got to 100 people but the time I met him it was much smaller, the average age was 22.

These were people who had clearly never built a commercial product before but they believed in Steve and they believed in his vision. He was able to work in multiple levels in parallel.

On one level he is working at the “change the world,” the big concept. At the other level he is working down at the details of what it takes to actually build a product and design the software, the hardware, the systems design and eventually the applications, the peripheral products that connect to it.

In each case, he always reached out for the very best people he could find in the field. And he personally did all the recruiting for his team. He never delegated that to anybody else.

The other thing about Steve was that he did not respect large organizations. He felt that they were bureaucratic and ineffective. He would basically call them “bozos.” That was his term for organizations that he didn’t respect.

The Mac team they were all in one building and they eventually got to one hundred people. Steve had a rule that there could never be more than one hundred people on the Mac team. So if you wanted to add someone you had to take someone out. And the thinking was a typical Steve Jobs observation: “I can’t remember more than a hundred first names so I only want to be around people that I know personally. So if it gets bigger than a hundred people, it will force us to go to a different organization structure where I can’t work that way. The way I like to work is where I touch everything.” Through the whole time I knew him at Apple that’s exactly how he ran his division.

Q: So how did he cope when Apple became bigger? I mean, Apple has tens of thousands of people now.

Sculley: Steve would say: “The organization can become bigger but not the Mac team. The Macintosh was set up as a product development division — and so Apple had a central sales organization, a central back office for all the administration, legal. It had a centralized manufacturing of that sort but the actual team that was building the product, and this is true for high tech products, it doesn’t take a lot of people to build a great product. Normally you will only see a handful of software engineers who are building an operating system. People think that it must be hundreds and hundreds working on an operating system. It really isn’t. It’s really just a small team of people. Think of it like the atelier of an artist. It’s like an artist’s workshop and Steve is the master craftsman who walks around and looks at the work and makes judgments on it and in many cases his judgments were to reject something.

I can remember lots of evenings we would be there until 12 or 1 o’clock in the morning because the engineers usually don’t show up until lunchtime and they work well into the night. And an engineer would bring Steve in and show him the latest software code that he’s written. Steve would look at it and throw it back at him and say: “It’s just not good enough.” And he was constantly forcing people to raise their expectations of what they could do. So people were producing work that they never thought they were capable of. Largely because Steve would shift between being highly charismatic and motivating and getting them excited to feel like they are part of something insanely great. And on the other hand he would be almost merciless in terms of rejecting their work until he felt it had reached the level of perfection that was good enough to go into – in this case, the Macintosh.

Q: He was quite conscious about that, right? This was very well thought out, not just crazy capriciousness?

Sculley: No, Steve was incredibly methodical. He always had a white board in his office. He did not draw himself. He didn’t have particular drawing ability himself, yet he had an incredible taste.

The thing that separated Steve Jobs from other people like Bill Gates — Bill was brilliant too — but Bill was never interested in great taste. He was always interested in being able to dominate a market. He would put out whatever he had to put out there to own that space. Steve would never do that. Steve believed in perfection. Steve was willing to take extraordinary chances in trying new product areas but it was always from the vantage point of being a designer. So when I think about different kinds of CEOs — CEOs who are great leaders, CEOs who are great turnaround artists, great deal negotiators, great people motivators — but the great skill that Steve has is he’s a great designer. Everything at Apple can be best understood through the lens of designing.

Whether it’s designing the look and feel of the user experience, or the industrial design, or the system design and even things like how the boards were laid out. The boards had to be beautiful in Steve’s eyes when you looked at them, even though when he created the Macintosh he made it impossible for a consumer to get in the box because he didn’t want people tampering with anything.

In his level of perfection, everything had to be beautifully designed even if it wasn’t going to be seen by most people.

That went all the way through to the systems when he built the Macintosh factory. It was supposed to be the first automated factory but what it really was a final assembly and test factory with a pick-to-pack robotic automation. It is not as novel today as it was 25 years ago, but I can remember when the CEO of General Motors along with Ross Perot came out just to look at the Macintosh factory. All we were doing was final assembly and test but it was done so beautifully. It was as well thought through in design as a factory, a lights out factory requiring many people as the products were.

Now if you leap forward and look at the products that Steve builds today, today the technology is far more capable of doing things, it can be miniaturized, it is commoditized, it is inexpensive. And Apple no longer builds any products. When I was there, people used to call Apple “a vertically-integrated advertising agency,” which was not a compliment.

Actually today, that’s what everybody is. That’s what HP is; that’s what Apple is; and that’s what most companies are because they outsource to EMS — electronics manufacturing services.

 

Q: Isn’t Nike a good analogy?

Sculley: Yeah, probably, Nike is closer, I think that is true. I think if you look at the Japanese consumer electronics in that era they were all analog companies.

The one that Steve admired was Sony. We used to go visit Akio Morita and he had really the same kind of high-end standards that Steve did and respect for beautiful products. I remember Akio Morita gave Steve and me each one of the first Sony Walkmans. None of us had ever seen anything like that before because there had never been a product like that. This is 25 years ago and Steve was fascinated by it. The first thing he did with his was take it apart and he looked at every single part. How the fit and finish was done, how it was built.

He was fascinated by the Sony factories. We went through them. They would have different people in different colored uniforms. Some would have red uniforms, some green, some blue, depending on what their functions were. It was all carefully thought out and the factories were spotless. Those things made a huge impression on him.

The Mac factory was exactly like that. They didn’t have colored uniforms, but it was every bit as elegant as the early Sony factories that we saw. Steve’s point of reference was Sony at the time. He really wanted to be Sony. He didn’t want to be IBM. He didn’t want to be Microsoft. He wanted to be Sony.

The challenge was in that era you couldn’t build digital products like Sony. Everything was analog and the Japanese companies approached things and you can read Prahalad’s book, from University of Michigan, he studied it. (Note: Sculley is referring to C.K. Prahalad’s “Competing for the Future” (1994))

The Japanese always started with the market share of components first. So one would dominate, let’s say sensors and someone else would dominate memory and someone else hard drive and things of that sort. They would then build up their market strengths with components and then they would work towards the final product. That was fine with analog electronics where you are trying to focus on cost reduction — and whoever controlled the key component costs was at an advantage. It didn’t work at all for digital electronics because digital electronics you’re starting at the wrong end of the value chain. You are not starting with the components. You are starting with the user experience.

And you can see today the tremendous problem Sony has had for at least the last 15 years as the digital consumer electronics industry has emerged. They have been totally stove-piped in their organization. The software people don’t talk to the hardware people, who don’t talk to the component people, who don’t talk to the design people. They argue between their organizations and they are big and bureaucratic.

Sony should have had the iPod but they didn’t — it was Apple. The iPod is a perfect example of Steve’s methodology of starting with the user and looking at the entire end-to-end system.

It was always an end-to-end system with Steve. He was not a designer but a great systems thinker. That is something you don’t see with other companies. They tend to focus on their piece and outsource everything else.

If you look at the state of the iPod, the supply chain going all the way over to iPod city in China – it is as sophisticated as the design of the product itself. The same standards of perfection are just as challenging for the supply chain as they are for the user design. It is an entirely different way of looking at things.

Q: Where did he get the idea for controlling the whole widget? The idea to be in charge of everything, the whole system?

Sculley: Steve believed that if you opened the system up people would start to make little changes and those changes would be compromises in the experience and he would not be able to deliver the kind of experience that he wanted.

Q: But this control extends to every aspect of the product – even to opening the box. The experience of opening the box is designed by Steve Jobs.

Sculley: The original Mac really had no operating system. People keep saying, “Well why didn’t we license the operating system?” The simple answer is that there wasn’t one. It was all done with lots of tricks with hardware and software. Microprocessors in those days were so weak compared to what we had today. In order to do graphics on a screen you had to consume all of the power of the processor. Then you had to glue chips all around it to enable you to offload other functions. Then you had to put what are called “calls to ROM.” There were 400 calls to ROM, which were all the little subroutines that had to be offloaded into the ROM because there was no way you could run these in real time. All these things were neatly held together. It was totally remarkable that you could deliver a machine when you think the first processor on the Mac was less than three MIPs (Million Instructions Per Second), which today would be — I can’t think of any device which has three MIPS, or equivalent. Even your digital watch is at least 200 or 300 times more powerful than the first Macintosh. (NOTE. For comparison, today’s entry-level iMac uses an Intel Core i3 chip, rated at over 40,000 MIPS!)

It’s hard to conceive how he was able to accomplish so much with so little in those days. So for someone to build consumer products in the 1980s beyond what we did with the first Mac was literally impossible. In the 1990s with Moore’s Law and other things, the homogenization of technology, it became possible to begin to see what consumer products would look like but you couldn’t really build them. It really hasn’t been until the turn of the century that you sort of got the crossover between the cost of components, the commoditization and the miniaturization that you need for consumer products. The performance suddenly reached the point where you could actually build things that we can call digital consumer products. Because Steve’s design methodology was so correct even 25 years ago he was able to make a design methodology – his first principles — of user experience, focus on just a few things, look at the system, never compromise, compare yourself not to other electronic products but compare yourself to the finest pieces of jewelry — all those criteria — no one else was thinking about that. Everyone else was just going through an evolution of cheap products that are getting more powerful and cheaper to build. Like the MP3 player. Remember when he came in with the iPod, there were thousands of MP3 players out there. Can anyone else remember any of the others?

His tradeoff was he believed that he had to control the entire system. He made every decision. The boxes were locked.

Steve Jobs circa 1984. Illustration by Matthew Phelan

Q: But the motivation for this is the user experience?

Sculley: Absolutely. The user experience has to go through the whole end-to-end system, whether it’s desktop publishing or iTunes. It is all part of the end-to-end system. It is also the manufacturing. The supply chain. The marketing. The stores. I remember I was brought in because I had a design background and because I was a marketer. I had product marketing experience. Not because I knew anything about computers.

Q: I find that pretty fascinating. You say in your book that first and foremost you wanted to make Apple a “product marketing company.”

Sculley: Right. Steve and I spent months getting to know each other before I joined Apple. He had no exposure to marketing other than what he picked up on his own. This is sort of typical of Steve. When he knows something is going to be important he tries to absorb as much as he possibly can.

One of the things that fascinated him: I described to him that there’s not much difference between a Pepsi and a Coke, but we were outsold 9 to 1. Our job was to convince people that Pepsi was a big enough decision that they ought to pay attention to it, and eventually switch. We decided that we had to treat Pepsi like a necktie. In that era people cared what necktie they wore. The necktie said: “Here’s how I want you to see me.” So we have to make Pepsi like a nice necktie. When you are holding a Pepsi in your hand, its says, “Here’s how I want you to see me.”

We did some research and we discovered that when people were going to serve soft drinks to a friend in their home, if they had Coca Cola in the fridge, they would go out to the kitchen, open the fridge, take out the Coke bottle, bring it out, put it on the table and pour a glass in front of their guests.

If it was a Pepsi, they would go out in to the kitchen, take it out of the fridge, open it, and pour it in a glass in the kitchen, and only bring the glass out. The point was people were embarrassed to have someone know that they were serving Pepsi. Maybe they would think it was Coke because Coke had a better perception. It was a better necktie. Steve was fascinated by that.

We talked a lot about how perception leads reality and how if you are going to create a reality you have to be able to create the perception. We did it with something called the Pepsi generation.

I had learned through a lecture that Dr. Margaret Mead had given, an anthropologist in the 60’s, that the most important fact for marketers was going to be the emergence of an affluent middle class — what we call the Baby Boomers, who are now turning 60. They were the first people to have discretionary income. They could go out and spend money for things other than what they had to have.

When we created Pepsi generation it was created with them in mind. It was always focusing on the user of the drink, never the drink.

Coke always focused on the drink. We focused on the person using it. We showed people riding dirt bikes, waterskiing, or kite flying, hang gliding — doing different things. And at the end of it there would always be a Pepsi as a reward. This all happened when color television was first coming in. We were the first company to do lifestyle marketing. The first and the longest-running lifestyle campaign was — and still is — Pepsi.

We did it was just as color television was coming in and when large-screen TVs were coming in, like 19-inch screens. We didn’t go to people who made TV commercials because they were making commercials for little tiny black-and-white screens. We went out to Hollywood and got the best movie directors and said we want you to make 60-second movies for us. They were lifestyle movies. The whole thing was to create the perception that Pepsi was number one because you couldn’t be number one unless you thought like number one. You had to appear like number one.

Steve loved those ideas. A lot of the stuff we were doing and our marketing was focused on when we bring the Mac to market. It has to be done at such a high level of perception of expectation that he will sort of tease people to want to find out what the product is capable of. The product couldn’t do very much in the beginning. Almost all of the technology was used for the user experience. In fact we did get a backlash where people said it’s a toy. It doesn’t do anything. But eventually it did as the technology got more powerful.

 

Q: Of course, Apple is famous for the same kind of lifestyle advertising now. It shows people living an enviable lifestyle, courtesy of Apple’s products. Hip young people grooving to iPods…

Sculley. I don’t take any credit for it. What Steve’s brilliance is, is his ability to see something and then understand it and then figure out how to put into the context of his design methodology — everything is design.

An anecdotal story, a friend of mine was at meetings at Apple and Microsoft on the same day and this was in the last year, so this was recently. He went into the Apple meeting (he’s a vendor for Apple) and when he went into the meeting at Apple as soon as the designers walked in the room, everyone stopped talking because the designers are the most respected people in the organization. Everyone knows the designers speak for Steve because they have direct reporting to him. It is only at Apple where design reports directly to the CEO.

Later in the day he was at Microsoft. When he went into the Microsoft meeting, everybody was talking and then the meeting starts and no designers ever walk into the room. All the technical people are sitting there trying to add their ideas of what ought to be in the design. That’s a recipe for disaster.

Microsoft hires some of the smartest people in the world. They are known for their incredibly challenging test they put people through to get hired. It’s not an issue of people being smart and talented. It’s that design at Apple is at the highest level of the organization, led by Steve personally. Design at other companies is not there. It is buried down in the bureaucracy somewhere… In bureaucracies many people have the authority to say no, not the authority to say yes. So you end up with products with compromises. This goes back to Steve’s philosophy that the most important decisions are the things you decide NOT to do, not what you decide to do. It’s the minimalist thinking again.

Having been around in the early days, I don’t see any change in Steve’s first principles — except he’s gotten better and better at it.

Another example, which has been brilliant, is what he did with the retail stores.

He brought one of the top retailers in the world on his board to learn about retail (Mickey Drexler from The Gap, who advised Jobs to build a prototype store before launch). Not only did he learn about retail, I’ve never been in a better store than an Apple store. It has the highest revenue per square foot of any store in the world but it’s not just the revenue, it’s the experience.

Apple stores are packed. You can go to the Sony center — go in the San Francisco center at the Moscone. There’s nobody there. You can go into the Nokia store, they have one in New York on 57th St. There’s nobody there.

But other people have the stores. They have the products to look at. You can touch and feel them but you walk into an Apple store and it’s just like an amazing experience. It is as much the people who are there shopping alongside you.

Again, it is like necktie products. It’s like being in an Apple store says, “here’s how I want you to see me. I’m here. I’m at the genius bar. I’m trying out the products. Look at me: I’m like the other people in the store.”

The user experience is taken all the way from the experience of using the product, to the advertising of how it is presented, to the design of the product. Steve is legendary for his fit and finish requirements on a product. Looking at the radius and parting lines and bezels and all these little details that designers pay attention to.

He will reject something which no one will see as a problem. But because his standards are so high, people sit there and say, “How does Apple do it? How does apple have such incredible products?”

I remember one of the things we talked about, Steve used to ask me: “How did Pepsi get such great advertising?” He asked if it was the agencies that you picked? And I said what it really is. First of all you have to have an exciting product and you have to be able to present it as an opportunity to do bold advertising.

But great advertising comes from great clients. The best creative people want to work for the best clients. If you are a client who doesn’t appreciate great work, or a client who won’t take risks and try new stuff, or a client who can’t get excited about the creative, then you’re the wrong kind of client.

Most big companies delegate it way down in the organization. The CEO rarely knows anything about the advertising except when it’s presented, when it’s all done. That’s not how we did it at Pepsi, not how we did it at Apple, and I’m sure it’s not how Steve does it now. He always adamantly involved in the advertising, the design and everything.

Q: Right. I hear Lee Clow flies up to Apple every week to meet with Jobs.

Sculley: Once you realize that Apple leads through design, than you can start to see, that’s what makes it different. Look at the stores, at the stairs in the stores. They are made of some special glass that had to be fabricated. And that’s so typical of the way he thinks. Everyone around him knows he beats to a different drummer. He sets standards that are entirely different than any other CEO would set.

He’s a minimalist and constantly reducing things to their simplest level. It’s not simplistic. It’s simplified. Steve is a systems designer. He simplifies complexity.

If you are someone who doesn’t care about it, you end up with simplistic results. It’s amazing to me how many companies make that mistake. Take the Microsoft Zune. I remember going to CES when Microsoft launched Zune and it was literally so boring that people didn‘t even go over to look at it… The Zunes were just dead. It was like someone had just put aging vegetables into a supermarket. Nobody wanted to go near it. I’m sure they were very bright people but it’s just built from a different philosophy. The legendary statement about Microsoft, which is mostly true, is that they get it right the third time. Microsoft’s philosophy is to get it out there and fix it later. Steve would never do that. He doesn’t get anything out there until it is perfected.

 

Q: Let’s talk about advertising, which is so important to Apple. In you book you talk about ‘strategic advertising’ – advertising as strategy. That’s a very interesting idea…

Sculley: At the time I came to Silicon Valley there was no advertising… The only one who was really interested in doing advertising was Apple. H-P didn’t advertise in those days. No one advertised in those days on a big brand basis. One of the things that I was recruited to Apple to help do was to bring big brand advertising to Apple.

The Apple logo was multicolor because the Apple II was the first color computer. No one else could do color, so that’s why they put the color blocks into the logo. If you wanted to print the logo in a magazine ad or on a package you could print it with four colors but Steve being Steve insisted on six colors. So whenever the Apple logo was printed, it was always printed in six colors. It added another 30 to 40 percent to the cost of everything, but that’s what Steve wanted. That’s what we always did. He was a perfectionist even from the early days.

Q: That drives some people a little bit crazy. Did it drive you crazy?

Sculley: It’s okay to be driven a little crazy by someone who is so consistently right. What I’ve learned in high tech is that there’s a very, very thin line between success and failure. It’s an industry where you are constantly taking risks, particularly if you’re a company like Apple, which is constantly living out on the edge.

Your chance of being on one side of that line or the other side of the line is about equal. Sometimes… he was wrong tactically on a number of things. He wouldn’t put a hard drive in the Macintosh. When someone asked him about communications, he just threw a little disk across the room and said, “That’s all we’ll ever need.” On the other hand, Steve led the development of what was called AppleTalk and AppleLink. AppleTalk was the communications that enabled the Macintosh to communicate to the laser printer that enabled… desktop publishing.

AppleTalk was brilliant in its day. It was as brilliant as the Macintosh. It was another example of using a minimalist approach and solving a problem that no one else thought was a problem that needed to be solved. Steve was solving problems back in the 80s that turned out 15, 20 years later to be exactly the right problems to be working on. The challenge was we were decades away from when the technology would be homogenized enough and powerful enough to be able to make all those things mass market. He was just, in many cases, he was way ahead of his time.

Looking back, it was a big mistake that I was ever hired as CEO. I was not the first choice that Steve wanted to be the CEO. He was the first choice, but the board wasn’t prepared to make him CEO when he was 25, 26 years old.

They exhausted all of the obvious high-tech candidates to be CEO… Ultimately, David Rockefeller, who was a shareholder in Apple, said let’s try a different industry and let’s go to the top head hunter in the United States who isn’t in high tech: Gerry Roche.

They went and recruited me. I came in not knowing anything about computers. The idea was that Steve and I were going to work as partners. He would be the technical person and I would be the marketing person.

The reason why I said it was a mistake to have hired me as CEO was Steve always wanted to be CEO. It would have been much more honest if the board had said, “Let’s figure out a way for him to be CEO. You could focus on the stuff that you bring and he focuses on the stuff he brings.”

Remember, he was the chairman of the board, the largest shareholder and he ran the Macintosh division, so he was above me and below me. It was a little bit of a façade and my guess is that we never would have had the breakup if the board had done a better job of thinking through not just how do we get a CEO to come and join the company that Steve will approve of, but how do we make sure that we create a situation where this thing is going to be successful over time?

My sense is that when Steve left (in 1986, after the board rejected his bid to replace Sculley as CEO) I still didn’t know very much about computers.

My decision was first to fix the company, but I didn’t know how to fix companies and to get it back to be successful again.

All the stuff we did then were all his ideas. I understood his methodology. We never changed it. So we didn’t license the products. We focused on industrial design. We actually built up our own in-house design organization, which they have to this day. We developed the PowerBook… We developed QuickTime. All these things were built around Steve’s philosophy… It was all about sales and marketing and the evolution of the products.

All the design ideas were clearly Steve’s. The one who should really be given credit for all that stuff while I was there is really Steve.

I made two really dumb mistakes that I really regret because I think they would have made a difference to Apple. One was when we are at the end of the life of the Motorola processor… we took two of our best technologists and put them on a team to go look and recommend what we ought to do.

They came back and they said it doesn’t make any difference which RISC architecture you pick, just pick the one that you think you can get the best business deal with. But don’t use CISC. CISC is complex instructions set. RISC is reduced instruction set.

So Intel lobbied heavily to get us to stay with them… (but) we went with IBM and Motorola with the PowerPC. And that was a terrible decision in hindsight. If we could have worked with Intel, we would have gotten onto a more commoditized component platform for Apple, which would have made a huge difference for Apple during the 1990s. In the 1990s, the processors were getting powerful enough that you could run all of your technology and software, and that’s when Microsoft took off with their Windows 3.1.

Prior to that you had to do it in software and hardware, the way Apple did. When the processors became powerful enough, it just became a commodity and the software can handle all those subroutines we had to do in hardware.

So we totally missed the boat. Intel would spend 11 billion dollars and evolve the Intel processor to do graphics… and it was a terrible technical decision. I wasn’t technically qualified, unfortunately, so I went along with the recommendation.

The other even bigger failure on my part was if I had thought about it better I should have gone back to Steve.

I wanted to leave Apple. At the end of 10 years, I didn’t want to stay any longer. I wanted to go back to the east coast. I told the board I wanted to leave and IBM was trying to recruit me at the time. They asked me to stay. I stayed and then they later fired me. I really didn’t want to be there any longer.

The board decided that we ought to sell Apple. So I was given the assignment to go off and try to sell Apple in 1993. So I went off and tried to sell it to AT&T to IBM and other people. We couldn’t get anyone who wanted to buy it. They thought it was just too high risk because Microsoft and Intel were doing well then. But if I had any sense, I would have said: “Why don’t we go back to the guy who created the whole thing and understands it. Why don’t we go back and hire Steve to come back and run the company?”

It’s so obvious looking back now that that would have been the right thing to do. We didn’t do it, so I blame myself for that one. It would have saved Apple this near-death experience they had.

One of the issues that got me fired was that there was a split inside the company as to what the company ought to do. There was one contingent that wanted Apple to be more of a business computer company. They wanted to open up the architecture and license it. There was another contingent, which I was a part of, that wanted to take the Apple methodology — the user experience and stuff like that — and move into the next generation of products, like the Newton.

But the Newton failed. It was a new direction. It was so fundamentally different. The result was I got fired and they had two more CEOs who both licensed the technology but… they shut down the industrial design. They turned out computers that looked like everybody else’s computers and they no longer cared about advertising, public relations. They just obliterated everything. We’re just going to become an engineering type company and they almost drove the company into bankruptcy during that.

I’m actually convinced that if Steve hadn’t come back when he did — if they had waited another six months — Apple would have been history. It would have been gone, absolutely gone.

What did he do? He turned it right back to where it was — as though he never left. He went all the way back.

So during my era, really everything we did was following his philosophy — his design methodology.

Unfortunately, I wasn’t as good at it as he was. Timing in life is everything. It just wasn’t a time when you could build consumer products and he wasn’t having any more luck at NeXT than we were having at Apple — and he was better at it than we were. The one thing he did do better: he built the better next-generation operating system, which eventually was merged into Apple’s operating system.

 

Q: People say he killed the Newton – your pet project – out of revenge. Do you think he did it for revenge?

Sculley: Probably. He won’t talk to me, so I don’t know.

The Newton was a terrific idea, but it was too far ahead of its time. The Newton actually saved Apple from going bankrupt. Most people don’t realize in order to build Newton, we had to build a new generation microprocessor. We joined together with Olivetti and a man named Herman Hauser, who had started Acorn computer over in the U.K. out of Cambridge university. And Herman designed the ARM processor, and Apple and Olivetti funded it. Apple and Olivetti owned 47 percent of the company and Herman owned the rest. It was designed around Newton, around a world where small miniaturized devices with lots of graphics, intensive subroutines and all of that sort of stuff… when Apple got into desperate financial situation, it sold its interest in ARM for $800 million. If it had kept it, the company went on to become an $8 or $10 billion company. It’s worth a lot more today. That’s what gave Apple the cash to stay alive.

So while Newton failed as a product, and probably burnt through $100 million, it more than made it up with the ARM processor… It’s in all the products today, including Apple’s products like the iPod and iPhone. It’s the Intel of its day.

Apple is not really a technology company. Apple is really a design company. If you look at the iPod, you will see that many of the technologies that are in the iPod are ones that Apple bought from other people and put together. Even when Apple created Macintosh, all the ideas came out of Xerox and Apple recruited some of the key people out of Xerox.

Everything Apple does fails the first time because it is out on the bleeding edge. Lisa failed before the Mac. The Macintosh laptop failed before the PowerBook. It was not unusual for products to fail. The mistake we made with the Newton was we over-hyped the advertising. We hyped the expectation of what the product could actually, so it became a celebrated failure.

Q: I want to ask about Jobs’ heroes. You say Edwin Land was one of his heroes?

Sculley: Yeah, I remember when Steve and I went to meet Dr Land.

Dr Land had been kicked out of Polaroid. He had his own lab on the Charles River in Cambridge. It was a fascinating afternoon because we were sitting in this big conference room with an empty table. Dr Land and Steve were both looking at the center of the table the whole time they were talking. Dr Land was saying: “I could see what the Polaroid camera should be. It was just as real to me as if it was sitting in front of me before I had ever built one.”

And Steve said: “Yeah, that’s exactly the way I saw the Macintosh.” He said if I asked someone who had only used a personal calculator what a Macintosh should be like they couldn’t have told me. There was no way to do consumer research on it so I had to go and create it and then show it to people and say now what do you think?

Both of them had this ability to not invent products, but discover products. Both of them said these products have always existed – it’s just that no one has ever seen them before. We were the ones who discovered them. The Polaroid camera always existed and the Macintosh always existed — it’s a matter of discovery. Steve had huge admiration for Dr. Land. He was fascinated by that trip.

Q: What other heroes did he talk about?

Sculley: He became very close with Ross Perot.

Ross Perot came and visited Apple several times and visited the Macintosh factory. Ross was a systems thinker. He created EDS (Electronic Data Systems) and was an entrepreneur. He believed in big ideas; change the world ideas. He was another one.

Akio Morita was clearly one of his great heroes. He was an entrepreneur who built Sony and did it with great products — Steve is a products person.

Q: How about Hewlett-Packard? Jobs has said in the early days that HP was a big influence when he worked there briefly with Woz.

Sculley: HP was not a model for Apple. I’ve never heard that. HP had the “HP way,” where Bill Hewitt and David Packard would wander people would leave their work out on their desk at night and they’d wonder around and look at it. So it was very open and it was an engineers company. Apple is a designers company, not an engineers company. HP was never in those days known for great design. It was known for great engineering, not great design. No, I don’t remember HP being a model for Apple at all.

Q: Didn’t Jobs also manage by walking around?

Sculley: He did that. Everyone did that in Silicon Valley. That was what HP contributed to the way Silicon Valley does business. There are certain characteristics that all Silicon Valley startups have and that’s one of them. That clearly came from HP.

HP was the father of the walking around style of management. And HP was the father of the engineer being at the top of the hierarchy in companies.

Engineers are far more important than managers at Apple — and designers are at the top of the hierarchy. Even when you look at software, the best designers like Bill Atkinson, Andy Hertzfeld, Steve Capps, were called software designers, not software engineers because they were designing in software. It wasn’t just that their code worked. It had to be beautiful code. People would go in and admire it. It’s like a writer. People would look at someone’s style. They would look at their code writing style and they were considered just beautiful geniuses at the way they wrote code or the way they designed hardware.

Q: Steve Jobs is famous for being a student of design. He’d run around looking intently at all the Mercedes in the Apple car park.

Sculley: Steve was a fanatic on looking at how things were printed: the fonts, the colors, the layouts. I remember once after Steve had left, one of our tasks was to go and build the business in Japan. Apple had a $4 million of turnover and we were being sued by the Japanese FTC and people saying we ought to close the office down — it’s losing money. I remember going over and to make a long story short, four years later we were a $2 billion dollar business and the number two company in Japan selling computers.

A big part of it was that we had to learn to make products the way the Japanese wanted products. We were assembling products in Singapore and sending them to Japan. And the first thing the customer saw when they opened the box was the manual, but the manual was turned the wrong way around – and the whole batch was rejected. In the United States, we’d never experienced anything like that. If you put the manual in this way or that way — what difference did it make?

Well, it made a huge difference in Japan. Their standards are just different than ours. If you look at Apple and the attention to detail. The “open me first,” the way the box is designed, the fold lines, the quality of paper, the printing — Apple just goes to extraordinary lengths. It looks like you are buying something from Bulgari or one of the highest in jewelry firms. At the time, it was the Japanese.

We used to study Italian designers when we were looking for selecting a design company before we selected Hartmut Esslinger from Frog to do what was called the Snow White design. We were looking at Italian car designers. We really did study the designs of cars that they had done and looking at the fit and finish and the materials and the colors and all of that. At that time, nobody was doing this in Silicon Valley. It was the furthest thing on the planet from Silicon Valley back then in the 80’s. Again, this is not my idea. I could relate to it because of my interest and background in design, but it was totally driven by Steve.

At the time when Steve was gone and I took over I was highly criticized. They said, “How could they put a guy who knows nothing about computers in charge of a computer company?” What a lot of people didn’t realize was that Apple wasn’t just about computers. It was about designing products and designing marketing and it was about positioning.

People used to call us a “vertically-integrated advertising agency” and that was a huge cheap shot. Engineers couldn’t think of anything worse to say about a company than to say it was a “vertically-integrated advertising agency.” Well, guess what? They all are today. That’s the model. The supply chain is managed somewhere else.

乔布斯辞去CEO一职

北京时间8月25日最新消息,苹果公司董事会刚刚宣布CEO史蒂夫·乔布斯致信辞去其首席执行官一职。同时任命COO库克接任CEO。乔布斯被选为董事会主席,库克将加入董事会,立即生效。

左为时任首席运营官COO的蒂姆·库克,右为史蒂夫·乔布斯

信件内容如下:

致苹果董事会和苹果团体:

我曾经说过,如果有一天我不再能够胜任,无法满足你们对我作为苹果首席执行官的期待,那么我将主动让你们知道。非常不幸的是,这一天已经来临。

从此之后,我将辞去苹果首席执行官一职,但是,如果董事会认为合适的话,我仍将继续担任苹果董事会主席、董事以及苹果员工等职位。

就我的继任者而言,我强烈建议公司按照我们的继任计划进行,并任命蒂姆·库克(Tim Cook)为苹果新任首席执行官。

我相信,苹果最英明、最创新的时代就在眼前。我也希望能够看到这一切,并在新的角色上继续为苹果的成功贡献一份力量。

在苹果,我已经结交了一些最好的朋友,感谢你们所有人,感谢你们多年来能和我一起工作。

乔布斯敬上

    发自我的iPhone

架构一个 iPhone 聊天应用程序

目前已有 4000 万台 iPhones 在用,您无疑对编写 iOS 应用程序感兴趣。但是从何着手呢?大多数应用程序都会连接网络,那么一个跨越两端的项目(比如说聊天应用程序)又是如何呢?本文将向您介绍如何利用服务器 和客户端组件构建一个聊天应用程序。从本文可以学到编写 iOS 应用程序的整个流程。学完本文之后,我保证您会想要编写一个这样的应用程序。

构建应用程序从架构解决方案开始。图 1 中的架构展示了 iOS 设备(这里是 iPhone)如何通过两个 PHP 页面连接到服务器。
图 1. Chat App 客户端/服务器架构

这两个 PHP 页面(add.php 和 messages.php)都连接到数据库,分别用于发布和检索消息。在我提供的代码中,数据库是 MySQL,但是您可以使用 DB2 或者您喜欢的任何其他数据库。

我使用的协议是 XML。add.php 页面返回一个 XML 消息,指出消息发布是否成功。messages.php 页面返回发布到服务器的最新消息。

在您开始之前,我想要介绍一下您将从本文学到的内容。

  • 数据库访问。我将向您介绍如何使用 PHP 向数据库添加行和检索行。
  • XML 编码。服务器代码演示如何将消息打包成 XML。
  • 构建 iOS 界面。我将详细介绍如何为应用程序构建用户界面。
  • 查询服务器。Objective-C 代码向 messages.php 页面发出 GET 请求,以得到最新的聊天消息。
  • 解析 XML。使用对 iOS 开发人员可用的 XML 解析器,您可以解析从 messages.php 返回的 XML。
  • 显示消息。应用程序使用一个定制列表项显示聊天消息;这一方法可以让您了解到如何定制自己的 iOS 应用程序的外观。
  • 发布消息。应用程序通过 add.php 将数据发布到服务器,add.php 将指导您完成发布过程。
  • 定时器。定时器任务用于周期性地轮询 messages.php,看何时来了新的聊天项目。

对于一个例子来说,这些内容太多了,应该为您开发您想要构建的任何类型的客户端/服务器 iOS 应用程序提供一组适当的工具。

构建服务器脚本

从创建数据库开始。我将我的数据库叫做 “chat”,您可以给您的数据库随便取个您喜欢的名字。您只需要确保在 PHP 中更改连接字符串,以匹配数据库的名称。用来为应用程序构建单个表的 SQL 脚本在清单 1 中
清单 1. chat.sql

DROP TABLE IF EXISTS chatitems;
CREATE TABLE chatitems (
    id BIGINT NOT NULL PRIMARY KEY auto_increment,
    added TIMESTAMP NOT NULL,
    user VARCHAR(64) NOT NULL,
    message VARCHAR(255) NOT NULL
);

这个简单的单表数据库只有 4 个字段:

  • 行的 id,这是一个自动递增的整数
  • 添加消息的日期
  • 添加消息的用户
  • 消息本身的文本

您可以更改这些字段的大小,以适应您的内容。

在生产系统中,您很可能还想要有一个带有姓名和密码字段的用户表,还有一个用户登录界面。对于本例来说,我想要让数据库尽量简单,所以数据库中只有一个表。

您想要构建的第一部分代码是清单 2 中的 add.php 脚本。
清单 2. add.php

<?php
header( 'Content-type: text/xml' );
mysql_connect( 'localhost:/tmp/mysql.sock', 'root', '' );
mysql_select_db( 'chat' );
mysql_query( "INSERT INTO chatitems VALUES ( null, null, '".
    mysql_real_escape_string( $_REQUEST['user'] ).
    "', '".
    mysql_real_escape_string( $_REQUEST['message'] ).
    "')" );
?>
<success />

该脚本连接到数据库,并使用已发布的 user 和 message 字段存储消息。就是在简单的 INSERT 语句中,两个值被转义,以解决任何含义不确定的字符,比如说可能会扰乱 SQL 语法的单引号。

为了测试 add 脚本,您创建一个 test.html 页面,如清单 3 所示,它只是将字段张贴到 add.php 脚本。
清单 3. test.html

<html>
<head>
    <title>Chat Message Test Form</title>
</head>
<body>
    <form action="add.php" method="POST">
        User: <input name="user" /><br />
        Message: <input name="message" /><br />
        <input type="submit" />
    </form>
</body>
</html>

这个简单的页面只有一个表单(指向 add.php)和两个文本字段(分别用于用户和消息)。然后还有一个 Submit 按钮,用于执行张贴。

test.html 页面安装好之后,您就可以测试 add.php 脚本了。在浏览器中打开测试页面,结果类似于 图 2,User 字段中显示有值 “jack”,Message 字段中有值 “This is a test”,下面是一个 Submit Query 按钮。
图 2. 消息发布测试页面

从这里,您添加一些值并单击 Submit Query 按钮。如果一切正常,您会看到类似于图 3 的画面。
图 3. 成功的消息发布

否则,您可能会得到一个 PHP 堆栈跟踪,告诉您数据库连接失败或者 INSERT 语句不工作。

消息添加脚本能够工作,下面应该构建 messages.php 脚本了,它返回消息列表。该脚本展示在清单 4 中。
清单 4. messages.php

<?php
header( 'Content-type: text/xml' );
mysql_connect( 'localhost:/tmp/mysql.sock', 'root', '' );
mysql_select_db( 'chat' );
if ( $_REQUEST['past'] ) {
    $result = mysql_query('SELECT * FROM chatitems WHERE id > '.
        mysql_real_escape_string( $_REQUEST['past'] ).
        ' ORDER BY added LIMIT 50');
} else {
    $result = mysql_query('SELECT * FROM chatitems ORDER BY added LIMIT 50' );
}
?>
<chat>
<?php
while ($row = mysql_fetch_assoc($result)) {
?>
<message added="<?php echo( $row['added'] ) ?>" id="<?php echo( $row['id'] ) ?>">
    <user><?php echo( htmlentities( $row['user'] ) ) ?></user>
    <text><?php echo( htmlentities( $row['message'] ) ) ?></text>
</message>
<?php
}
mysql_free_result($result);
?>
</chat>

这个脚本稍微有点复杂。它做的第一件事是完成查询。这里有两种可能:

  • 如果提供了 past 参数,那么脚本只返回超过指定 ID 的消息。
  • 如果没有指定 past 参数,那么返回所有消息。

使用 past 参数的原因是,您想要客户端是智能的。您想要客户端记住它已经看到过的消息,只寻找那些超过它已经具有的消息。客户端逻辑足够简单,它只保留它找到的最高值 ID,并作为 past 参数发送它。在开始时,它可以发送 0 作为值,相当于根本就不指定任何内容。

脚本的第二部分从查询结果集中检索记录,并将它们编码成 XML。如果这一部分脚本能够工作,那么您在浏览器中打开这一页面时,会看到类似图 4 的效果。
图 4. 聊天消息列表

服务器脚本就算完成了。当然,您可以添加您想要的任何逻辑,额外的通道、用户验证和登录,等等。对于这个实验性的聊天应用程序,这个脚本已经工作得很好了。现在您可以构建将会使用这个服务器脚本的 iOS 应用程序了。

构建客户端代码

iOS IDE 叫做 XCode。如果您还没有这个 IDE,那么需要从 Apple Developer Site(参见 参考资料)下载它。最新生产版本是 XCode 3,我这里的屏幕截图使用的就是这个版本。现在已经有了一个更新的版本,叫做 XCode 4,它在 IDE 中集成了 User Interface 编辑器,但是该版本目前还处于预览模式。

XCode 安装好之后,现在就该使用图 5 所示的 New Project 向导构建应用程序了。
图 5. 构建一个基于视图的 iPhone 应用程序

开始最容易的应用程序类型是基于视图的应用程序。这种应用程序允许您在您选择的地方放置控件,并为您完成大多数 UI 设计。选择控件之后,再选择 iPhone 或 iPad。这一选择关系到您将在什么样的设备上进行模拟。您可以编写代码,以便在 iPhone 或 iPad 或者 Apple 即将推出的任何其他 i-设备上运行。

单击 Choose 之后,您会被要求给应用程序命名。我将我的应用程序取名为 “iOSChatClient”,但是您可以随便给自己的应用程序取一个您喜欢的名字。您给应用程序命名之后,XCode IDE 会构建核心应用程序文件。然后,编译并启动它,确保一切正常。

创建用户界面

创建应用程序之后,您就可以开发界面了。从视图控制器 XIB 文件开始,该文件位于 Resources 文件夹中。通过双击该文件夹,可以打开 Interface Builder,这是 UI 工具箱。
图 6. 界面布局

图 6 展示了我如何布局三个控件。顶部是文本框,用于输入您想要发送的消息。文本框的右边是 Send 按钮。下面是 UITableView 对象,其中展示了所有聊天记录。

我会详细介绍如何在 Interface Builder 中完成这一切,但是我建议您下载项目代码,自己试验一下。尽管放心将该项目用作您自己的应用程序的模板。

创建视图控制器

用户界面这就完成了。下一个任务是回到 XCode IDE,向视图控制器类定义添加一些成员变量、属性和方法,如清单 5 所示。
清单 5. iOSChatClientViewController.h

#import <UIKit/UIKit.h>

@interface iOSChatClientViewController : UIViewController
                             <UITableViewDataSource,UITableViewDelegate>    {
    IBOutlet UITextField *messageText;
    IBOutlet UIButton *sendButton;
    IBOutlet UITableView *messageList;

    NSMutableData *receivedData;
    NSMutableArray *messages;
    int lastId;

    NSTimer *timer;

    NSXMLParser *chatParser;
    NSString *msgAdded;
    NSMutableString *msgUser;
    NSMutableString *msgText;
    int msgId;
    Boolean inText;
    Boolean inUser;
}

@property (nonatomic,retain) UITextField *messageText;
@property (nonatomic,retain) UIButton *sendButton;
@property (nonatomic,retain) UITableView *messageList;

- (IBAction)sendClicked:(id)sender;

@end

从顶部开始,我向类定义添加了 UITableViewDataSource 和 UITableViewDelegate。该代码用于驱动消息显示。类中有一些方法可以被回调,以便向表视图提供数据和布局信息。

实例变量分为五组。顶部是对各种 UI 元素的对象引用、要发送的消息的文本字段、发送按钮和消息列表。

下面是一些缓冲区,用于存储返回的 XML、消息列表和看到的最新 ID。lastID 从 0 开始,但是被设置为您看到的任何消息的最大 ID 值。它然后作为 past 参数的值被发送回服务器。

定时器每几秒钟触发一次,以查找来自服务器的新消息。最后一部分代码包含解析 XML 所需的所有成员变量。存在很多成员变量,这是因为 XML 解析器是一个基于回调的解析器,这表示它在类中保留有很多状态。

成员变量下面是属性和单击处理程序。它们由 Interface Builder 用来将界面元素连接到这个控制器类。事实上,视图控制器中有了这些元素之后,就可以回到 Interface Builder,使用连接器控件将消息文本、发送按钮和消息列表连接到它们对应的属性,将 Touch Inside 事件连接到sendClicked 方法。

构建视图控制器代码

在这一节,可以开始深入项目正题,实现视图控制器。虽然代码都在一个文件中,但是我把它们分成好几个清单,以便解释每一部分时更简单一点。

第一部分,清单 6,介绍应用程序开始部分和视图控制器的初始化。
清单 6. iOSChatClientViewController.m – 开始

#import "iOSChatClientViewController.h"

@implementation iOSChatClientViewController

@synthesize messageText, sendButton, messageList;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
        lastId = 0;
        chatParser = NULL;
    }
    return self;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:
                                       (UIInterfaceOrientation)interfaceOrientation {
    return YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
    [super dealloc];
}

这是标准的 iOS 代码。代码中有一些对可变的系统事件(比如说内存警告和存储单元分配)的回调。在生产应用程序中,您想要完美地处理这些事件,但是对于这个示例应用程序来说,我不想让事情过于复杂。

第一个真正的任务是对 messages.php 脚本发出 GET 请求。清单 7 展示了此任务的代码。
清单 7. iOSChatClientViewController.m – 得到消息

- (void)getNewMessages {
    NSString *url = [NSString stringWithFormat:
        @"http://localhost/chat/messages.php?past=%ld&t=%ld",
        lastId, time(0) ];

    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:url]];
    [request setHTTPMethod:@"GET"];

    NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (conn)
    {
        receivedData = [[NSMutableData data] retain];
    }
    else
    {
    }
}

- (void)connection:(NSURLConnection *)connection
  didReceiveResponse:(NSURLResponse *)response
{
    [receivedData setLength:0];
}  

- (void)connection:(NSURLConnection *)connection
  didReceiveData:(NSData *)data
{
    [receivedData appendData:data];
}  

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if (chatParser)
        [chatParser release];

    if ( messages == nil )
        messages = [[NSMutableArray alloc] init];

    chatParser = [[NSXMLParser alloc] initWithData:receivedData];
    [chatParser setDelegate:self];
    [chatParser parse];

    [receivedData release];

    [messageList reloadData];

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
    [self methodSignatureForSelector: @selector(timerCallback)]];
    [invocation setTarget:self];
    [invocation setSelector:@selector(timerCallback)];
    timer = [NSTimer scheduledTimerWithTimeInterval:5.0
        invocation:invocation repeats:NO];
}

- (void)timerCallback {
    [timer release];
    [self getNewMessages];
}

代码开始是 getNewMessages 方法。该方法创建请求,并通过构建一个 NSURLConnection 而开始这个请求。它还创建了用于存储响应数据的数据缓冲区。三个事件处理程序didReceieveResponsedidReceiveData 和 connectionDidFinishLoading 都处理加载数据的各个阶段。

connectionDidFinishLoading 方法是最重要的,因为它启动读取数据并挑出消息的 XML 解析器。

这里的最后一个方法是 timerCallback,由定时器用来启动新消息请求。当定时器超时时,getNewMessages 方法被调用,这将再次启动定时过程,最后将创建一个新的定时器,这个定时器超时时,会再次启动消息检索过程,等等。

下一部分,清单 8,处理 XML 的解析。
清单 8. iOSChatClientViewController.m – 解析消息

- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
    if ( [elementName isEqualToString:@"message"] ) {
        msgAdded = [[attributeDict objectForKey:@"added"] retain];
        msgId = [[attributeDict objectForKey:@"id"] intValue];
        msgUser = [[NSMutableString alloc] init];
        msgText = [[NSMutableString alloc] init];
        inUser = NO;
        inText = NO;
    }
    if ( [elementName isEqualToString:@"user"] ) {
        inUser = YES;
    }
    if ( [elementName isEqualToString:@"text"] ) {
        inText = YES;
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if ( inUser ) {
        [msgUser appendString:string];
    }
    if ( inText ) {
        [msgText appendString:string];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
   namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if ( [elementName isEqualToString:@"message"] ) {
        [messages addObject:[NSDictionary dictionaryWithObjectsAndKeys:msgAdded,
            @"added",msgUser,@"user",msgText,@"text",nil]];

        lastId = msgId;

        [msgAdded release];
        [msgUser release];
        [msgText release];
    }
    if ( [elementName isEqualToString:@"user"] ) {
        inUser = NO;
    }
    if ( [elementName isEqualToString:@"text"] ) {
        inText = NO;
    }
}

了解 SAX 解析的人应该都熟悉这个 XML 解析器。您给它一些 XML,当标签打开或关闭时,当找到文本时,它都会向您的代码发送事件。它是一个基于事件的解析器,而不是基于 DOM 的解析器。事件解析器的优点是,内存占用少。但是缺点是比较难以使用,因为在解析期间,所有的状态都需要存储在主机对象中。

过程开始时,所有成员变量(比如 msgAddedmsgUserinUser 和 inText)都被初始化为一个空字符串或 false。然后,随着每个标签在 didStartElement 方法中完成初始处理,代码查看标签名,并设置适当的 inUser 或 inText Boolean 值。这里,foundCharacters 方法处理向适当的字符串添加文本数据。didEndElement 方法然后处理标签的结束,即在发现 <message> 结束标签时将已解析的消息添加到消息列表。

现在您需要编写代码来显示消息。代码展示在清单 9 中。
清单 9. iOSChatClientViewController.m – 显示消息

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)myTableView numberOfRowsInSection:
                                       (NSInteger)section {
    return ( messages == nil ) ? 0 : [messages count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:
                                       (NSIndexPath *)indexPath {
    return 75;
}

- (UITableViewCell *)tableView:(UITableView *)myTableView
   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = (UITableViewCell *)[self.messageList
        dequeueReusableCellWithIdentifier:@"ChatListItem"];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ChatListItem"
            owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    NSDictionary *itemAtIndex = (NSDictionary *)[messages objectAtIndex:indexPath.row];
    UILabel *textLabel = (UILabel *)[cell viewWithTag:1];
    textLabel.text = [itemAtIndex objectForKey:@"text"];
    UILabel *userLabel = (UILabel *)[cell viewWithTag:2];
    userLabel.text = [itemAtIndex objectForKey:@"user"];

    return cell;
}

这些就是 UITableViewDataSource 和 UITableViewDelegate 接口定义的所有方法。最重要的一个是 cellForRowAtIndexPath 方法,它为列表项创建一个定制的 UI,并将它的文本字段设置为这个消息的适当文本。

这个定制的列表项定义在新的 ChatListItem.xib 文件中,您需要将这个文件创建在 Resources 文件夹中。在这个文件中,您创建一个新的 UITableViewCell 条目,其中有两个标签,分别标注为 1 和 2。这个文件以及所有其他代码都可从可下载的项目中得到(参见 下载)。

cellForRowAtIndexPath 方法中的代码分配这些 ChatListItem 单元格中的一个,然后将标签的文本字段设置为我们看到的这个消息的文本和用户值。

我知道要考虑的事项太多,但是已经快结束了。您已经完成了启动视图、获得消息 XML、解析消息和显示消息的代码。惟一剩下要做的事情是编写发送消息的代码。

构建此代码的第一件事是为用户名创建一个设置。iOS 应用程序可以定义进入 Settings 控制面板的定制设置。要创建一个设置,您需要使用 New File 向导在 Resources 文件夹中创建一个设置包。然后您使用图 7 中的 settings 编辑器,将它删除成单个设置。
图 7. 设置 settings

然后您确定,您想要此设置的标题为 User,并具有键 user_preference。然后,您就可以为清单 10 中的消息发送代码使用这个首选项来得到用户名了。
清单 10. iOSChatClientViewController.m – 发送消息

- (IBAction)sendClicked:(id)sender {
    [messageText resignFirstResponder];
    if ( [messageText.text length] > 0 ) {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

        NSString *url = [NSString stringWithFormat:
            @"http://localhost/chat/add.php"];

        NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]
            init] autorelease];
        [request setURL:[NSURL URLWithString:url]];
        [request setHTTPMethod:@"POST"];

        NSMutableData *body = [NSMutableData data];
        [body appendData:[[NSString stringWithFormat:@"user=%@&message=%@",
             [defaults stringForKey:@"user_preference"],
             messageText.text] dataUsingEncoding:NSUTF8StringEncoding]];
        [request setHTTPBody:body];

        NSHTTPURLResponse *response = nil;
        NSError *error = [[[NSError alloc] init] autorelease];
        [NSURLConnection sendSynchronousRequest:request
            returningResponse:&response error:&error];

        [self getNewMessages];
    }

    messageText.text = @"";
}

- (void)viewDidLoad {
        [super viewDidLoad];

    messageList.dataSource = self;
    messageList.delegate = self;

    [self getNewMessages];
}

@end

这是 Send Message 按钮的单击处理程序代码。它创建一个 NSMutableURLRequest,该请求具有add.php 脚本的 URL。它然后将消息主体设置为一个字符串,该字符串的用户和消息数据被编码为 POST 格式。它然后使用一个 NSURLConnection 同步地向服务器发送消息数据,并使用getNewMessages 启动一次消息检索。

该文件底部的 viewDidLoad 方法是视图加载时调用的方法。它开始消息检索过程,并将消息列表与该对象连接,以便消息列表知道从哪里得到数据。

所有这些都编写好之后,现在就该测试应用程序了。首先是在图 8 所示的 Settings 页面中设置用户名。
图 8. Settings 页面

单击 iOSChatClient 应用程序,会显示图 9 所示的 settings 页面。
图 9. 设置用户名

然后就像使用手机一样回到应用程序,并像图 10 中一样使用键盘输入一条消息。
图 10. 输入新消息

然后按下 send 按钮,我们看到消息被发送并发布到服务器,并从 messages.php 返回,就像您可以从图 11 中看到的一样。
图 11. 完成的聊天应用程序

您会从代码中看到,send 按钮和消息列表之间没有直接连接。所以消息进入消息列表的惟一方式是,通过服务器成功地将数据插入到数据库中。然后 message.php 代码成功地返回消息列表中的消息用于显示。

结束语

这篇文章无疑让您受益匪浅。您在后台对 XML 数据完成了一些数据库操作。构建了一个带有定制用户界面的 iOS 应用程序,它向服务器发送数据,从服务器检索数据。您使用 XML 解析器解析从服务器返回的响应 XML。您还构建了一个定制列表 UI,以便消息看起来更为美观。

下一步该怎么走完全取决于您自己。Apple 已经为您在 iPhone 或 iPad 上实现您的愿景提供了工具。本文给您构建自己的支持网络的应用程序提供了路线图。我鼓励您亲自动手试一试。如果您确实构建了比较酷的应用程序,请告诉我, 我会帮助您将它提交到 App Store。

下载本文源代码

本文转自 http://www.oschina.net/question/12_15994

iPhone开发内存管理

开发iPhone 应用程序并不难,基本上就是三个词 – “memory, memory, memory” 。iPhone OS 对内存的要求很严格,有memory leak ,杀掉; 内存使用超限额,杀掉。一个经过测试的程序,在使用过程中90%以上的崩溃都是内存问题造成的。在这里简单总结一下Object-C 内存管理。

基本概念

Object-C 的内存管理基于引用计数(Reference Count)这种非常常用的技术。简单讲,如果要使用一个对象,并希望确保在使用期间对象不被释放,需要通过函数调用来取得“所有权”,使用结束后再调用函数释放“所有权”。“所有权”的获得和释放,对应引用计数的增加和减少,为正数时代表对象还有引用,为零时代表可以释放。

函数

获得所有权的函数包括

  • alloc – 创建对象是调用alloc,为对象分配内存,对象引用计数加一。
  • copy – 拷贝一个对象,返回新对象,引用计数加一。
  • retain – 引用计数加一,获得对象的所有权。

另外,名字中带有alloc, copy, retain 字串的函数也都认为会为引用计数加一。

释放所有权的函数包括

  • release – 引用计数减一,释放所有权。如果引用计数减到零,对象会被释放。
  • autorelease – 在未来某个时机释放。下面具体解释。

autorelease

在某些情况下,并不想取得所有权,又不希望对象被释放。例如在一个函数中生成了一个新对象并返回,函数本身并不希望取得所有权,因为取得后再没有机会释放(除非创造出新的调用规则,而调用规则是一切混乱的开始),又不可能在函数内释放,可以借助autorelease 。所谓autorelease , 可以理解为把所有权交给一个外在的系统(这个系统实际上叫autorelease pool),由它来管理该对象的释放。通常认为交给 autorelease 的对象在当前event loop 中都是有效的。也可以自己创建NSAutoreleasePool 来控制autorelease的过程。

据苹果的人说,autorelease效率不高,所以能自己release的地方,尽量自己release,不要随便交给autorelease来处理。

规则

引用计数系统有自己的引用规则,遵守规则就可以少出错:

  • 获得所有权的函数要和释放所有权的函数一一对应。
  • 保证只有带alloc, copy, retain 字串的函数才会让调用者获得所有权,也就是引用计数加一。
  • 在对象的 dealloc函数中释放对象所拥有的实例变量。
  • 永远不要直接调用dealloc来释放对象,完全依赖引用计数来完成对象的释放。

有很多类都提供“便利构造函数(convenience constructors)”,它们创建对象但并不增加引用计数,意味着不需要调用release来释放所有权。很好辨认,它们的名字中不会有alloc和copy。

只要遵守这些规则,基本上可以消除所有Intrument可以发现的内存泄露问题。

容器

类似NSArray, NSDictionary, NSSet 等类,会在对象加入后引用计数加一获得所有权,在对象被移除或者整个容器对象被释放的时候释放容器内对象的所有权。类似的情况还有UIView对subview的所有权关系,UINavigationController对其栈上的controller的所有权关系等等。

其他所有权的产生

还有一些用法会让系统拥有对象的所有权。比如NSObject 的performSelector:withObject:afterDelay 。如果有必要,需要显示的调用cancelPreviousPerformRequestsWithTarget:selector:object: ,否则有可能产生内存泄露。

因这种原因产生的泄露因为并不违反任何规则,是Intrument所无法发现的。

循环引用

所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:

  • 对象a创建并引用到了对象b.
  • 对象b创建并引用到了对象c.
  • 对象c创建并引用到了对象b.

这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。

这种情况,必须打断循环引用,通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

因为循环引用而产生的内存泄露也是Instrument无法发现的,所以要特别小心。

一些和内存管理相关的有用内容:
Practical Memory Management
Reference counting

本文转自石锅拌饭,感谢作者!

iPhone Application Post Build Script

很多平台软件开发发布前, 有个很重要的事情就是保留好符号表(symbol)文件. 因为发布的软件一般来说都去除了符号表, 而符号表对于维护是很重要的信息, 没有它, 在拿到call stack后会很难对应到代码中. iPhone开发也是一样, 每次做完Build, 都会有一个.dSYM的目录, 就是符号表. 有了它, 在用户提交crash log后会很容易定位问题.

 

手工保存这些符号表文件很容易产生疏漏, 恢复起来又麻烦. 我做了一个脚本, 添加到Target的Run Script Build Phase中, 可以在Build类型是Distribution时, 将build出的binary和符号表分别打包为:

  • [application].app.[version].zip
  • [application].app.dSYM.[version].[git hash].zip

如果你用的版本管理系统不是git,需要对脚本做相应改动.
以下是脚本代码

 

[code lang=”shell”]
<div>
<div>
<pre>#!/usr/bin/env ruby
if ENV["BUILD_STYLE"] == "Distribution" && ENV["ARCHS"] == ‘armv6’
common_git_paths = %w[/usr/local/bin/git /usr/local/git/bin/git /opt/local/bin/git]
git_path = ""

common_git_paths.each do |p|
if File.exist?(p)
git_path = p
break
end
end

if git_path == ""
puts "Path to git not found"
exit
end

command_line = git_path + " rev-parse –short HEAD"
sha = `#{command_line}`.chomp

info_file = ENV[‘INFOPLIST_FILE’]

f = File.open(info_file, "r").read
re = /([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>)(.*?)(<\/string>)/
f =~ re
puts $1

# Get the version info from the source Info.plist file
# If the script has already been run we need to remove the git sha
# from the bundle’s Info.plist.
version = $2.sub(/ \([\w]+\)/, "")

cmdline = "cd #{ENV[‘BUILT_PRODUCTS_DIR’]};zip -r #{ENV[‘CONTENTS_FOLDER_PATH’]}.#{version}.zip #{ENV[‘CONTENTS_FOLDER_PATH’]};zip -r #{ENV[‘CONTENTS_FOLDER_PATH’]}.dSYM.#{version}.#{sha}.zip #{ENV[‘CONTENTS_FOLDER_PATH’]}.dSYM"
`#{cmdline}`
end

[/code]