nonocast / me

记录和分享技术的博客
http://nonocast.cn
MIT License
20 stars 0 forks source link

读书笔记: Advanced Mac OSX Programming: The Big Nerd Ranch Guide #279

Open nonocast opened 2 years ago

nonocast commented 2 years ago

Mac OS X: Built to Evolve

Complex systems come into existence in only two ways: through careful planning or through evolution. An airport is an example of something that is planned carefully beforehand, built, and then undergoes only minor changes for the rest of its existence. Complex organisms (like humans) are an example of something that has evolved continually from something simple. In the end, organisms that are well suited to evolution will always win out over organisms that are less suited to evolve.

几个概念:

20220504000309

Chapter 1: C and Objective-C

The Compiler pipeline: Preprocess - Compile - Link

The C preprocessor

% gcc -E -dM - < /dev/null 可以查看编译器预定义的符号:

#define _LP64 1
#define __AARCH64EL__ 1
#define __AARCH64_CMODEL_SMALL__ 1
#define __AARCH64_SIMD__ 1
#define __APPLE_CC__ 6000
#define __APPLE__ 1
#define __ARM64_ARCH_8__ 1
#define __ARM_64BIT_STATE 1
#define __ARM_ACLE 200
#define __ARM_ALIGN_MAX_STACK_PWR 4
#define __ARM_ARCH 8
#define __ARM_ARCH_8_3__ 1
#define __ARM_ARCH_8_4__ 1
#define __ARM_ARCH_8_5__ 1
#define __ARM_ARCH_ISA_A64 1
#define __ARM_ARCH_PROFILE 'A'
#define __ARM_FEATURE_AES 1
#define __ARM_FEATURE_ATOMICS 1
#define __ARM_FEATURE_CLZ 1
#define __ARM_FEATURE_COMPLEX 1
#define __ARM_FEATURE_CRC32 1
...

注: 多达3, 400个, -E flag显示p reprocess output, -dM: debugging flags, < /dev/null 给一个空的输入

Objective-C

在C的基础上扩展了面向对象的语法,同时加多了一套Foundation的框架,比如NSString, NSNUmber, NSArray, NS everything。

Chapter 2: The Compiler

Handy Flags

Debugging

Warnings

app.c

int main() {
  int i;
  return 0;
}

64-bit

#include <sys/types.h>中这些类型都是64-bit (long),所以在int或uint32_t转换时需要注意溢出

Chapter 3: Blocks

app.c

#include <stdio.h>
#include <sys/types.h>

typedef void (^Block)(void);

int main() {
  int val = 23;
  Block block1 = ^{
    printf("%d\n", val);
  };

  block1();

  return 0;
}

在Mac OS上gcc/clang都可以编译运行,但是在linux/ubuntu上的gcc就不行。我理解就是如果你写macOS C就可以直接使用Objective-C中的特性,只需要知道放弃移植性,取舍问题而已。

Chapter 4: Command-Line Programs

app.m

#include <Foundation/Foundation.h>
#include <stdio.h> // for standard I/O stuff
#include <stdlib.h> // for EXIT_FAILURE/SUCCESS

#define BUFFER_SIZE (4096)

void changecaseBuffer(char *buffer, size_t length, BOOL upcase);

int main() {
  char buffer[BUFFER_SIZE];
  const BOOL upcase = YES;

  while (!feof(stdin)) {
    const size_t length = fread(buffer, 1, BUFFER_SIZE, stdin);
    changecaseBuffer(buffer, length, upcase);
    fwrite(buffer, 1, length, stdout);
  }

  return EXIT_SUCCESS;
} // main

void changecaseBuffer(char *buffer, size_t length, BOOL upcase) {
  char *scan = buffer;
  char *const stop = buffer + length;

  while (scan < stop) {
    *scan = upcase ? toupper(*scan) : tolower(*scan);
    scan++;
  }
} // changecaseBuffer

运行后交互如下:

%  bignerd ./app
hello world
HELLO WORLD
%  bignerd echo 'hello world' > text
%  bignerd ./app < text
HELLO WORLD
bignerd cat text | ./app | grep 'HELLO' 
HELLO WORLD

Looking at the Environment

getenv可以获取到环境变量,调用时shell不管是在.zshrch还是临时都需要通过export FOO=BAR进行设置即可。

为什么需要export?

% foo=bar
%echo $foo
bar
% bash -c 'echo $foo'

% export foo
% bash -c 'echo $foo'
bar

shell - What do the bash-builtins 'set' and 'export' do? - Unix & Linux Stack Exchange

Chapter 5: Exceptions, Error Handling, and Signals

With the Unix API, 两个方式告知你错误:

errno

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
  FILE *file = fopen("no-this-file", "rb");
  if (file == NULL) {
    printf("errno: %d\n", errno);
    switch (errno) {
    case ENOENT: {
      fprintf(stderr, "[ENOENT] file not file\n");
      break;
    }
    default: {
      fprintf(stderr, "unknown error\n");
      break;
    }
    }
  }
  return 0;
} // main

注: hostent, noent中的ent都是entry的缩写。

signals

Exception Handling in Cocoa

通过NS_DURING, NS_HANDLER来模拟try...catch

SIGSEGV

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static jmp_buf save_point;

void handleSig(int signo) {
  switch (signo) {
  case SIGSEGV: {
    fprintf(stderr, "SIGSEVG: Segmentation violation.\n");
    longjmp(save_point, 1);
    break;
  }
  }
}

int main() {
  int *nullpointer;
  signal(SIGSEGV, handleSig);
  setjmp(save_point);

  for (int i = 0; i < 5; ++i) {
    if (i == 4) {
      *nullpointer = 1;
    } else {
      printf("%d\n", i);
    }
    sleep(1);
  }
  return 0;
} // main

注: 捕捉null pointer 产生的signal,然后通过jmp来恢复。

Chapter 6: Library

一般情况自己直接用static library就行。

Chapter 7: Memory

栈上分配的数据会在函数结束时回收,所以不应该返回stack的内存指针,会产生错误。

char *frobulate(void) {
  char buffer[5000];

  // wrok on buffer

  return buffer;
} // frobulate

int main() {
  char *p = frobulate();
  *p = 1;
  return 0;
} // main

如果需要超越stack, 那么就需要用heap, 通过malloc/free/realloc/reallocf操作heap。

发现memory leak:

Chapter 8: Debugging With GDB

Chapter 9: DTrace

Chapter 10: Performance Tuning

// skip, 留给二刷

Chapter 11: Files, Part 1: I/O and Permissions

Chapter 12: Files, Part 2: Directories, File Systems, and Links

Chapter 13: NSFileManager - Cocoa and the File System

Everything is treated as a file.

默认I/O操作是同步的,异步需要采用select/poll模型。

Formatted I/O

/Users/markd/Documents/Badgers.acorn

Chapter 14: Network Programming With Sockets

sockaddr

struct sockaddr {
  uint8_t sa_len;
  sa_family_t sa_family;
  char sa_data[14];
};
struct sockaddr_in {
  uint8_t sin_len;
  sa_family_t sin_family;
  in_port_t sin_port;  // 2 bytes
  struct in_addr sin_addr; // 4 bytes
  char sin_zero[8];
}

sockaddr_in的_in后缀表示Internet,sockeaddr用IPv4的结构,ip地址,端口来解释sockaddr中的14个字节, 所以内存是mapping的,只是解释不同而已。sin_port, sin_addr, sin_zero加起来共14个字节。

Network Byte Order

注:

Chapter 15: CFRunLoop

A run loop is an event loop in a thread that looks for events, such as mouse clicks, key presses, and timer firings. The run loop dispatches these events to interested parties and then goes to sleep waiting for more events.

Core Foundation通过CFRunLoop, CFSocket和CFHost提升了thread, socket的层次。

Chapter 17: Bonjour

Bonjour采用mDNS,所以只能在local network中,格式为name.type.domain, 如_home-sharing._tcp.local, 你可以publish你自己的服务,比如_chat._tcp.local,然后等待其他客户端来发现。

 ~ dns-sd -B _services._dns-sd._udp
Browsing for _services._dns-sd._udp
DATE: ---Sun 22 May 2022---
22:48:13.832  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
22:48:13.833  Add        3  12 .                    _tcp.local.          _androidtvremote
22:48:13.833  Add        3  12 .                    _tcp.local.          _apple-mobdev2
22:48:14.011  Add        3   1 .                    _tcp.local.          _smb
22:48:14.011  Add        3   1 .                    _tcp.local.          _companion-link
22:48:14.011  Add        3   1 .                    _tcp.local.          _home-sharing
...
22:48:14.011  Add        3   4 .                    _tcp.local.          _home-sharing
22:48:14.012  Add        3   5 .                    _tcp.local.          _ssh
22:48:14.012  Add        3   5 .                    _tcp.local.          _sftp-ssh
22:48:14.012  Add        3   5 .                    _tcp.local.          _rfb
22:48:14.012  Add        3   5 .                    _tcp.local.          _smb
22:48:14.012  Add        2   5 .                    _tcp.local.          _home-sharing
22:48:14.137  Add        3  12 .                    _tcp.local.          _printer
22:48:14.137  Add        3  12 .                    _tcp.local.          _ipp
22:48:14.137  Add        2  12 .                    _tcp.local.          _http

可以指定type查找dns-sd -B _home-sharing._tcp

Chapter 18: Multiprocessing

Chapter 19: Using NSTask

Chapter 20: Multithreading

Chapter 21: Operations

Chapter 22: Grand Central Dispatch

Chapter 23: Accessing the Keychain

这部分讲的比较简单, 没讲透。

扩展